Move pisi-1.0_alpha9 tag
This commit is contained in:
@@ -1,126 +0,0 @@
|
||||
Like every serious project, there are guidelines.
|
||||
Oooooo. "Coding Standards".
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
0. Before reading any further please observe
|
||||
PEP 8: Style Guide for Python Code
|
||||
http://www.python.org/peps/pep-0008.html
|
||||
1. When using dirnames, don't expect the dir to end
|
||||
with a trailing slash, and please use the dirnames
|
||||
in pisiconfig. Use util.join_path instead of os.path.join
|
||||
2. Python indentation is usually 4 spaces.
|
||||
3. Follow python philosophy of 'batteries included'
|
||||
4. Use exceptions, don't return error codes
|
||||
5. Don't make the PISI code have runtime dependencies on
|
||||
a particular distribution (as much as possible).
|
||||
6. Don't assume narrow use cases. Allow for a mediocre
|
||||
amount of generalization in your code, for pieces that
|
||||
will be required later.
|
||||
7. If you are changing something, check if that change
|
||||
breaks anything and fix breakage. For instance a
|
||||
name. Running the tests is not always enough!
|
||||
8. A good design ensures separation of concerns. Every module
|
||||
has a specific documented responsibility. Don't make the
|
||||
horse clean your windows.
|
||||
9. To ensure readability avoid nesting python constructs
|
||||
more than 3 levels deep. Python is a good language (unlike C),
|
||||
so you can define inner functions in a convenient way, use
|
||||
such decomposition techniques to break down your code into
|
||||
manageable chunks. The worst code you can write is one huge
|
||||
procedure that goes on for 1000 (or more) lines.
|
||||
10. Use a particular abstraction like a class or function only
|
||||
if it makes sense. Don't just define things because they can
|
||||
be defined. Define only things that will/may be used.
|
||||
11. If you are doing an expensive task like searching through
|
||||
10000 text chunks, please use an efficient data structure
|
||||
and algorithm. We are not MS engineers who know no data
|
||||
structure beyond doubly linked lists and no algorithm beyond
|
||||
quicksort.
|
||||
12. Resist the temptation to develop kludges and workarounds in
|
||||
response to pressure. Take your time to solve the problems by
|
||||
the book. The payoff comes later.
|
||||
13. Same thing goes for premature optimizations. Knuth and Dijkstra
|
||||
are watching over your shoulder. :)
|
||||
|
||||
|
||||
Unit testing
|
||||
------------
|
||||
|
||||
Unit tests are located in unittests directory. Running the tests is
|
||||
trivial. But you must synchronize your code and data with the test
|
||||
code, which can be a tedious work if you lose discipline.
|
||||
|
||||
Sample data files are located in the same directory with test modules.
|
||||
|
||||
For running the entire test suite, use the following command:
|
||||
|
||||
$ ./tests/run.py
|
||||
|
||||
The following command will run tests in specfiletests and archivetests
|
||||
in unittests dir:
|
||||
|
||||
$ ./tests/run.py specfile archive
|
||||
|
||||
Do not depend on the output of unittests. Instead of producing an
|
||||
output message/data in your tests, check the data internally. By
|
||||
definition, unittest should just report succeeding and failing cases.
|
||||
|
||||
If you didn't, take a look at the links below for having an idea of
|
||||
unit testing.
|
||||
http://www.extremeprogramming.org/rules/unittests.html
|
||||
http://www.extremeprogramming.org/rules/unittests2.html
|
||||
|
||||
|
||||
Misc. Suggestions
|
||||
-----------------
|
||||
|
||||
1. Demeter's Law
|
||||
|
||||
In OO programming, try to invoke Demeter's law.
|
||||
One of the "rules" there is not directly accessing any
|
||||
objects that are further than, 2/3 refs, away. So the
|
||||
following code is OK.
|
||||
destroy_system(a.system().name())
|
||||
but the following isn't as robust
|
||||
destroy_system(object_store.root().a.system.name())
|
||||
As you can tell, this introduces too many implementation
|
||||
dependencies. The rule of thumb is that, in these cases
|
||||
this statement must have been elsewhere.... It may be a
|
||||
good idea to not count the object scope in this case,
|
||||
so in Python self.a means only one level of reference,
|
||||
not two.
|
||||
|
||||
One quibble with this: it may be preferable not to insist
|
||||
on this where it would be inefficient. So if everything
|
||||
is neatly packed into one object contained in another
|
||||
object, why replicate everything in the upper level? If
|
||||
the semantics prevents dependency changes, then chains
|
||||
of 3 or even 4 could be acceptable.
|
||||
|
||||
OTOH, in Python and C++, it's not always good to implement
|
||||
accessor/modifier pairs for every property of an object.
|
||||
It would be much simpler if you are not doing any special
|
||||
processing on the property (e.g. if what the type system
|
||||
does is sufficient).
|
||||
|
||||
The main rule of thumb in Demeter's Law is avoiding
|
||||
putting more than, say, 10 methods in a class. That works
|
||||
really well in practice, forcing refactoring every now
|
||||
and then.
|
||||
|
||||
2. We all know, you're using LISP but didn't want to tell
|
||||
us. Don't be scared, as a success story and for your encouragment
|
||||
there are tens of people somewhere with LISP releated jobs.
|
||||
|
||||
3. If you are studying Data structures and Algorithms, and if
|
||||
your first assignment is to implement a basic FIFO queue,
|
||||
don't implement it. Just show your teacher the syntax of LISP,
|
||||
tell him how beautiful it is, and show how an autistic person
|
||||
can count lots of parenthesis with a "one second" look, you'll
|
||||
probably get A+.
|
||||
|
||||
4. If you are interested in "Playstation 2 Linux Games Programming"
|
||||
or "How to extend C programs with Guile", please don't exercise
|
||||
your valuable skills in this project.
|
||||
@@ -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,4 +0,0 @@
|
||||
recursive-include tests *.py
|
||||
recursive-include doc *
|
||||
recursive-include spec *
|
||||
recursive-include samples *
|
||||
@@ -1,9 +0,0 @@
|
||||
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
|
||||
@@ -1,12 +0,0 @@
|
||||
PISI - Packages Installed Succesfully as Intended
|
||||
|
||||
PISI is a new package manager implemented in python for the PARDUS
|
||||
distribution.
|
||||
|
||||
Main features
|
||||
|
||||
- Implemented in python.
|
||||
- All specification and intermediate data is in an XML format.
|
||||
- Fast database access implemented with berkeley DB.
|
||||
- Integrates both low-level and high-level package operations.
|
||||
|
||||
@@ -1,208 +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
|
||||
|
||||
* medium priority
|
||||
|
||||
/ 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 checking, printing and tests
|
||||
/ components and distribution (eray)
|
||||
+ requirements
|
||||
+ xml format
|
||||
- query components: list of components/packages in a component
|
||||
- install/remove components
|
||||
- safety switch: preserve system:base, install system:devel for build, etc.
|
||||
+ multiple package repository (eray)
|
||||
+ decide how to implement
|
||||
+ support repo order
|
||||
- a helper script for finding dependencies (eray)
|
||||
|
||||
* finished tasks
|
||||
|
||||
+ 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
|
||||
|
||||
* low priority
|
||||
|
||||
- improve url support, it's a little dumb at the moment
|
||||
- devise a File class that works with URLs
|
||||
- consider adding "other" protocols
|
||||
- removable media: media://
|
||||
- search command: (eray)
|
||||
- implement an inverted index (if there's a good one, just use it)
|
||||
- search in either summary, or description
|
||||
- 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)
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
/ verify metodlari
|
||||
+ SpecFile
|
||||
+ MetaData
|
||||
+ Files
|
||||
- Index (somebody should write one :} )
|
||||
? non-interactive use (baris)
|
||||
|
||||
5. Post Release
|
||||
|
||||
- high-level build commands
|
||||
- install build dependencies
|
||||
- build-all (or build-distro) command
|
||||
- an emerge command, just for fun
|
||||
- more support for categories:
|
||||
- a database of components: faster access
|
||||
- put categories into a database
|
||||
- fast search for categories
|
||||
/ make a package and sourcedb for each repo
|
||||
+ packagedb
|
||||
- sourceb
|
||||
- transaction stuff for database (eray)
|
||||
- multi-architecture support (baris, caglar)
|
||||
/ design decisions
|
||||
/ extend XML specs to support that
|
||||
- cross-platform building support
|
||||
- incremental build/fetch for repository index (pisi-index.xml)
|
||||
- diffsets (caglar)
|
||||
@@ -1,79 +0,0 @@
|
||||
% ALGORITHM STYLE -- Released 8 April 1996
|
||||
% for LaTeX-2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail Peter.Williams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithm}
|
||||
\typeout{Document Style `algorithm' - floating environment}
|
||||
|
||||
\RequirePackage{float}
|
||||
\RequirePackage{ifthen}
|
||||
\newcommand{\ALG@within}{nothing}
|
||||
\newboolean{ALG@within}
|
||||
\setboolean{ALG@within}{false}
|
||||
\newcommand{\ALG@floatstyle}{ruled}
|
||||
\newcommand{\ALG@name}{Algorithm}
|
||||
\newcommand{\listalgorithmname}{List of \ALG@name s}
|
||||
|
||||
% Declare Options
|
||||
% first appearance
|
||||
\DeclareOption{plain}{
|
||||
\renewcommand{\ALG@floatstyle}{plain}
|
||||
}
|
||||
\DeclareOption{ruled}{
|
||||
\renewcommand{\ALG@floatstyle}{ruled}
|
||||
}
|
||||
\DeclareOption{boxed}{
|
||||
\renewcommand{\ALG@floatstyle}{boxed}
|
||||
}
|
||||
% then numbering convention
|
||||
\DeclareOption{part}{
|
||||
\renewcommand{\ALG@within}{part}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{chapter}{
|
||||
\renewcommand{\ALG@within}{chapter}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{section}{
|
||||
\renewcommand{\ALG@within}{section}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsection}{
|
||||
\renewcommand{\ALG@within}{subsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsubsection}{
|
||||
\renewcommand{\ALG@within}{subsubsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{nothing}{
|
||||
\renewcommand{\ALG@within}{nothing}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption*{\edef\ALG@name{\CurrentOption}}
|
||||
|
||||
% ALGORITHM
|
||||
%
|
||||
\ProcessOptions
|
||||
\floatstyle{\ALG@floatstyle}
|
||||
\ifthenelse{\boolean{ALG@within}}{
|
||||
\ifthenelse{\equal{\ALG@within}{part}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[part]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{chapter}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[chapter]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{section}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[section]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsubsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsubsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{nothing}}
|
||||
{\newfloat{algorithm}{htbp}{loa}}{}
|
||||
}{
|
||||
\newfloat{algorithm}{htbp}{loa}
|
||||
}
|
||||
\floatname{algorithm}{\ALG@name}
|
||||
|
||||
\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
% ALGORITHMIC STYLE -- Released 8 APRIL 1996
|
||||
% for LaTeX version 2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail PeterWilliams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithmic}
|
||||
\typeout{Document Style `algorithmic' - environment}
|
||||
%
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\newboolean{ALC@noend}
|
||||
\setboolean{ALC@noend}{false}
|
||||
\newcounter{ALC@line}
|
||||
\newcounter{ALC@rem}
|
||||
\newlength{\ALC@tlm}
|
||||
%
|
||||
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
|
||||
%
|
||||
\ProcessOptions
|
||||
%
|
||||
% ALGORITHMIC
|
||||
\newcommand{\algorithmicrequire}{\textbf{Require:}}
|
||||
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
|
||||
\newcommand{\algorithmiccomment}[1]{\{#1\}}
|
||||
\newcommand{\algorithmicend}{\textbf{end}}
|
||||
\newcommand{\algorithmicif}{\textbf{if}}
|
||||
\newcommand{\algorithmicthen}{\textbf{then}}
|
||||
\newcommand{\algorithmicelse}{\textbf{else}}
|
||||
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
|
||||
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
|
||||
\newcommand{\algorithmicfor}{\textbf{for}}
|
||||
\newcommand{\algorithmicforall}{\textbf{for all}}
|
||||
\newcommand{\algorithmicdo}{\textbf{do}}
|
||||
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
|
||||
\newcommand{\algorithmicwhile}{\textbf{while}}
|
||||
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
|
||||
\newcommand{\algorithmicloop}{\textbf{loop}}
|
||||
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
|
||||
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
|
||||
\newcommand{\algorithmicuntil}{\textbf{until}}
|
||||
\def\ALC@item[#1]{%
|
||||
\if@noparitem \@donoparitem
|
||||
\else \if@inlabel \indent \par \fi
|
||||
\ifhmode \unskip\unskip \par \fi
|
||||
\if@newlist \if@nobreak \@nbitem \else
|
||||
\addpenalty\@beginparpenalty
|
||||
\addvspace\@topsep \addvspace{-\parskip}\fi
|
||||
\else \addpenalty\@itempenalty \addvspace\itemsep
|
||||
\fi
|
||||
\global\@inlabeltrue
|
||||
\fi
|
||||
\everypar{\global\@minipagefalse\global\@newlistfalse
|
||||
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
|
||||
\penalty\z@ \fi
|
||||
\everypar{}}\global\@nobreakfalse
|
||||
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
|
||||
\sbox\@tempboxa{\makelabel{#1}}%
|
||||
\global\setbox\@labels
|
||||
\hbox{\unhbox\@labels \hskip \itemindent
|
||||
\hskip -\labelwidth \hskip -\ALC@tlm
|
||||
\ifdim \wd\@tempboxa >\labelwidth
|
||||
\box\@tempboxa
|
||||
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
|
||||
\hskip \ALC@tlm}\ignorespaces}
|
||||
%
|
||||
\newenvironment{algorithmic}[1][0]{
|
||||
\let\@item\ALC@item
|
||||
\newcommand{\ALC@lno}{%
|
||||
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
|
||||
{{\footnotesize \arabic{ALC@line}:}}{}%
|
||||
}
|
||||
\let\@listii\@listi
|
||||
\let\@listiii\@listi
|
||||
\let\@listiv\@listi
|
||||
\let\@listv\@listi
|
||||
\let\@listvi\@listi
|
||||
\let\@listvii\@listi
|
||||
\newenvironment{ALC@g}{
|
||||
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
|
||||
\listparindent\z@ \rightmargin\z@
|
||||
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
|
||||
\leftmargin 1em
|
||||
\addtolength{\ALC@tlm}{\leftmargin}
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
\newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
|
||||
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
|
||||
{}{\ \algorithmiccomment{##1}}}
|
||||
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
|
||||
\newcommand{\ENSURE}{\item[\algorithmicensure]}
|
||||
\newcommand{\STATE}{\ALC@it}
|
||||
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
|
||||
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\renewcommand{\\}{\@centercr}
|
||||
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSIF}[2][default]%
|
||||
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@whl}}
|
||||
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
|
||||
\ALC@com{##1}\begin{ALC@loop}}
|
||||
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
|
||||
\ALC@com{##1}\begin{ALC@rpt}}
|
||||
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
|
||||
\ifthenelse{\boolean{ALC@noend}}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}}
|
||||
}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
|
||||
}
|
||||
\renewcommand{\@toodeep}{}
|
||||
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
|
||||
\itemsep\z@ \itemindent\z@ \listparindent\z@%
|
||||
\partopsep\z@ \parskip\z@ \parsep\z@%
|
||||
\labelsep 0.5em \topsep 0.2em%
|
||||
\ifthenelse{\equal{#1}{0}}
|
||||
{\labelwidth 0.5em }
|
||||
{\labelwidth 1.2em }
|
||||
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
|
||||
\ALC@tlm\labelsep
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,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,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==--
|
||||
|
||||
@@ -1,181 +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 is provided by the
|
||||
upstream maintainer of the source archive used in package. It must be
|
||||
always 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{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) $>$ rc $>$ pre $>$ beta $>$ alpha}.
|
||||
|
||||
The scope of a source version string is global in the literal
|
||||
sense.
|
||||
|
||||
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 PISI 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.
|
||||
|
||||
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 nonnegative 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{0}. 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 near 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 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 the previous build, and the
|
||||
build number is automatically determined by the P\.IS\.I build system.
|
||||
|
||||
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.
|
||||
|
||||
The scope of a build number is a given distribution build environment
|
||||
for a particular architecture. Therefore, it is not used in dependency
|
||||
specifications.
|
||||
|
||||
\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.
|
||||
|
||||
\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,65 +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 pisi.ui
|
||||
import pisi.context as ctx
|
||||
from pisi.cli.pisicli import PisiCLI
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
def handle_exception(exception, value, tb):
|
||||
|
||||
ui = pisi.cli.CLI() # make a temporary UI
|
||||
show_traceback = False
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("Keyboard Interrupt: Exiting..."))
|
||||
sys.exit(1)
|
||||
elif isinstance(value, pisi.Error):
|
||||
ui.error(_("Fatal Error. Program Terminated."))
|
||||
show_traceback = ctx.get_option('debug')
|
||||
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."))
|
||||
|
||||
ui.error("%s: %s\n" % (exception, 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."))
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
cli = PisiCLI()
|
||||
cli.run_command()
|
||||
@@ -1,117 +0,0 @@
|
||||
<!-- Document Type Declaration file for PISI pspec.xml files -->
|
||||
<!-- http://www.uludag.org.tr/projeler/pisi/ -->
|
||||
|
||||
<!ELEMENT PISI (Source,Package+,History)>
|
||||
|
||||
<!-- Source Section -->
|
||||
|
||||
<!ELEMENT Source (
|
||||
#PCDATA |
|
||||
Name |
|
||||
Homepage |
|
||||
Icon |
|
||||
Packager |
|
||||
License |
|
||||
IsA |
|
||||
PartOf |
|
||||
Summary |
|
||||
Description |
|
||||
Archive |
|
||||
Patches |
|
||||
BuildDependencies
|
||||
)*>
|
||||
|
||||
<!ELEMENT Name (#PCDATA)>
|
||||
|
||||
<!ELEMENT Homepage (#PCDATA)>
|
||||
|
||||
<!ELEMENT Icon (#PCDATA)>
|
||||
|
||||
<!ELEMENT Packager (Name, Email)>
|
||||
<!ELEMENT Email (#PCDATA)>
|
||||
|
||||
<!ELEMENT License (#PCDATA)>
|
||||
|
||||
<!ELEMENT IsA (#PCDATA)>
|
||||
|
||||
<!ELEMENT PartOf (#PCDATA)>
|
||||
|
||||
<!ELEMENT Summary (#PCDATA)>
|
||||
<!ATTLIST Summary xml:lang CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT Description (#PCDATA)>
|
||||
<!ATTLIST Description xml:lang CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT Archive (#PCDATA)>
|
||||
<!ATTLIST Archive sha1sum CDATA #REQUIRED>
|
||||
<!-- NOTE: we can limit possible types here to the ones that pisi know -->
|
||||
<!ATTLIST Archive type CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT BuildDependencies (Dependency*)>
|
||||
|
||||
<!ELEMENT Dependency (#PCDATA)>
|
||||
<!ATTLIST Dependency versionFrom CDATA #IMPLIED>
|
||||
<!ATTLIST Dependency versionTo CDATA #IMPLIED>
|
||||
<!ATTLIST Dependency version CDATA #IMPLIED>
|
||||
<!-- NOTE: release number dependencies? -->
|
||||
|
||||
<!ELEMENT Patches (Patch*)>
|
||||
|
||||
<!ELEMENT Patch (#PCDATA)>
|
||||
<!ATTLIST Patch compressionType CDATA #IMPLIED>
|
||||
<!ATTLIST Patch level CDATA #IMPLIED>
|
||||
<!ATTLIST Patch target CDATA #IMPLIED>
|
||||
|
||||
|
||||
<!-- Package Section -->
|
||||
|
||||
<!ELEMENT Package (
|
||||
#PCDATA |
|
||||
Name |
|
||||
License |
|
||||
IsA |
|
||||
PartOf |
|
||||
Summary |
|
||||
Description |
|
||||
RuntimeDependencies |
|
||||
Files |
|
||||
Conflicts |
|
||||
AdditionalFiles |
|
||||
Provides
|
||||
)*>
|
||||
|
||||
<!ELEMENT Provides (COMAR)+>
|
||||
<!ELEMENT COMAR (#PCDATA)>
|
||||
<!ATTLIST COMAR script CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT Conflicts (#PCDATA)>
|
||||
|
||||
<!ELEMENT AdditionalFiles (AdditionalFile+)>
|
||||
<!ELEMENT AdditionalFile (#PCDATA)>
|
||||
<!ATTLIST AdditionalFile target CDATA #REQUIRED>
|
||||
<!ATTLIST AdditionalFile permission CDATA #IMPLIED>
|
||||
<!ATTLIST AdditionalFile owner CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT RuntimeDependencies (Dependency*)>
|
||||
|
||||
<!ELEMENT Files (Path)+>
|
||||
|
||||
<!ELEMENT Path (#PCDATA)>
|
||||
<!-- NOTE: is this list correct? -->
|
||||
<!ATTLIST Path fileType
|
||||
(executable|library|data|config|doc|man|info|localedata|header|all)
|
||||
#REQUIRED>
|
||||
|
||||
|
||||
<!-- History Section -->
|
||||
|
||||
<!ELEMENT History (Update+)>
|
||||
|
||||
<!ELEMENT Update (#PCDATA | Date | Version | Comment | Name | Email)*>
|
||||
<!ATTLIST Update release CDATA #REQUIRED>
|
||||
<!-- FIXME: valid types must be used instead of CDATA -->
|
||||
<!ATTLIST Update type CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT Date (#PCDATA)>
|
||||
<!ELEMENT Version (#PCDATA)>
|
||||
<!ELEMENT Comment (#PCDATA)>
|
||||
@@ -1,468 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
|
||||
<!-- Project file for project pisi -->
|
||||
<!-- Saved: 2005-11-13, 00:06:19 -->
|
||||
<!-- Copyright (C) 2005 PiSi Development Team, -->
|
||||
<Project version="3.8">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
<UIType>Qt</UIType>
|
||||
<Description>The package management software of Pardus distribution.
|
||||
</Description>
|
||||
<Author>PiSi Development Team</Author>
|
||||
<EmaIl></EmaIl>
|
||||
<Sources>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>ebuild2pisi.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>cat-db.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>popt</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>zip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>unzip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>configfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>sourcedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>utiltests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>installdbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>run.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>metadatatests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>constantstests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>fetchertests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>archivetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>versiontests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>actionsapitests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>specfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>graphtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>get.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>shelltools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>variables.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>autotools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>kde.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>libtools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>coreutils.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>pisicli.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>commands.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>lockeddbshelve.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>specfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>package.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>build.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>packagedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>config.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>index.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>repodb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>ui.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>util.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>dependency.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>metadata.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>pgraph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>operations.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>context.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcefetcher.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>graph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcearchive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>configfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>installdb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>constants.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>fetcher.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>archive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>version.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>setup.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitoolsfunctions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>repostats.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>uri.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>api.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>TODO</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>fetchAll.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pythonmodules.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>scons.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>colors.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>pisi-cli</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>perlmodules.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>comariface.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>po</Dir>
|
||||
<Name>tr.po</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>oo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>update-environ.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>convert_history.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>dependencytests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>atomicoperations.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>ootests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>testcase.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>fix_old_metadata_files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>xmlexttests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>filestests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>component.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>autoxmltests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextpiks.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>autoxml.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilemdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextmdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlext.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilecdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextcdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilepiks.py</Name>
|
||||
</Source>
|
||||
</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="*.py" type="SOURCES" />
|
||||
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
|
||||
</FiletypeAssociations>
|
||||
</Project>
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
# PiSi version
|
||||
|
||||
__version__ = "1.0_alpha9"
|
||||
|
||||
__all__ = [ 'api', 'packagedb', 'installdb' ]
|
||||
|
||||
class Exception(Exception):
|
||||
"""Class of exceptions that must be caught and handled within PISI"""
|
||||
def __str__(self):
|
||||
s = ''
|
||||
for x in self.args:
|
||||
if s != '':
|
||||
s += '\n'
|
||||
s += x.encode('utf-8')
|
||||
return s
|
||||
|
||||
class Error(Exception):
|
||||
"""Class of exceptions that lead to program termination"""
|
||||
pass
|
||||
|
||||
# FIXME: can't do this due to name clashes in config and other singletons booo
|
||||
#from pisi.api import *
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
import pisi.context as ctx
|
||||
|
||||
def error(msg):
|
||||
if ctx.config.get_option('ignore_action_errors'):
|
||||
ctx.ui.error(msg)
|
||||
else:
|
||||
raise Error(msg)
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
from pisi.actionsapi.libtools import gnuconfig_update
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
if can_access_file('config.log'):
|
||||
ctx.ui.error(_('Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def configure(parameters = ''):
|
||||
'''configure source with given parameters = "--with-nls --with-libusb --with-something-usefull"'''
|
||||
if can_access_file('configure'):
|
||||
gnuconfig_update()
|
||||
|
||||
args = './configure \
|
||||
--prefix=/%s \
|
||||
--host=%s \
|
||||
--mandir=/%s \
|
||||
--infodir=/%s \
|
||||
--datadir=/%s \
|
||||
--sysconfdir=/%s \
|
||||
--localstatedir=/%s \
|
||||
%s' % (get.defaultprefixDIR(), \
|
||||
get.HOST(), get.manDIR(), \
|
||||
get.infoDIR(), get.dataDIR(), \
|
||||
get.confDIR(), get.localstateDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise ConfigureError(_('Configure failed.'))
|
||||
else:
|
||||
raise ConfigureError(_('No configure script found.'))
|
||||
|
||||
def rawConfigure(parameters = ''):
|
||||
'''configure source with given parameters = "--prefix=/usr --libdir=/usr/lib --with-nls"'''
|
||||
if can_access_file('configure'):
|
||||
gnuconfig_update()
|
||||
|
||||
if system('./configure %s' % parameters):
|
||||
raise ConfigureError(_('Configure failed.'))
|
||||
else:
|
||||
raise ConfigureError(_('No configure script found.'))
|
||||
|
||||
def compile(parameters = ''):
|
||||
#FIXME: Only one package uses this until now, hmmm
|
||||
system('%s %s %s' % (get.CC(), get.CFLAGS(), parameters))
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters = "all" || "doc" etc.'''
|
||||
if system('make %s' % 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,192 +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 gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PISI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
from variables import glb
|
||||
|
||||
class BinutilsError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
# Globals
|
||||
env = glb.env
|
||||
dirs = glb.dirs
|
||||
|
||||
def curDIR():
|
||||
'''returns current work directory's path'''
|
||||
return os.getcwd()
|
||||
|
||||
def curKERNEL():
|
||||
'''returns currently running kernel's version'''
|
||||
versionString = file("/proc/version").readline()
|
||||
return versionString.split()[2]
|
||||
|
||||
def curPYTHON():
|
||||
''' returns currently used python's version'''
|
||||
return os.path.basename(os.readlink("/usr/bin/python"))
|
||||
|
||||
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
|
||||
|
||||
# Directory Related Functions
|
||||
|
||||
def docDIR():
|
||||
return dirs.doc
|
||||
|
||||
def sbinDIR():
|
||||
return dirs.sbin
|
||||
|
||||
def infoDIR():
|
||||
return dirs.info
|
||||
|
||||
def manDIR():
|
||||
return dirs.man
|
||||
|
||||
def dataDIR():
|
||||
return dirs.data
|
||||
|
||||
def confDIR():
|
||||
return dirs.conf
|
||||
|
||||
def localstateDIR():
|
||||
return dirs.localstate
|
||||
|
||||
def defaultprefixDIR():
|
||||
return dirs.defaultprefix
|
||||
|
||||
def kdeDIR():
|
||||
# FIXME: Get followings from env.d or somewhere else
|
||||
return dirs.kde
|
||||
|
||||
def qtDIR():
|
||||
# FIXME: Get followings from env.d or somewhere else
|
||||
return dirs.qt
|
||||
|
||||
def qtLIBDIR():
|
||||
# FIXME: Get followings from env.d or somewhere else
|
||||
return '%s/lib/' % qtDIR()
|
||||
|
||||
# Binutils Variables
|
||||
|
||||
def existBinary(bin):
|
||||
# determine if path has binary
|
||||
path = os.environ['PATH'].split(':')
|
||||
for directory in path:
|
||||
if os.path.exists(os.path.join(directory, bin)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def getBinutilsInfo(util):
|
||||
cross_build_name = '%s-%s' % (HOST(), util)
|
||||
if not existBinary(cross_build_name):
|
||||
if not existBinary(util):
|
||||
raise BinutilsError(_('Util %s cannot be found') % util)
|
||||
else:
|
||||
ctx.ui.debug(_('Warning: %s does not exist, using plain name %s') \
|
||||
% (cross_build_name, util))
|
||||
return util
|
||||
else:
|
||||
return cross_build_name
|
||||
|
||||
def AR():
|
||||
return getBinutilsInfo('ar')
|
||||
|
||||
def AS():
|
||||
return getBinutilsInfo('as')
|
||||
|
||||
def CC():
|
||||
return getBinutilsInfo('gcc')
|
||||
|
||||
def CXX():
|
||||
return getBinutilsInfo('g++')
|
||||
|
||||
def LD():
|
||||
return getBinutilsInfo('ld')
|
||||
|
||||
def NM():
|
||||
return getBinutilsInfo('nm')
|
||||
|
||||
def RANLIB():
|
||||
return getBinutilsInfo('ranlib')
|
||||
|
||||
def F77():
|
||||
return getBinutilsInfo('f77')
|
||||
|
||||
def GCJ():
|
||||
return getBinutilsInfo('gcj')
|
||||
@@ -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' % parameters):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = 'install'):
|
||||
if can_access_file('Makefile'):
|
||||
args = 'make DESTDIR=%s destdir=%s %s' % (get.installDIR(), get.installDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise InstallError(_('Install failed.'))
|
||||
else:
|
||||
raise InstallError(_('No Makefile found.'))
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
import pisi.actionsapi.get as get
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def preplib(sourceDirectory = '/usr/lib'):
|
||||
sourceDirectory = join_path(get.installDIR(), sourceDirectory)
|
||||
if can_access_directory(sourceDirectory):
|
||||
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
|
||||
raise RunTimeError(_('Running ldconfig failed.'))
|
||||
|
||||
def gnuconfig_update():
|
||||
''' copy newest config.* onto source\'s '''
|
||||
for root, dirs, files in os.walk(os.getcwd()):
|
||||
for file in files:
|
||||
if file in ['config.sub', 'config.guess']:
|
||||
targetFile = os.path.join(root, file)
|
||||
if os.path.islink(targetFile):
|
||||
unlink(targetFile)
|
||||
copy('/usr/share/gnuconfig/%s' % file, join_path(root, file))
|
||||
ctx.ui.info(_('GNU Config Update Finished.'))
|
||||
|
||||
def libtoolize(parameters = ''):
|
||||
if system('/usr/bin/libtoolize %s' % parameters):
|
||||
raise RunTimeError(_('Running libtoolize failed.'))
|
||||
|
||||
def gen_usr_ldscript(dynamicLib):
|
||||
|
||||
makedirs('%s/usr/lib' % get.installDIR())
|
||||
|
||||
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
|
||||
content = '''
|
||||
/* GNU ld script
|
||||
Since Pardus has critical dynamic libraries
|
||||
in /lib, and the static versions in /usr/lib,
|
||||
we need to have a "fake" dynamic lib in /usr/lib,
|
||||
otherwise we run into linking problems.
|
||||
*/
|
||||
GROUP ( /lib/%s )
|
||||
''' % dynamicLib
|
||||
|
||||
destinationFile.write(content)
|
||||
destinationFile.close()
|
||||
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
from pisi.actionsapi.shelltools import export
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def configure(parameters = ''):
|
||||
'''configure source with given parameters.'''
|
||||
export('PERL_MM_USE_DEFAULT', '1')
|
||||
if can_access_file('Build.PL'):
|
||||
if system('perl Build.PL installdirs=vendor destdir=%s' % get.installDIR()):
|
||||
raise CompileError, _('Configure failed.')
|
||||
else:
|
||||
if system('perl Makefile.PL %s PREFIX=/usr INSTALLDIRS=vendor DESTDIR=%s' % (parameters, get.installDIR())):
|
||||
raise CompileError, _('Configure failed.')
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters.'''
|
||||
if can_access_file('Makefile'):
|
||||
if system('make %s' % parameters):
|
||||
raise MakeError, _('Make failed.')
|
||||
else:
|
||||
if system('perl Build build'):
|
||||
raise MakeError, _('perl build failed.')
|
||||
|
||||
def install(parameters = 'install'):
|
||||
'''install source with given parameters.'''
|
||||
if can_access_file('Makefile'):
|
||||
if system('make %s' % parameters):
|
||||
raise InstallError, _('Make failed.')
|
||||
else:
|
||||
if system('perl Build install'):
|
||||
raise MakeError, _('perl install failed.')
|
||||
|
||||
# FIXME: fix_local_pod % update_pod
|
||||
@@ -1,240 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
'''supports globs in sourceFile arguments'''
|
||||
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
import fileinput
|
||||
import re
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.pisitoolsfunctions import *
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
from pisi.actionsapi import error
|
||||
|
||||
def dobin(sourceFile, destinationDirectory = '/usr/bin'):
|
||||
'''insert a executable file into /bin or /usr/bin'''
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dodir(destinationDirectory):
|
||||
'''creates a directory tree'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
def dodoc(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
|
||||
readable_insinto(join_path(get.installDIR(), join_path('/usr/share/doc', get.srcTAG())), *sourceFiles)
|
||||
|
||||
def doexe(sourceFile, destinationDirectory):
|
||||
'''insert a executable file into destination directory'''
|
||||
|
||||
''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")'''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dohard(sourceFile, destinationFile):
|
||||
'''creates hard link between sourceFile and destinationFile'''
|
||||
#FIXME: How can i use hard-links in Python?
|
||||
ctx.ui.warning('*** Hard links not implemented yet')
|
||||
|
||||
def dohtml(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE/html'''
|
||||
|
||||
''' example call: pisitools.dohtml("doc/doxygen/html/*")'''
|
||||
destionationDirectory = join_path(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html')
|
||||
|
||||
if not can_access_directory(destionationDirectory):
|
||||
makedirs(destionationDirectory)
|
||||
|
||||
allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js']
|
||||
disallowed_directories = ['CVS']
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions:
|
||||
system('install -m0644 "%s" %s' % (source, destionationDirectory))
|
||||
if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories:
|
||||
for root, dirs, files in os.walk(source):
|
||||
for source in files:
|
||||
if os.path.splitext(source)[1] in allowed_extensions:
|
||||
makedirs(destionationDirectory)
|
||||
system('install -m0644 %s %s' % (join_path(root, source), destionationDirectory))
|
||||
|
||||
def doinfo(*sourceFiles):
|
||||
'''inserts the into files in the list of files into /usr/share/info'''
|
||||
readable_insinto(join_path(get.installDIR(), get.infoDIR()), *sourceFiles)
|
||||
|
||||
def dolib(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the library into /usr/lib'''
|
||||
|
||||
'''example call: pisitools.dolib_a("libz.a")'''
|
||||
'''example call: pisitools.dolib_a("libz.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def dolib_a(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0644'''
|
||||
|
||||
'''example call: pisitools.dolib_a("staticlib/libvga.a")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 644)
|
||||
|
||||
def dolib_so(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0755'''
|
||||
|
||||
'''example call: pisitools.dolib_so("pppd/plugins/minconn.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def doman(*sourceFiles):
|
||||
'''inserts the man pages in the list of files into /usr/share/man/'''
|
||||
|
||||
'''example call: pisitools.doman("man.1", "pardus.*")'''
|
||||
manDIR = join_path(get.installDIR(), get.manDIR())
|
||||
if not can_access_directory(manDIR):
|
||||
makedirs(manDIR)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
try:
|
||||
pageName, pageDirectory = source[:source.rindex('.')], \
|
||||
source[source.rindex('.')+1:]
|
||||
except ValueError:
|
||||
error(_('ActionsAPI [doman]: Wrong man page file: %s') % (source))
|
||||
|
||||
makedirs(join_path(manDIR, '/man%s' % pageDirectory))
|
||||
system('install -m0644 %s %s' % (source, join_path(manDIR, '/man%s' % pageDirectory)))
|
||||
|
||||
def domo(sourceFile, locale, destinationFile ):
|
||||
'''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES'''
|
||||
|
||||
'''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")'''
|
||||
|
||||
system('msgfmt %s' % sourceFile)
|
||||
makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale))
|
||||
move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile))
|
||||
|
||||
def domove(sourceFile, destination, destinationFile = ''):
|
||||
'''moves sourceFile/Directory into destinationFile/Directory'''
|
||||
|
||||
''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")'''
|
||||
''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")'''
|
||||
makedirs(join_path(get.installDIR(), destination))
|
||||
|
||||
for file in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
if not destinationFile:
|
||||
move(file, join_path(get.installDIR(), join_path(destination, os.path.basename(file))))
|
||||
else:
|
||||
move(file, join_path(get.installDIR(), join_path(destination, destinationFile)))
|
||||
|
||||
def rename(sourceFile, destinationFile):
|
||||
''' renames sourceFile as destinationFile'''
|
||||
|
||||
''' example call: pisitools.rename("/usr/bin/bash", "bash.old") '''
|
||||
''' the result of the previous example would be "/usr/bin/bash.old" '''
|
||||
|
||||
baseDir = os.path.dirname(sourceFile)
|
||||
|
||||
try:
|
||||
os.rename(join_path(get.installDIR(), sourceFile), join_path(get.installDIR(), baseDir, destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [rename]: No such file or directory: %s') % (sourceFile))
|
||||
|
||||
def dosed(sourceFiles, findPattern, replacePattern = ''):
|
||||
'''replaces patterns in sourceFiles'''
|
||||
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
|
||||
''' example call: pisitools.dosym("/etc/passwd", "caglar")'''
|
||||
''' example call: pisitools.dosym("/etc/pass*", "caglar")'''
|
||||
''' example call: pisitools.dosym("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
|
||||
|
||||
for sourceFile in glob.glob(sourceFiles):
|
||||
if can_access_file(sourceFile):
|
||||
for line in fileinput.input(sourceFile, inplace = 1):
|
||||
#FIXME: In-place filtering is disabled when standard input is read
|
||||
line = re.sub(findPattern, replacePattern, line)
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
raise FileError(_('File does not exist or permission denied: %s') % sourceFile)
|
||||
|
||||
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
|
||||
'''insert a executable file into /sbin or /usr/sbin'''
|
||||
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dosym(sourceFile, destinationFile):
|
||||
'''creates soft link between sourceFile and destinationFile'''
|
||||
|
||||
''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")'''
|
||||
makedirs(join_path(get.installDIR(), os.path.dirname(destinationFile)))
|
||||
|
||||
try:
|
||||
os.symlink(sourceFile, join_path(get.installDIR() ,destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [dosym]: File exists: %s') % (sourceFile))
|
||||
|
||||
def insinto(destinationDirectory, sourceFile, destinationFile = ''):
|
||||
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
if not destinationFile:
|
||||
for file in glob.glob(sourceFile):
|
||||
if can_access_file(file):
|
||||
copy(file, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(file))))
|
||||
else:
|
||||
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)))
|
||||
|
||||
def newdoc(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
|
||||
destinationDirectory = '' #490
|
||||
destinationDirectory = os.path.dirname(destinationFile)
|
||||
destinationFile = os.path.basename(destinationFile)
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
readable_insinto(join_path(get.installDIR(), 'usr/share/doc', get.srcTAG(), destinationDirectory), destinationFile)
|
||||
|
||||
def newman(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
doman(destinationFile)
|
||||
|
||||
def remove(sourceFile):
|
||||
'''removes sourceFile'''
|
||||
for file in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
unlink(file)
|
||||
|
||||
def removeDir(destinationDirectory):
|
||||
'''removes destinationDirectory and its subtrees'''
|
||||
for directory in glob.glob(join_path(get.installDIR(), destinationDirectory)):
|
||||
unlinkDir(directory)
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Generic functions for common usage of pisitools #
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
class FileError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class ArgumentError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def executable_insinto(destinationDirectory, *sourceFiles):
|
||||
'''insert a executable file into destinationDirectory'''
|
||||
|
||||
if not sourceFiles or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
# FIXME: use an internal install routine for these
|
||||
system('install -m0755 -o root -g root %s %s' % (source, destinationDirectory))
|
||||
|
||||
def readable_insinto(destinationDirectory, *sourceFiles):
|
||||
'''inserts file list into destinationDirectory'''
|
||||
|
||||
if not sourceFiles or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
system('install -m0644 %s %s' % (source, destinationDirectory))
|
||||
|
||||
def lib_insinto(sourceFile, destinationDirectory, permission = 0644):
|
||||
'''inserts a library fileinto destinationDirectory with given permission'''
|
||||
|
||||
if not sourceFile or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
if os.path.islink(sourceFile):
|
||||
os.symlink(os.path.realpath(sourceFile), os.path.join(destinationDirectory, sourceFile))
|
||||
else:
|
||||
system('install -m%s %s %s' % (permission, sourceFile, destinationDirectory))
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system, can_access_file
|
||||
from pisi.actionsapi.pisitools import dodoc
|
||||
|
||||
class CompileError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def compile(parameters = ''):
|
||||
'''compile source with given parameters.'''
|
||||
if system('python setup.py build %s' % (parameters)):
|
||||
raise CompileError, _('Make failed.')
|
||||
|
||||
def install(parameters = ''):
|
||||
'''does python setup.py install'''
|
||||
if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)):
|
||||
raise InstallError, _('Install failed.')
|
||||
|
||||
DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \
|
||||
CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*'
|
||||
|
||||
for doc in DDOCS:
|
||||
if can_access_file(doc):
|
||||
pisitools.dodoc(doc)
|
||||
|
||||
def run(parameters = ''):
|
||||
'''executes parameters with python'''
|
||||
if system('python %s' % (parameters)):
|
||||
raise RunTimeError, _('Running %s failed.') % parameters
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def make(parameters = ''):
|
||||
if system("scons %s" % parameters):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = 'install'):
|
||||
if system("scons prefix=%s %s" % (get.installDIR(), parameters)):
|
||||
raise InstallError(_('Install failed.'))
|
||||
@@ -1,212 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get
|
||||
|
||||
from pisi.actionsapi import error
|
||||
|
||||
def can_access_file(sourceFile):
|
||||
'''test the existence of file'''
|
||||
return os.access(sourceFile, os.F_OK)
|
||||
|
||||
def can_access_directory(destinationDirectory):
|
||||
'''test readability, writability and executablility of directory'''
|
||||
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
|
||||
|
||||
def makedirs(destinationDirectory):
|
||||
'''recursive directory creation function'''
|
||||
try:
|
||||
if not os.access(destinationDirectory, os.F_OK):
|
||||
os.makedirs(destinationDirectory)
|
||||
except OSError:
|
||||
error(_('Cannot create directory %s') % destinationDirectory)
|
||||
|
||||
def echo(destionationFile, content):
|
||||
try:
|
||||
f = open(destionationFile, 'a')
|
||||
f.write("%s\n" % content)
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [echo]: Can\'t append to file %s.') % (destionationFile))
|
||||
|
||||
def chmod(sourceFile, mode = 0755):
|
||||
'''change the mode of sourceFile to the mode'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if can_access_file(file):
|
||||
try:
|
||||
os.chmod(file, mode)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
|
||||
% (file, mode))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (file))
|
||||
|
||||
def chown(sourceFile, uid = "root", gid = "root"):
|
||||
'''change the owner and group id of sourceFile to uid and gid'''
|
||||
if can_access_file(sourceFile):
|
||||
try:
|
||||
os.chown(sourceFile, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
|
||||
% (sourceFile, uid, gid))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % sourceFile)
|
||||
|
||||
def sym(sourceFile, destinationFile):
|
||||
'''creates symbolic link'''
|
||||
try:
|
||||
os.symlink(sourceFile, destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (file, destinationFile))
|
||||
|
||||
def unlink(sourceFile):
|
||||
'''remove the file path'''
|
||||
if isFile(sourceFile) or isLink(sourceFile):
|
||||
try:
|
||||
os.unlink(sourceFile)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (sourceFile))
|
||||
elif isDirectory(sourceFile):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (sourceFile))
|
||||
|
||||
def unlinkDir(sourceDirectory):
|
||||
'''delete an entire directory tree'''
|
||||
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
|
||||
try:
|
||||
shutil.rmtree(sourceDirectory)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [unlinkDir]: Operation not permitted: %s') % (sourceDirectory))
|
||||
elif isFile(sourceDirectory):
|
||||
pass
|
||||
else:
|
||||
error(_('ActionsAPI [unlinkDir]: Directory %s doesn\'t exists.') % (sourceDirectory))
|
||||
|
||||
def move(sourceFile, destinationFile):
|
||||
'''recursively move a sourceFile or directory to destinationFile'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if isFile(file) or isLink(file) or isDirectory(file):
|
||||
try:
|
||||
shutil.move(file, destinationFile)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (file, destinationFile))
|
||||
else:
|
||||
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (file))
|
||||
|
||||
def copy(sourceFile, destinationFile):
|
||||
'''recursively copy a sourceFile or directory to destinationFile'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if isFile(file) or isLink(file):
|
||||
try:
|
||||
shutil.copy(file, destinationFile)
|
||||
except IOError:
|
||||
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (file, destinationFile))
|
||||
elif isDirectory(file):
|
||||
copytree(file, destinationFile)
|
||||
else:
|
||||
error(_('ActionsAPI [copy]: File %s does not exist.') % file)
|
||||
|
||||
def copytree(source, destination, sym = False):
|
||||
'''recursively copy an entire directory tree rooted at source'''
|
||||
if isDirectory(source) or isLink(source):
|
||||
try:
|
||||
shutil.copytree(source, destination, sym)
|
||||
except OSError, e:
|
||||
error(_('ActionsAPI [copytree] %s to %s: %s') % (source, destination, e))
|
||||
else:
|
||||
error(_('ActionsAPI [copytree]: Directory %s doesn\'t exists.') % (source))
|
||||
|
||||
def touch(sourceFile):
|
||||
'''changes the access time of the 'sourceFile', or creates it if it is not exist'''
|
||||
if glob.glob(sourceFile):
|
||||
for file in glob.glob(sourceFile):
|
||||
os.utime(file, None)
|
||||
else:
|
||||
try:
|
||||
f = open(sourceFile, 'w')
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [touch]: Permission denied: %s') % (sourceFile))
|
||||
|
||||
def cd(directoryName = ''):
|
||||
'''change directory'''
|
||||
current = os.getcwd()
|
||||
if directoryName:
|
||||
os.chdir(directoryName)
|
||||
else:
|
||||
os.chdir(os.path.dirname(current))
|
||||
|
||||
def ls(source):
|
||||
'''listdir'''
|
||||
if os.path.isdir(source):
|
||||
return os.listdir(source)
|
||||
else:
|
||||
return glob.glob(source)
|
||||
|
||||
def export(key, value):
|
||||
'''export environ variable'''
|
||||
os.environ[key] = value
|
||||
|
||||
def isLink(sourceFile):
|
||||
'''return True if sourceFile refers to a symbolic link'''
|
||||
return os.path.islink(sourceFile)
|
||||
|
||||
def isFile(sourceFile):
|
||||
'''return True if sourceFile is an existing regular file'''
|
||||
return os.path.isfile(sourceFile)
|
||||
|
||||
def isDirectory(sourceDirectory):
|
||||
'''Return True if sourceFile is an existing directory'''
|
||||
return os.path.isdir(sourceDirectory)
|
||||
|
||||
def realPath(sourceFile):
|
||||
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
|
||||
return os.path.realpath(sourceFile)
|
||||
|
||||
def baseName(sourceFile):
|
||||
'''return the base name of pathname sourceFile'''
|
||||
return os.path.basename(sourceFile)
|
||||
|
||||
def dirName(sourceFile):
|
||||
'''return the directory name of pathname path'''
|
||||
return os.path.dirname(sourceFile)
|
||||
|
||||
def system(command):
|
||||
#FIXME: might be better to merge this with util.run_batch
|
||||
#FIXME: String formatting
|
||||
command = command.replace(" ", " ")
|
||||
ctx.ui.debug(_('executing %s') % command)
|
||||
p = os.popen(command)
|
||||
while 1:
|
||||
line = p.readline()
|
||||
if not line:
|
||||
break
|
||||
ctx.ui.debug(line[:-1])
|
||||
|
||||
return p.close()
|
||||
@@ -1,83 +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
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
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'
|
||||
|
||||
#FIXME: Get these from config or somewhere else!
|
||||
kde = '/usr/kde/3.4'
|
||||
qt = '/usr/qt/3'
|
||||
|
||||
def initVariables():
|
||||
ctx.env = Env()
|
||||
ctx.dirs = Dirs()
|
||||
return ctx
|
||||
|
||||
glb = initVariables()
|
||||
@@ -1,334 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
"""Top level PISI interfaces. a facade to the entire PISI system"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from os.path import exists
|
||||
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.operations as operations
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
import pisi.sourcedb
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
from pisi.operations import install, remove, upgrade
|
||||
from pisi.atomicoperations import resurrect_package
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def init(database = True, options = None, ui = None, comar = True):
|
||||
"""Initialize PiSi subsystem"""
|
||||
|
||||
import pisi.config
|
||||
ctx.config = pisi.config.Config(options)
|
||||
|
||||
# TODO: this is definitely not dynamic beyond this point!
|
||||
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
|
||||
|
||||
if ui is None:
|
||||
if options:
|
||||
pisi.context.ui = pisi.cli.CLI(options.debug)
|
||||
else:
|
||||
pisi.context.ui = pisi.cli.CLI()
|
||||
else:
|
||||
pisi.context.ui = ui
|
||||
|
||||
# initialize repository databases
|
||||
if database:
|
||||
ctx.repodb = pisi.repodb.init()
|
||||
ctx.installdb = pisi.installdb.init()
|
||||
ctx.filesdb = pisi.files.FilesDB()
|
||||
packagedb.init_db()
|
||||
pisi.sourcedb.init()
|
||||
else:
|
||||
ctx.repodb = None
|
||||
ctx.installdb = None
|
||||
ctx.filesdb = None
|
||||
ctx.ui.debug('PISI API initialized')
|
||||
ctx.initialized = True
|
||||
|
||||
def finalize():
|
||||
pisi.repodb.finalize()
|
||||
pisi.installdb.finalize()
|
||||
if ctx.filesdb != None:
|
||||
ctx.filesdb.close()
|
||||
|
||||
packagedb.finalize_db()
|
||||
pisi.sourcedb.finalize()
|
||||
ctx.ui.debug('PISI API finalized')
|
||||
ctx.initialized = False
|
||||
|
||||
def list_upgradable():
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
|
||||
A = ctx.installdb.list_installed()
|
||||
# filter packages that are not upgradable
|
||||
Ap = []
|
||||
for x in A:
|
||||
if not ctx.installdb.is_installed(x):
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
pkg = packagedb.get_package(x)
|
||||
if ignore_build or (not build):
|
||||
if release < pkg.release:
|
||||
Ap.append(x)
|
||||
elif build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
pass
|
||||
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
|
||||
return Ap
|
||||
|
||||
|
||||
def package_graph(A, ignore_installed = False):
|
||||
"""Construct a package relations graph, containing
|
||||
all dependencies of packages A, if ignore_installed
|
||||
option is True, then only uninstalled deps will
|
||||
be added."""
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
#print pkg
|
||||
for dep in pkg.runtimeDependencies:
|
||||
if ignore_installed:
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
return G_f
|
||||
|
||||
def postpone_postinstall(package):
|
||||
try:
|
||||
import pisi.comariface as comariface
|
||||
comariface.register(package)
|
||||
except ImportError:
|
||||
raise Error(_("COMAR: comard not fully installed"))
|
||||
|
||||
def configure_pending():
|
||||
# start with pending packages
|
||||
# configure them in reverse topological order of dependency
|
||||
A = ctx.installdb.list_pending()
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
for x in A:
|
||||
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 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:
|
||||
comariface.run_postinstall(x)
|
||||
ctx.installdb.clear_pending(x)
|
||||
except ImportError:
|
||||
raise Error(_("COMAR: comard not fully installed"))
|
||||
|
||||
def info(package):
|
||||
if package.endswith(ctx.const.package_suffix):
|
||||
return info_file(package)
|
||||
else:
|
||||
return info_name(package)
|
||||
|
||||
def info_file(package_fn):
|
||||
from package import Package
|
||||
|
||||
if not os.path.exists(package_fn):
|
||||
raise Error (_('File %s not found') % package_fn)
|
||||
|
||||
package = Package(package_fn)
|
||||
package.read()
|
||||
return package.metadata, package.files
|
||||
|
||||
def info_name(package_name):
|
||||
"""fetch package information for a package"""
|
||||
if packagedb.has_package(package_name):
|
||||
package = packagedb.get_package(package_name)
|
||||
from pisi.metadata import MetaData
|
||||
metadata = MetaData()
|
||||
metadata.package = package
|
||||
#FIXME: get it from sourcedb
|
||||
metadata.source = None
|
||||
#TODO: fetch the files from server if possible
|
||||
if ctx.installdb.is_installed(package.name):
|
||||
files = ctx.installdb.files(package.name)
|
||||
else:
|
||||
files = None
|
||||
return metadata, files
|
||||
else:
|
||||
raise Error(_('Package %s not found') % package_name)
|
||||
|
||||
def index(repo_dir = '.'):
|
||||
|
||||
ctx.ui.info(_('* Building index of PISI files under %s') % repo_dir)
|
||||
index = Index()
|
||||
index.index(repo_dir)
|
||||
index.write(ctx.const.pisi_index)
|
||||
ctx.ui.info(_('* Index file written'))
|
||||
|
||||
|
||||
def add_repo(name, indexuri):
|
||||
repo = pisi.repodb.Repo(URI(indexuri))
|
||||
ctx.repodb.add_repo(name, repo)
|
||||
ctx.ui.info(_('Repo %s added to system.') % name)
|
||||
|
||||
def remove_repo(name):
|
||||
if ctx.repodb.has_repo(name):
|
||||
ctx.repodb.remove_repo(name)
|
||||
ctx.ui.info(_('Repo %s removed from system.') % name)
|
||||
else:
|
||||
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
|
||||
% name)
|
||||
|
||||
def update_repo(repo):
|
||||
|
||||
ctx.ui.info(_('* Updating repository: %s') % repo)
|
||||
index = Index()
|
||||
index.read_uri(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo)
|
||||
index.update_db(repo)
|
||||
ctx.ui.info(_('\n* Package database updated.'))
|
||||
|
||||
|
||||
# build functions...
|
||||
def prepare_for_build(pspecfile, authInfo=None):
|
||||
|
||||
url = URI(pspecfile)
|
||||
if url.is_remote_file():
|
||||
from sourcefetcher import SourceFetcher
|
||||
fs = SourceFetcher(url, authInfo)
|
||||
url.uri = fs.fetch_all()
|
||||
|
||||
import pisi.build
|
||||
pb = pisi.build.Builder(url.uri)
|
||||
|
||||
return pb
|
||||
|
||||
def build(pspecfile, authInfo=None):
|
||||
pb = prepare_for_build(pspecfile, authInfo)
|
||||
pb.build()
|
||||
|
||||
order = {"none": 0,
|
||||
"unpack": 1,
|
||||
"setupaction": 2,
|
||||
"buildaction": 3,
|
||||
"installaction": 4,
|
||||
"buildpackages": 5}
|
||||
|
||||
def __buildState_unpack(pb):
|
||||
# unpack is the first state to run.
|
||||
pb.fetch_source_archive()
|
||||
pb.unpack_source_archive()
|
||||
pb.apply_patches()
|
||||
|
||||
def __buildState_setupaction(pb, last):
|
||||
|
||||
if order[last] < order["unpack"]:
|
||||
__buildState_unpack(pb)
|
||||
pb.run_setup_action()
|
||||
|
||||
def __buildState_buildaction(pb, last):
|
||||
|
||||
if order[last] < order["setupaction"]:
|
||||
__buildState_setupaction(pb, last)
|
||||
pb.run_build_action()
|
||||
|
||||
def __buildState_installaction(pb, last):
|
||||
|
||||
if order[last] < order["buildaction"]:
|
||||
__buildState_buildaction(pb, last)
|
||||
pb.run_install_action()
|
||||
|
||||
def __buildState_buildpackages(pb, last):
|
||||
|
||||
if order[last] < order["installaction"]:
|
||||
__buildState_installaction(pb, last)
|
||||
pb.build_packages()
|
||||
|
||||
def build_until(pspecfile, state, authInfo=None):
|
||||
pb = prepare_for_build(pspecfile, authInfo)
|
||||
pb.compile_action_script()
|
||||
|
||||
last = pb.get_state()
|
||||
ctx.ui.info("Last state was %s"%last)
|
||||
|
||||
if not last: last = "none"
|
||||
|
||||
if state == "unpack":
|
||||
__buildState_unpack(pb)
|
||||
return
|
||||
|
||||
if state == "setupaction":
|
||||
__buildState_setupaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "buildaction":
|
||||
__buildState_buildaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "installaction":
|
||||
__buildState_installaction(pb, last)
|
||||
return
|
||||
|
||||
__buildState_buildpackages(pb, last)
|
||||
|
||||
def delete_cache():
|
||||
util.clean_dir(ctx.config.packages_dir())
|
||||
util.clean_dir(ctx.config.archives_dir())
|
||||
util.clean_dir(ctx.config.tmp_dir())
|
||||
@@ -1,234 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Archive module provides access to regular archive file types.
|
||||
# maintainer baris and meren
|
||||
|
||||
# standard library modules
|
||||
import os
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
|
||||
class ArchiveError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class ArchiveBase(object):
|
||||
"""Base class for Archive classes."""
|
||||
def __init__(self, file_path, atype):
|
||||
self.file_path = file_path
|
||||
self.type = atype
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
self.target_dir = target_dir
|
||||
# first we check if we need to clean-up our working env.
|
||||
if os.path.exists(self.target_dir) and clean_dir:
|
||||
util.clean_dir(self.target_dir)
|
||||
|
||||
os.makedirs(self.target_dir)
|
||||
|
||||
|
||||
class ArchiveBinary(ArchiveBase):
|
||||
"""ArchiveBinary handles binary archive files (usually distrubuted as
|
||||
.bin files)"""
|
||||
def __init__(self, file_path, arch_type = "binary"):
|
||||
super(ArchiveBinary, self).__init__(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
super(ArchiveBinary, self).unpack(target_dir, clean_dir)
|
||||
|
||||
# we can't unpack .bin files. we'll just move them to target
|
||||
# directory and leave the dirty job to actions.py ;)
|
||||
import shutil
|
||||
target_file = os.path.join(target_dir, os.path.basename(self.file_path))
|
||||
shutil.copyfile(self.file_path, target_file)
|
||||
|
||||
|
||||
class ArchiveTar(ArchiveBase):
|
||||
"""ArchiveTar handles tar archives depending on the compression
|
||||
type. Provides access to tar, tar.gz and tar.bz2 files.
|
||||
|
||||
This class provides the unpack magic for tar archives."""
|
||||
def __init__(self, file_path, arch_type = "tar"):
|
||||
super(ArchiveTar, self).__init__(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
"""Unpack tar archive to a given target directory(target_dir)."""
|
||||
super(ArchiveTar, self).unpack(target_dir, clean_dir)
|
||||
|
||||
rmode = ""
|
||||
if self.type == 'tar':
|
||||
rmode = 'r:'
|
||||
elif self.type == 'targz':
|
||||
rmode = 'r:gz'
|
||||
elif self.type == 'tarbz2':
|
||||
rmode = 'r:bz2'
|
||||
else:
|
||||
raise ArchiveError(_("Archive type not recognized"))
|
||||
|
||||
tar = tarfile.open(self.file_path, rmode)
|
||||
oldwd = os.getcwd()
|
||||
os.chdir(self.target_dir)
|
||||
for tarinfo in tar:
|
||||
tar.extract(tarinfo)
|
||||
os.chdir(oldwd)
|
||||
tar.close()
|
||||
|
||||
|
||||
class ArchiveZip(ArchiveBase):
|
||||
"""ArchiveZip handles zip archives.
|
||||
|
||||
Being a zip archive PISI packages also use this class
|
||||
extensively. This class provides unpacking and packing magic for
|
||||
zip archives."""
|
||||
|
||||
symmagic = 2716663808 #long of hex val '0xA1ED0000L'
|
||||
|
||||
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
|
||||
super(ArchiveZip, self).__init__(file_path, arch_type)
|
||||
|
||||
self.zip_obj = zipfile.ZipFile(self.file_path, mode)
|
||||
|
||||
def close(self):
|
||||
"""Close the zip archive."""
|
||||
self.zip_obj.close()
|
||||
|
||||
def add_to_archive(self, file_name):
|
||||
"""Add file or directory path to the zip file"""
|
||||
# It's a pity that zipfile can't handle unicode strings. Grrr!
|
||||
file_name = str(file_name)
|
||||
if os.path.isdir(file_name) and not os.path.islink(file_name):
|
||||
self.zip_obj.writestr(file_name + '/', '')
|
||||
for f in os.listdir(file_name):
|
||||
self.add_to_archive(os.path.join(file_name, f))
|
||||
else:
|
||||
if os.path.islink(file_name):
|
||||
dest = os.readlink(file_name)
|
||||
attr = zipfile.ZipInfo()
|
||||
attr.filename = file_name
|
||||
attr.create_system = 3
|
||||
attr.external_attr = self.symmagic
|
||||
self.zip_obj.writestr(attr, dest)
|
||||
else:
|
||||
self.zip_obj.write(file_name, file_name, zipfile.ZIP_DEFLATED)
|
||||
|
||||
def add_basename_to_archive(self, file_name):
|
||||
"""Add only the basepath to the zip file. For example; if the given
|
||||
file_name parameter is /usr/local/bin/somedir, this function
|
||||
will create only the base directory/file somedir in the
|
||||
archive."""
|
||||
cwd = os.getcwd()
|
||||
path_name = os.path.dirname(file_name)
|
||||
file_name = os.path.basename(file_name)
|
||||
if path_name:
|
||||
os.chdir(path_name)
|
||||
self.add_to_archive(file_name)
|
||||
os.chdir(cwd)
|
||||
|
||||
def unpack_file_cond(self, pred, target_dir, archive_root = ''):
|
||||
"""Unpack/Extract a file according to predicate function filename ->
|
||||
bool"""
|
||||
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)
|
||||
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.exists(ofile) or os.path.islink(ofile):
|
||||
os.remove(ofile)
|
||||
|
||||
if info.external_attr == self.symmagic:
|
||||
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,454 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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 install operation
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
|
||||
# for python 2.3 compatibility
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.dependency as dependency
|
||||
import pisi.util as util
|
||||
from pisi.specfile import *
|
||||
from pisi.package import Package
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
from pisi.uri import URI
|
||||
import pisi.ui
|
||||
#import conflicts
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# single package operations
|
||||
|
||||
class AtomicOperation(object):
|
||||
|
||||
def __init__(self, ignore_dep = None):
|
||||
#self.package = package
|
||||
if ignore_dep==None:
|
||||
self.ignore_dep = ctx.config.get_option('ignore_dependency')
|
||||
else:
|
||||
self.ignore_dep = ignore_dep
|
||||
|
||||
def run(self, package):
|
||||
"perform an atomic package operation"
|
||||
pass
|
||||
|
||||
|
||||
class Install(AtomicOperation):
|
||||
"Install class, provides install routines for pisi packages"
|
||||
|
||||
def __init__(self, package_fname, ignore_dep = None):
|
||||
"initialize from a file name"
|
||||
super(Install, self).__init__(ignore_dep)
|
||||
self.package = Package(package_fname)
|
||||
self.package.read()
|
||||
self.metadata = self.package.metadata
|
||||
self.files = self.package.files
|
||||
self.pkginfo = self.metadata.package
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
"entry point"
|
||||
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
|
||||
(self.pkginfo.name, self.pkginfo.version,
|
||||
self.pkginfo.release, self.pkginfo.build))
|
||||
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
|
||||
self.ask_reinstall = ask_reinstall
|
||||
self.check_requirements()
|
||||
self.check_relations()
|
||||
self.check_reinstall()
|
||||
self.extract_install()
|
||||
self.store_pisi_files()
|
||||
if ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
self.register_comar_scripts()
|
||||
if not ctx.config.options.postpone_postinstall:
|
||||
comariface.run_postinstall(self.pkginfo.name)
|
||||
self.update_databases()
|
||||
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.
|
||||
pass
|
||||
|
||||
def check_relations(self):
|
||||
# check conflicts
|
||||
for pkg in self.metadata.package.conflicts:
|
||||
if ctx.installdb.is_installed(self.pkginfo):
|
||||
raise Error(_("Package conflicts %s") % pkg)
|
||||
|
||||
# check dependencies
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
if not self.pkginfo.installable():
|
||||
ctx.ui.error(_('Dependencies for %s not satisfied') %
|
||||
self.pkginfo.name)
|
||||
raise Error(_("Package not installable"))
|
||||
|
||||
# check if package is in database
|
||||
# If it is not, put it into 3rd party packagedb
|
||||
if not packagedb.has_package(self.pkginfo.name):
|
||||
db = packagedb.thirdparty_packagedb
|
||||
db.add_package(self.pkginfo)
|
||||
|
||||
def check_reinstall(self):
|
||||
"check reinstall, confirm action, and schedule reinstall"
|
||||
|
||||
pkg = self.pkginfo
|
||||
|
||||
self.reinstall = False
|
||||
self.upgrade = False
|
||||
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
|
||||
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
|
||||
|
||||
# determine if same version
|
||||
same_ver = False
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
if (not ibuild) or (not pkg.build) or ignore_build:
|
||||
# we don't look at builds to compare two package versions
|
||||
if pkg.version == iversion and pkg.release == irelease:
|
||||
same_ver = True
|
||||
else:
|
||||
if pkg.build == ibuild:
|
||||
same_ver = True
|
||||
|
||||
if same_ver:
|
||||
if self.ask_reinstall:
|
||||
if not ctx.ui.confirm(_('Re-install same version package?')):
|
||||
raise Error(_('Package re-install declined'))
|
||||
else:
|
||||
upgrade = False
|
||||
# is this an upgrade?
|
||||
# determine and report the kind of upgrade: version, release, build
|
||||
if pkg.version > iversion:
|
||||
ctx.ui.info(_('Upgrading to new upstream version'))
|
||||
upgrade = True
|
||||
elif pkg.release > irelease:
|
||||
ctx.ui.info(_('Upgrading to new distribution release'))
|
||||
upgrade = True
|
||||
elif ((not ignore_build) and ibuild and pkg.build
|
||||
and pkg.build > ibuild):
|
||||
ctx.ui.info(_('Upgrading to new distribution build'))
|
||||
upgrade = True
|
||||
self.upgrade = upgrade
|
||||
|
||||
# is this a downgrade? confirm this action.
|
||||
if self.ask_reinstall and (not upgrade):
|
||||
if pkg.version < iversion:
|
||||
x = _('Downgrade to old upstream version?')
|
||||
elif pkg.release < irelease:
|
||||
x = _('Downgrade to old distribution release?')
|
||||
else:
|
||||
x = _('Downgrade to old distribution build?')
|
||||
if not ctx.ui.confirm(x):
|
||||
raise Error(_('Package downgrade declined'))
|
||||
|
||||
|
||||
# schedule for reinstall
|
||||
self.old_files = ctx.installdb.files(pkg.name)
|
||||
self.reinstall = True
|
||||
Remove(pkg.name).run_preremove()
|
||||
|
||||
def extract_install(self):
|
||||
"unzip package in place"
|
||||
|
||||
ctx.ui.info(_('Extracting files'))
|
||||
self.package.extract_dir_flat('install', ctx.config.dest_dir())
|
||||
|
||||
if self.reinstall:
|
||||
# remove left over files
|
||||
new = set(map(lambda x: str(x.path), self.files.list))
|
||||
old = set(map(lambda x: str(x.path), self.old_files.list))
|
||||
leftover = old - new
|
||||
old_fileinfo = {}
|
||||
for fileinfo in self.old_files.list:
|
||||
old_fileinfo[str(fileinfo.path)] = fileinfo
|
||||
for path in leftover:
|
||||
Remove.remove_file( old_fileinfo[path] )
|
||||
|
||||
def store_pisi_files(self):
|
||||
"""put files.xml, metadata.xml, actions.py and COMAR scripts
|
||||
somewhere in the file system. We'll need these in future..."""
|
||||
|
||||
ctx.ui.info(_('Storing %s, ') % ctx.const.files_xml)
|
||||
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
|
||||
|
||||
ctx.ui.info(_('Storing %s.') % ctx.const.metadata_xml)
|
||||
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
|
||||
# comar prefix is added to the pkg_dir while extracting comar
|
||||
# script file. so we'll use pkg_dir as destination.
|
||||
ctx.ui.info(_('Storing %s') % fpath)
|
||||
self.package.extract_file(fpath, self.package.pkg_dir())
|
||||
|
||||
def register_comar_scripts(self):
|
||||
"register COMAR scripts"
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script)
|
||||
import pisi.comariface
|
||||
pisi.comariface.register(pcomar, self.metadata.package.name,
|
||||
scriptPath)
|
||||
|
||||
def update_databases(self):
|
||||
"update databases"
|
||||
|
||||
if self.reinstall:
|
||||
Remove(self.metadata.package.name).remove_db()
|
||||
|
||||
# installdb
|
||||
ctx.installdb.install(self.metadata.package.name,
|
||||
self.metadata.package.version,
|
||||
self.metadata.package.release,
|
||||
self.metadata.package.build,
|
||||
self.metadata.package.distribution)
|
||||
|
||||
# filesdb
|
||||
ctx.filesdb.add_files(self.metadata.package.name, self.files)
|
||||
|
||||
# installed packages
|
||||
packagedb.inst_packagedb.add_package(self.pkginfo)
|
||||
|
||||
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"""
|
||||
# find package in repository
|
||||
repo = packagedb.which_repo(name)
|
||||
if repo:
|
||||
repo = ctx.repodb.get_repo(repo)
|
||||
pkg = packagedb.get_package(name)
|
||||
|
||||
# FIXME: let pkg.packageURI be stored as URI type rather than string
|
||||
pkg_uri = URI(pkg.packageURI)
|
||||
if pkg_uri.is_absolute_path():
|
||||
pkg_path = str(pkg.packageURI)
|
||||
else:
|
||||
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(pkg_uri.path()))
|
||||
|
||||
ctx.ui.debug(_("Package URI: %s") % pkg_path)
|
||||
|
||||
# Package will handle remote file for us!
|
||||
install_single_file(pkg_path, upgrade)
|
||||
else:
|
||||
raise Error(_("Package %s not found in any active repository.") % name)
|
||||
|
||||
|
||||
class Remove(AtomicOperation):
|
||||
|
||||
def __init__(self, package_name, ignore_dep = None):
|
||||
super(Remove, self).__init__(ignore_dep)
|
||||
self.package_name = package_name
|
||||
|
||||
def run(self):
|
||||
"""Remove a single package"""
|
||||
inst_packagedb = packagedb.inst_packagedb
|
||||
self.package = packagedb.get_package(self.package_name)
|
||||
self.files = ctx.installdb.files(self.package_name)
|
||||
|
||||
ctx.ui.status(_('Removing package %s') % self.package_name)
|
||||
if not ctx.installdb.is_installed(self.package_name):
|
||||
raise Exception(_('Trying to remove nonexistent package ')
|
||||
+ self.package_name)
|
||||
|
||||
self.check_dependencies()
|
||||
|
||||
self.run_preremove()
|
||||
|
||||
for fileinfo in self.files.list:
|
||||
self.remove_file(fileinfo)
|
||||
|
||||
self.remove_db()
|
||||
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.type == ctx.const.conf:
|
||||
if os.path.isfile(fpath):
|
||||
os.rename(fpath, fpath + ".pisi")
|
||||
else:
|
||||
# check if file is removed manually.
|
||||
# And we don't remove directories!
|
||||
# TODO: remove directory if there is nothing under it?
|
||||
if os.path.isfile(fpath) or os.path.islink(fpath):
|
||||
os.unlink(fpath)
|
||||
else:
|
||||
ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath)
|
||||
|
||||
remove_file = staticmethod(remove_file)
|
||||
|
||||
def run_preremove(self):
|
||||
if ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
comariface.run_preremove(self.package_name)
|
||||
else:
|
||||
# TODO: store this somewhere
|
||||
pass
|
||||
|
||||
def remove_pisi_files(self):
|
||||
util.clean_dir(self.package.pkg_dir())
|
||||
|
||||
def remove_db(self):
|
||||
ctx.installdb.remove(self.package_name)
|
||||
ctx.filesdb.remove_files(ctx.installdb.files(self.package_name))
|
||||
#FIXME: this looks like a mistake!
|
||||
packagedb.remove_package(self.package_name)
|
||||
|
||||
|
||||
def remove_single(package_name):
|
||||
Remove(package_name).run()
|
||||
|
||||
def __is_virtual_upgrade(metadata):
|
||||
|
||||
pkg = metadata.package
|
||||
|
||||
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
|
||||
|
||||
upgrade = False
|
||||
if pkg.version > iversion:
|
||||
upgrade = True
|
||||
elif pkg.release > irelease:
|
||||
upgrade = True
|
||||
elif (ibuild and pkg.build and pkg.build > ibuild):
|
||||
upgrade = True
|
||||
|
||||
return upgrade
|
||||
|
||||
def virtual_install(metadata, files):
|
||||
"""Recreate the package info for rebuilddb command"""
|
||||
pkg = metadata.package
|
||||
|
||||
# normally this can't be true. Just for backward compatibility
|
||||
# TODO: for speed only ctx.installdb.install exception can be
|
||||
# handled but this is much cleaner
|
||||
if ctx.installdb.is_installed(pkg.name):
|
||||
if __is_virtual_upgrade(metadata):
|
||||
ctx.installdb.remove(pkg.name)
|
||||
packagedb.remove_package(pkg.name)
|
||||
ctx.filesdb.remove_files(ctx.installdb.files(pkg.name))
|
||||
else:
|
||||
return
|
||||
|
||||
pkginfo = metadata.package
|
||||
|
||||
# installdb
|
||||
ctx.installdb.install(metadata.package.name,
|
||||
metadata.package.version,
|
||||
metadata.package.release,
|
||||
metadata.package.build,
|
||||
metadata.package.distribution)
|
||||
|
||||
# filesdb
|
||||
ctx.filesdb.add_files(metadata.package.name, files)
|
||||
|
||||
# installed packages
|
||||
packagedb.inst_packagedb.add_package(pkginfo)
|
||||
|
||||
def resurrect_package(package_fn):
|
||||
"""Resurrect the package in the PiSi databases"""
|
||||
|
||||
from os.path import exists
|
||||
|
||||
metadata_xml = util.join_path(ctx.config.lib_dir(), package_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)
|
||||
|
||||
files_xml = util.join_path(ctx.config.lib_dir(), package_fn, ctx.const.files_xml)
|
||||
if not exists(files_xml):
|
||||
raise Error, _("Files XML '%s' cannot be found") % files_xml
|
||||
|
||||
files = Files()
|
||||
files.read(files_xml)
|
||||
|
||||
if files.errors():
|
||||
raise Error, _("Invalid %s") % ctx.const.files_xml
|
||||
|
||||
import pisi.atomicoperations
|
||||
pisi.atomicoperations.virtual_install(metadata, files)
|
||||
ctx.ui.info(_('OK.'))
|
||||
@@ -1,546 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package bulding stuff
|
||||
# maintainer: baris and meren
|
||||
|
||||
# python standard library
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
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, File as FileInfo
|
||||
from pisi.files import Files
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.package import Package
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
# Helper Functions
|
||||
def get_file_type(path, pinfoList):
|
||||
"""Return the file type of a path according to the given PathInfo
|
||||
list"""
|
||||
# The usage of depth is somewhat confusing. It is used for finding
|
||||
# the best match to paths(in pinfolist). For an example, if paths
|
||||
# contain ['/usr/share','/usr/share/doc'] and path is
|
||||
# /usr/share/doc/filename our iteration over paths should match
|
||||
# the second item.
|
||||
depth = 0
|
||||
ftype = ""
|
||||
path = "/"+path # we need a real path.
|
||||
for pinfo in pinfoList:
|
||||
if util.subpath(pinfo.path, path):
|
||||
length = len(pinfo.path)
|
||||
if depth < length:
|
||||
depth = length
|
||||
ftype = pinfo.fileType
|
||||
return ftype
|
||||
|
||||
def check_path_collision(package, pkgList):
|
||||
"""This function will check for collision of paths in a package with
|
||||
the paths of packages in pkgList. The return value will be the
|
||||
list containing the paths that collide."""
|
||||
collisions = []
|
||||
for pinfo in package.files:
|
||||
for pkg in pkgList:
|
||||
if pkg is package:
|
||||
continue
|
||||
for path in pkg.files:
|
||||
# if pinfo.path is a subpath of path.path like
|
||||
# the example below. path.path is marked as a
|
||||
# collide. Exp:
|
||||
# pinfo.path: /usr/share
|
||||
# path.path: /usr/share/doc
|
||||
if util.subpath(pinfo.path, path.path):
|
||||
collisions.append(path.path)
|
||||
ctx.ui.error(_('Path %s belongs in multiple packages') %
|
||||
path.path)
|
||||
return collisions
|
||||
|
||||
# a dynamic build context
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
|
||||
class BuildContext(object):
|
||||
"""Build Context Singleton"""
|
||||
|
||||
def __init__(self, pspecfile):
|
||||
super(BuildContext, self).__init__()
|
||||
self.set_spec_file(pspecfile)
|
||||
|
||||
def set_spec_file(self, pspecfile):
|
||||
self.pspecfile = pspecfile
|
||||
spec = SpecFile()
|
||||
spec.read(pspecfile)
|
||||
# FIXME: following checks the integrity but does nothing when it is wrong
|
||||
# -gurer
|
||||
#spec.verify() # check pspec integrity
|
||||
self.spec = spec
|
||||
|
||||
# directory accessor functions
|
||||
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def pkg_dir(self):
|
||||
"package build directory"
|
||||
packageDir = self.spec.source.name + '-' + \
|
||||
self.spec.source.version + '-' + self.spec.source.release
|
||||
return util.join_path(ctx.config.dest_dir(), ctx.config.values.dirs.tmp_dir,
|
||||
packageDir)
|
||||
|
||||
def pkg_work_dir(self):
|
||||
return self.pkg_dir() + ctx.const.work_dir_suffix
|
||||
|
||||
def pkg_install_dir(self):
|
||||
return self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
|
||||
class Builder:
|
||||
"""Provides the package build and creation routines"""
|
||||
def __init__(self, pspec):
|
||||
self.bctx = BuildContext(pspec)
|
||||
self.pspecDir = os.path.dirname(os.path.realpath(self.bctx.pspecfile))
|
||||
self.spec = self.bctx.spec
|
||||
self.sourceArchive = SourceArchive(self.bctx)
|
||||
|
||||
self.set_environment_vars()
|
||||
|
||||
self.actionLocals = None
|
||||
self.actionGlobals = None
|
||||
self.srcDir = None
|
||||
|
||||
def set_state(self, state):
|
||||
stateFile = util.join_path(self.bctx.pkg_work_dir(), "pisiBuildState")
|
||||
open(stateFile, "w").write(state)
|
||||
|
||||
def get_state(self):
|
||||
stateFile = util.join_path(self.bctx.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.info(_("Building PISI source package: %s") % self.spec.source.name)
|
||||
util.xterm_title(_("Building PISI source package: %s\n") % 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_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
|
||||
self.build_packages()
|
||||
|
||||
def set_environment_vars(self):
|
||||
"""Sets the environment variables for actions API to use"""
|
||||
evn = {
|
||||
"PKG_DIR": self.bctx.pkg_dir(),
|
||||
"WORK_DIR": self.bctx.pkg_work_dir(),
|
||||
"INSTALL_DIR": self.bctx.pkg_install_dir(),
|
||||
"SRC_NAME": self.spec.source.name,
|
||||
"SRC_VERSION": self.spec.source.version,
|
||||
"SRC_RELEASE": self.spec.source.release
|
||||
}
|
||||
os.environ.update(evn)
|
||||
|
||||
# First check icecream, if not found use ccache, no need to use both
|
||||
# together (according to kde-wiki it cause performance loss)
|
||||
if os.path.exists("/opt/icecream/bin/gcc"):
|
||||
# Add icecream directory for support distributed compiling :)
|
||||
os.environ["PATH"] = "/opt/icecream/bin/:" + os.environ["PATH"]
|
||||
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
|
||||
elif os.path.exists("/usr/lib/ccache/bin/gcc"):
|
||||
# Add ccache directory for support Compiler Cache :)
|
||||
os.environ["PATH"] = "/usr/lib/ccache/bin/:" + os.environ["PATH"]
|
||||
ctx.ui.info(_("CCache detected..."))
|
||||
|
||||
def fetch_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..."))
|
||||
self.sourceArchive.unpack()
|
||||
ctx.ui.info(_(" unpacked (%s)") % self.bctx.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.bctx.pkg_install_dir()):
|
||||
util.clean_dir(self.bctx.pkg_install_dir())
|
||||
|
||||
# install function is mandatory!
|
||||
self.run_action_function(ctx.const.install_func, True)
|
||||
self.set_state("installaction")
|
||||
|
||||
def compile_action_script(self):
|
||||
"""Compiles actions.py and sets the actionLocals and actionGlobals"""
|
||||
specdir = os.path.dirname(self.bctx.pspecfile)
|
||||
scriptfile = util.join_path(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.bctx.pkg_work_dir(), workdir)
|
||||
|
||||
def run_action_function(self, func, mandatory=False):
|
||||
"""Calls the corresponding function in actions.py.
|
||||
|
||||
If mandatory parameter is True, and function is not present in
|
||||
actionLocals pisi.build.Error will be raised."""
|
||||
# we'll need our working directory after actionscript
|
||||
# finished its work in the archive source directory.
|
||||
curDir = os.getcwd()
|
||||
os.chdir(self.srcDir)
|
||||
|
||||
|
||||
if func in self.actionLocals:
|
||||
self.actionLocals[func]()
|
||||
else:
|
||||
if mandatory:
|
||||
Error, _("unable to call function from actions: %s") %func
|
||||
|
||||
os.chdir(curDir)
|
||||
|
||||
def check_build_dependencies(self):
|
||||
"""fail if dependencies not satisfied"""
|
||||
|
||||
# find out the build dependencies that are not satisfied...
|
||||
dep_unsatis = []
|
||||
for dep in self.spec.source.buildDependencies:
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
dep_unsatis.append(dep)
|
||||
|
||||
if dep_unsatis:
|
||||
ctx.ui.info(_("Unsatisfied Build Dependencies:"))
|
||||
for dep in dep_unsatis:
|
||||
ctx.ui.warning(dep.package)
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
if ctx.ui.confirm(
|
||||
_('Do you want to install the unsatisfied build dependencies')):
|
||||
ctx.ui.info(_('Installing build dependencies.'))
|
||||
operations.install([dep.package for dep in dep_unsatis])
|
||||
else:
|
||||
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
|
||||
else:
|
||||
ctx.ui.warning(_('Ignoring build dependencies.'))
|
||||
|
||||
def patch_exists(self):
|
||||
"""check existence of patch files declared in PSPEC"""
|
||||
|
||||
files_dir = os.path.abspath(util.join_path(self.pspecDir,
|
||||
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.pspecDir,
|
||||
ctx.const.files_dir))
|
||||
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = util.join_path(files_dir, patch.filename)
|
||||
if patch.compressionType:
|
||||
patchFile = util.uncompress(patchFile,
|
||||
compressType=patch.compressionType,
|
||||
targetDir=ctx.config.tmp_dir())
|
||||
|
||||
ctx.ui.action(_("* Applying patch: %s") % patch.filename)
|
||||
util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target)
|
||||
|
||||
def strip_install_dir(self):
|
||||
"""strip install directory"""
|
||||
ctx.ui.action(_("Stripping files.."))
|
||||
install_dir = self.bctx.pkg_install_dir()
|
||||
try:
|
||||
nostrip = self.actionGlobals['NoStrip']
|
||||
util.strip_directory(install_dir, nostrip)
|
||||
except KeyError:
|
||||
util.strip_directory(install_dir)
|
||||
|
||||
def gen_metadata_xml(self, package):
|
||||
"""Generate the metadata.xml file for build source.
|
||||
|
||||
metadata.xml is composed of the information from specfile plus
|
||||
some additional information."""
|
||||
metadata = MetaData()
|
||||
metadata.from_spec(self.spec.source, package)
|
||||
|
||||
metadata.package.distribution = ctx.config.values.general.distribution
|
||||
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
|
||||
metadata.package.architecture = "Any"
|
||||
|
||||
size, d = 0, self.bctx.pkg_install_dir()
|
||||
|
||||
for path in package.files:
|
||||
size += util.dir_size(util.join_path(d, path.path))
|
||||
|
||||
metadata.package.installedSize = size
|
||||
|
||||
# build no
|
||||
if ctx.config.options.ignore_build_no:
|
||||
metadata.package.build = None # means, build no information n/a
|
||||
ctx.ui.warning(_('build number is not available.'))
|
||||
else:
|
||||
metadata.package.build = self.calc_build_no(metadata.package.name)
|
||||
|
||||
metadata_xml_path = util.join_path(self.bctx.pkg_dir(), ctx.const.metadata_xml)
|
||||
metadata.write(metadata_xml_path)
|
||||
self.metadata = metadata
|
||||
|
||||
def gen_files_xml(self, package):
|
||||
"""Generates files.xml using the path definitions in specfile and
|
||||
the files produced by the build system."""
|
||||
files = Files()
|
||||
install_dir = self.bctx.pkg_install_dir()
|
||||
|
||||
# FIXME: We need to expand globs before trying to calculate hashes
|
||||
# Not on the fly like now.
|
||||
|
||||
# we'll exclude collisions in get_file_hashes. Having a
|
||||
# collisions list is not wrong, we must just handle it :).
|
||||
# sure -- exa
|
||||
collisions = check_path_collision(package, self.spec.packages)
|
||||
# FIXME: material collisions after expanding globs must be
|
||||
# reported as errors, and an exception must be raised
|
||||
|
||||
d = {}
|
||||
def add_path(path):
|
||||
# add the files under material path
|
||||
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
|
||||
frpath = util.removepathprefix(install_dir, fpath) # relative path
|
||||
ftype = get_file_type(frpath, package.files)
|
||||
try: # broken links and empty dirs can cause problem
|
||||
fsize = os.path.getsize(fpath)
|
||||
except OSError:
|
||||
fsize = None
|
||||
d[frpath] = FileInfo(path=frpath, type=ftype, 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.bctx.pkg_dir(), ctx.const.files_xml)
|
||||
files.write(files_xml_path)
|
||||
self.files = files
|
||||
|
||||
def calc_build_no(self, package_name):
|
||||
"""Calculate build number"""
|
||||
|
||||
# find previous build in output dir and packages dir
|
||||
found = []
|
||||
def locate_package_names(files):
|
||||
for fn in files:
|
||||
fn = fn.decode('utf-8')
|
||||
if util.is_package_name(fn, package_name):
|
||||
old_package_fn = util.join_path(root, fn)
|
||||
ctx.ui.info('(found old version %s)' % old_package_fn)
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
if str(old_pkg.metadata.package.name) != package_name:
|
||||
ctx.ui.warning('Skipping %s with wrong pkg name ' %
|
||||
old_package_fn)
|
||||
continue
|
||||
old_build = old_pkg.metadata.package.build
|
||||
found.append( (old_package_fn, old_build) )
|
||||
|
||||
for root, dirs, files in os.walk(ctx.config.options.output_dir):
|
||||
dirs = [] # don't recurse
|
||||
locate_package_names(files)
|
||||
for root, dirs, files in os.walk(ctx.config.packages_dir()):
|
||||
locate_package_names(files)
|
||||
|
||||
if not found:
|
||||
return 1
|
||||
ctx.ui.warning(_('(no previous build found, setting build no to 1.)'))
|
||||
else:
|
||||
a = filter(lambda (x,y): y != None, found)
|
||||
ctx.ui.debug(str(a))
|
||||
if a:
|
||||
a.sort(lambda x,y : cmp(x[1],y[1]))
|
||||
old_package_fn = a[0][0]
|
||||
old_build = a[0][1]
|
||||
else:
|
||||
old_build = None
|
||||
|
||||
# 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
|
||||
|
||||
# set build number
|
||||
if old_build is None:
|
||||
ctx.ui.warning(_('(old package lacks a build no, setting build no to 0.)'))
|
||||
return 0
|
||||
elif changed:
|
||||
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
|
||||
return old_build + 1
|
||||
else:
|
||||
ctx.ui.info(_('There is no change from previous build %d ') % old_build)
|
||||
return old_build
|
||||
|
||||
def build_packages(self):
|
||||
"""Build each package defined in PSPEC file. After this process there
|
||||
will be .pisi files hanging around, AS INTENDED ;)"""
|
||||
|
||||
# Strip install directory before building .pisi packages.
|
||||
self.strip_install_dir()
|
||||
|
||||
for package in self.spec.packages:
|
||||
|
||||
# store additional files
|
||||
c = os.getcwd()
|
||||
os.chdir(self.pspecDir)
|
||||
install_dir = self.bctx.pkg_dir() + ctx.const.install_dir_suffix
|
||||
for afile in package.additionalFiles:
|
||||
destdir = util.join_path(install_dir, os.path.dirname(afile.target))
|
||||
for src in glob.glob(util.join_path(ctx.const.files_dir, afile.filename)):
|
||||
destfile = os.path.basename(afile.target)
|
||||
if not destfile: destfile = os.path.basename(src)
|
||||
ctx.ui.debug(_("Copying additional file: '%s' to '%s' as '%s'") \
|
||||
% (src, destdir, destfile))
|
||||
util.copy_file(src, util.join_path(destdir, destfile))
|
||||
if afile.permission:
|
||||
# mode is octal!
|
||||
os.chmod(util.join_path(destdir, destfile), int(afile.permission, 8))
|
||||
|
||||
os.chdir(c)
|
||||
|
||||
ctx.ui.action(_("** Building package %s") % package.name);
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.files_xml)
|
||||
self.gen_files_xml(package)
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.metadata_xml)
|
||||
self.gen_metadata_xml(package)
|
||||
|
||||
ctx.ui.info(_("Creating PISI package %s.") % package.name)
|
||||
|
||||
name = util.package_name(package.name,
|
||||
self.spec.source.version,
|
||||
self.spec.source.release,
|
||||
self.metadata.package.build)
|
||||
pkg = Package(name, 'w')
|
||||
|
||||
# add comar files to package
|
||||
os.chdir(self.pspecDir)
|
||||
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.bctx.pkg_dir())
|
||||
|
||||
pkg.add_to_package(ctx.const.metadata_xml)
|
||||
pkg.add_to_package(ctx.const.files_xml)
|
||||
|
||||
# Now it is time to add files to the packages using newly
|
||||
# created files.xml
|
||||
files = Files()
|
||||
files.read(ctx.const.files_xml)
|
||||
for finfo in files.list:
|
||||
pkg.add_to_package("install/" + finfo.path)
|
||||
|
||||
pkg.close()
|
||||
os.chdir(c)
|
||||
self.set_state("buildpackages")
|
||||
util.xterm_title_reset()
|
||||
ctx.ui.info(_("Done."))
|
||||
|
||||
if ctx.config.values.general.autoclean is True:
|
||||
ctx.ui.info(_("Cleaning Build Directory..."))
|
||||
util.clean_dir(self.bctx.pkg_dir())
|
||||
else:
|
||||
ctx.ui.info(_("Keeping Build Directory"))
|
||||
@@ -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,137 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import locale
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.ui import UI
|
||||
import pisi.ui as ui
|
||||
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 output(self, msg, err = False):
|
||||
if type(msg)==type(unicode()):
|
||||
msg = msg.encode('utf-8')
|
||||
if err:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
out.write(msg)
|
||||
out.flush()
|
||||
|
||||
def info(self, msg, verbose = False, noln = False):
|
||||
# TODO: need to look at more kinds of info messages
|
||||
# let's cheat from KDE :)
|
||||
if noln:
|
||||
msgend = ''
|
||||
else:
|
||||
msgend = '\n'
|
||||
if verbose and self.show_verbose:
|
||||
self.output(msg + msgend)
|
||||
elif not verbose:
|
||||
self.output(msg + msgend)
|
||||
|
||||
def warning(self,msg):
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Warning: ') + msg + '\n', err=True)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'purple'), err=True)
|
||||
|
||||
def error(self,msg):
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Error: ') + msg + '\n', err=True)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'red'), err=True)
|
||||
|
||||
def action(self,msg):
|
||||
#TODO: this seems quite redundant?
|
||||
self.output(colorize(msg + '\n', 'green'))
|
||||
|
||||
def choose(self, msg, opts):
|
||||
print msg
|
||||
for i in range(0,len(opts)):
|
||||
print i + 1, opts(i)
|
||||
while True:
|
||||
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
|
||||
try:
|
||||
opt = int(s)
|
||||
if 1 <= opt and opt <= len(opts):
|
||||
return opts(opt-1)
|
||||
except (Exception,e):
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
if ctx.config.options and ctx.config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
import re
|
||||
yesexpr = re.compile(locale.nl_langinfo(locale.YESEXPR))
|
||||
|
||||
prompt = msg + colorize(_('(yes/no)'), 'red')
|
||||
s = raw_input(prompt.encode('utf-8'))
|
||||
if yesexpr.search(s):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def display_progress(self, **ka):
|
||||
out = '\r%-30.30s %3d%% %12.2f %s' % \
|
||||
(ka['filename'], ka['percent'], ka['rate'], ka['symbol'])
|
||||
self.output(out)
|
||||
if ka['percent'] == 100:
|
||||
self.output(colorize(' [complete]\n', 'gray'))
|
||||
|
||||
def status(self, msg = None):
|
||||
if msg:
|
||||
self.output(colorize(msg + '\n', 'purple'))
|
||||
|
||||
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
|
||||
else:
|
||||
msg = None
|
||||
if msg:
|
||||
self.output(colorize(msg + '\n', 'cyan'))
|
||||
@@ -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,107 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.cli
|
||||
from pisi.cli import printu
|
||||
from pisi.uri import URI
|
||||
from pisi.cli.commands import *
|
||||
|
||||
class ParserError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PreParser(OptionParser):
|
||||
"""consumes any options, and finds arguments from command line"""
|
||||
|
||||
def __init__(self, version):
|
||||
OptionParser.__init__(self, usage=usage_text, version=version)
|
||||
|
||||
def error(self, msg):
|
||||
raise ParserError, msg
|
||||
|
||||
def parse_args(self, args=None):
|
||||
self.opts = []
|
||||
self.rargs = self._get_args(args)
|
||||
self._process_args()
|
||||
return (self.opts, self.args)
|
||||
|
||||
def _process_args(self):
|
||||
args = []
|
||||
rargs = self.rargs
|
||||
if not self.allow_interspersed_args:
|
||||
first_arg = False
|
||||
while rargs:
|
||||
arg = rargs[0]
|
||||
def option():
|
||||
if not self.allow_interspersed_args and first_arg:
|
||||
self.error_(('Options must precede non-option arguments'))
|
||||
self.opts.append(rargs[0][2:])
|
||||
del rargs[0]
|
||||
return
|
||||
# We handle bare "--" explicitly, and bare "-" is handled by the
|
||||
# standard arg handler since the short arg case ensures that the
|
||||
# len of the opt string is greater than 1.
|
||||
if arg == "--":
|
||||
del rargs[0]
|
||||
break
|
||||
elif arg[0:2] == "--":
|
||||
# process a single long option (possibly with value(s))
|
||||
option()
|
||||
elif arg[:1] == "-" and len(arg) > 1:
|
||||
# process a cluster of short options (possibly with
|
||||
# value(s) for the last one only)
|
||||
option()
|
||||
else: # then it must be an argument
|
||||
args.append(arg)
|
||||
del rargs[0]
|
||||
self.args = args
|
||||
|
||||
|
||||
class PisiCLI(object):
|
||||
|
||||
def __init__(self):
|
||||
# first construct a parser for common options
|
||||
# this is really dummy
|
||||
self.parser = PreParser(version="%prog " + pisi.__version__)
|
||||
|
||||
try:
|
||||
opts, args = self.parser.parse_args()
|
||||
if len(args)==0: # more explicit than using IndexError
|
||||
if 'version' in opts:
|
||||
self.parser.print_version()
|
||||
sys.exit(0)
|
||||
printu(_('No command given'))
|
||||
self.die()
|
||||
cmd_name = args[0]
|
||||
except ParserError:
|
||||
printu(_('Command line parsing error'))
|
||||
self.die()
|
||||
|
||||
self.command = Command.get_command(cmd_name)
|
||||
if not self.command:
|
||||
printu(_("Unrecognized command: %s") % cmd_name)
|
||||
self.die()
|
||||
|
||||
def die(self):
|
||||
printu('\n' + self.parser.format_help())
|
||||
sys.exit(1)
|
||||
|
||||
def run_command(self):
|
||||
self.command.run()
|
||||
@@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@uludag.org.tr>
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
|
||||
import comar
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def make_com():
|
||||
try:
|
||||
import comar
|
||||
comard = comar.Link()
|
||||
return comard
|
||||
except ImportError:
|
||||
raise Error(_("COMAR: comard not fully installed"))
|
||||
except comar.Error:
|
||||
raise Error(_("COMAR: comard not running or defunct"))
|
||||
|
||||
def register(pcomar, name, path):
|
||||
ctx.ui.info(_("Registering COMAR script %s") % pcomar.script)
|
||||
com = make_com()
|
||||
assert(com)
|
||||
com.register(pcomar.om, name, path)
|
||||
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
else:
|
||||
raise Error, _("COMAR.register ERROR!")
|
||||
|
||||
def run_postinstall(package_name):
|
||||
"run postinstall scripts trough COMAR"
|
||||
|
||||
com = make_com()
|
||||
assert(com)
|
||||
ctx.ui.info(_("Running post-install script for %s") % package_name)
|
||||
com.call_package("System.Package.postInstall", package_name)
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[0] == com.NONE: # package has no postInstall script
|
||||
break
|
||||
elif reply[0] == com.FAIL:
|
||||
e = _("COMAR.call_package(System.Package.postInstall, %s) failed!: %s") % (
|
||||
package_name, reply[2])
|
||||
raise Error, e
|
||||
else:
|
||||
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
|
||||
|
||||
def run_preremove(package_name):
|
||||
|
||||
com = make_com()
|
||||
assert(com)
|
||||
|
||||
# First, call preRemove script!
|
||||
ctx.ui.info(_("Running pre-remove script for %s") % package_name)
|
||||
com.call_package("System.Package.preRemove", package_name)
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[0] == com.NONE: # package has no preRemove script
|
||||
break
|
||||
elif reply[0] == com.FAIL:
|
||||
e = _("COMAR.call_package(System.Package.preRemove, %s) failed!: %s") % (
|
||||
package_name, reply[2])
|
||||
raise Error, e
|
||||
else:
|
||||
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
|
||||
|
||||
# and then, remove package's Comar Scripts...
|
||||
ctx.ui.info(_("Unregistering COMAR scripts for %s") % package_name)
|
||||
com.remove(package_name)
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[1] == com.ERROR:
|
||||
raise Error, "COMAR.remove failed!"
|
||||
@@ -1,62 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"""a placeholder for data types, might factor elsewhere later"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.pxml.xmlfile as xmlfile
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
__metaclass__ = xmlfile.autoxml
|
||||
|
||||
|
||||
class Distribution:
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.LocalText, xmlfile.mandatory]
|
||||
t_Version = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Type = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Dependencies = [ [xmlfile.Text], xmlfile.optional, "Distribution"]
|
||||
|
||||
|
||||
class Component(xmlfile.XmlFile):
|
||||
"part-of representation for component declarations"
|
||||
|
||||
__metaclass__ = xmlfile.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory] # fully qualified name
|
||||
t_PartOf = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_LocalName = [xmlfile.LocalText, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.LocalText, xmlfile.mandatory]
|
||||
#t_Icon = [xmlfile.Binary, xmlfile.mandatory]
|
||||
t_Dependencies = [ [xmlfile.String], xmlfile.optional, "Dependencies/Component"]
|
||||
|
||||
|
||||
class ComponentTree(xmlfile.XmlFile):
|
||||
"index representation for the component structure"
|
||||
|
||||
__metaclass__ = xmlfile.autoxml
|
||||
|
||||
tag = "Component"
|
||||
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory] # fully qualified name
|
||||
#t_Icon = [xmlfile.Binary, xmlfile.mandatory]
|
||||
t_Dependencies = [ [xmlfile.Text], xmlfile.optional, "Component"]
|
||||
t_Parts = [ [ComponentTree], xmlfile.optional, "Component"]
|
||||
@@ -1,100 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@uludag.org.tr>
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"""
|
||||
PISI Configuration module is used for gathering and providing
|
||||
regular PISI configurations.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.configfile import ConfigurationFile
|
||||
import pisi.util
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Options(object):
|
||||
def __getattr__(self, name):
|
||||
if not self.__dict__.has_key(name):
|
||||
return None
|
||||
else:
|
||||
return self.__dict__[name]
|
||||
|
||||
class Config(object):
|
||||
"""Config Singleton"""
|
||||
|
||||
def __init__(self, options = Options()):
|
||||
self.options = options
|
||||
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
|
||||
|
||||
def get_option(self, opt):
|
||||
if self.options:
|
||||
if hasattr(self.options, opt):
|
||||
return getattr(self.options, opt)
|
||||
return None
|
||||
|
||||
# directory accessor functions
|
||||
# here is how it goes
|
||||
# x_dir: system wide directory for storing info type x
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def dest_dir(self):
|
||||
dir = self.get_option('destdir')
|
||||
if dir:
|
||||
dir = str(dir)
|
||||
else:
|
||||
dir = self.values.general.destinationdirectory
|
||||
import os.path
|
||||
if not os.path.exists(dir):
|
||||
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % dir)
|
||||
os.makedirs(dir)
|
||||
return dir
|
||||
|
||||
def subdir(self, path):
|
||||
dir = join(self.dest_dir(), path)
|
||||
pisi.util.check_dir(dir)
|
||||
return dir
|
||||
|
||||
def lib_dir(self):
|
||||
return self.subdir(self.values.dirs.lib_dir)
|
||||
|
||||
def db_dir(self):
|
||||
return self.subdir(self.values.dirs.db_dir)
|
||||
|
||||
def archives_dir(self):
|
||||
return self.subdir(self.values.dirs.archives_dir)
|
||||
|
||||
def packages_dir(self):
|
||||
return self.subdir(self.values.dirs.packages_dir)
|
||||
|
||||
def index_dir(self):
|
||||
return self.subdir(self.values.dirs.index_dir)
|
||||
|
||||
def tmp_dir(self):
|
||||
return self.subdir(self.values.dirs.tmp_dir)
|
||||
|
||||
# bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde
|
||||
# kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye
|
||||
# ihtiyacımız var? (baris)
|
||||
def install_dir(self):
|
||||
return self.tmp_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
#TODO: remove this
|
||||
config = Config()
|
||||
@@ -1,139 +0,0 @@
|
||||
# -*- conding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI Configuration File module, obviously, is used to read from the
|
||||
# configuration file. Module also defines default values for
|
||||
# configuration parameters.
|
||||
#
|
||||
# Configuration file is located in /etc/pisi/pisi.conf by default,
|
||||
# having an INI like format like below.
|
||||
#
|
||||
#[general]
|
||||
#destinationdirectory = /tmp
|
||||
#autoclean = False
|
||||
#
|
||||
#[build]
|
||||
#host = i686-pc-linux-gnu
|
||||
#CFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#CXXFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#LDFLAGS=
|
||||
#
|
||||
#[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
|
||||
|
||||
import os
|
||||
from ConfigParser import ConfigParser, NoSectionError
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class GeneralDefaults:
|
||||
"""Default values for [general] section"""
|
||||
destinationdirectory = "/"
|
||||
autoclean = False
|
||||
distribution = "Pardus"
|
||||
distribution_release = "0.1"
|
||||
|
||||
class BuildDefaults:
|
||||
"""Default values for [build] section"""
|
||||
host = "i686-pc-linux-gnu"
|
||||
CFLAGS = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
CXXFLAGS = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
LDFLAGS = ""
|
||||
|
||||
class DirsDefaults:
|
||||
"Default values for [directories] section"
|
||||
lib_dir = "/var/lib/pisi"
|
||||
db_dir = "/var/db/pisi"
|
||||
archives_dir = "/var/cache/pisi/archives"
|
||||
packages_dir = "/var/cache/pisi/packages"
|
||||
index_dir = "/var/cache/pisi/index"
|
||||
tmp_dir = "/var/tmp/pisi"
|
||||
icon_theme_dir = "/usr/share/icons/Tulliana-1.0"
|
||||
|
||||
|
||||
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,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI constants.
|
||||
# If you have a "magic" constant value this is where it should be
|
||||
# defined.
|
||||
|
||||
# Author: Baris Metin <baris@uludag.org.tr
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
class _constant:
|
||||
"Constant members implementation"
|
||||
class ConstError(TypeError):
|
||||
pass
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't rebind constant: %s") % name
|
||||
# Binding an attribute once to a const is available
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't unbind constant: %s") % name
|
||||
# we don't have an attribute by this name
|
||||
raise NameError, name
|
||||
|
||||
class Constants:
|
||||
"Pisi Constants Singleton"
|
||||
|
||||
__c = _constant()
|
||||
|
||||
def __init__(self):
|
||||
# prefix for package names
|
||||
self.__c.package_suffix = ".pisi"
|
||||
|
||||
# directory suffixes for build
|
||||
self.__c.work_dir_suffix = "/work"
|
||||
self.__c.install_dir_suffix = "/install"
|
||||
|
||||
# file/directory names
|
||||
self.__c.actions_file = "actions.py"
|
||||
self.__c.files_dir = "files"
|
||||
self.__c.metadata_dir = "metadata"
|
||||
self.__c.comar_dir = "comar"
|
||||
self.__c.files_xml = "files.xml"
|
||||
self.__c.metadata_xml = "metadata.xml"
|
||||
self.__c.pisi_index = "pisi-index.xml"
|
||||
|
||||
# functions in actions_file
|
||||
self.__c.setup_func = "setup"
|
||||
self.__c.build_func = "build"
|
||||
self.__c.install_func = "install"
|
||||
|
||||
# file types
|
||||
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,41 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Context module.
|
||||
|
||||
# Authors: Baris Metin <baris@uludag.org.tr
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# global variables here
|
||||
|
||||
import pisi.constants
|
||||
|
||||
const = pisi.constants.Constants()
|
||||
|
||||
config = None
|
||||
|
||||
def get_option(opt):
|
||||
return config and config.get_option(opt)
|
||||
|
||||
# default UI is CLI
|
||||
ui = None # not now
|
||||
|
||||
installdb = None
|
||||
repodb = None
|
||||
|
||||
comar = None
|
||||
|
||||
initialized = False
|
||||
|
||||
#def register(_impl):
|
||||
# """ Register a UI implementation"""
|
||||
# ui = _impl
|
||||
@@ -1,116 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"""dependency analyzer"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
#import pisi.db as db
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
from pisi.version import Version
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.util import Checks
|
||||
|
||||
class Dependency:
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
s_Package = [autoxml.String, autoxml.mandatory]
|
||||
a_versionFrom = [autoxml.String, autoxml.optional]
|
||||
a_versionTo = [autoxml.String, autoxml.optional]
|
||||
a_releaseFrom = [autoxml.String, autoxml.optional]
|
||||
a_releaseTo = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.package
|
||||
if self.versionFrom:
|
||||
s += 'ver >= ' + self.versionFrom
|
||||
if self.versionTo:
|
||||
s += 'ver <= ' + self.versionTo
|
||||
if self.releaseFrom:
|
||||
s += 'rel >= ' + self.releaseFrom
|
||||
if self.releaseTo:
|
||||
s += 'rel <= ' + self.releaseTo
|
||||
return s
|
||||
|
||||
def satisfies(self, pkg_name, version, release):
|
||||
"""determine if a package ver. satisfies given dependency spec"""
|
||||
ret = True
|
||||
v = Version(version)
|
||||
if self.versionFrom:
|
||||
ret &= v >= Version(self.versionFrom)
|
||||
if self.versionTo:
|
||||
ret &= v <= Version(self.versionTo)
|
||||
if self.releaseFrom:
|
||||
ret &= v <= Version(self.releaseFrom)
|
||||
if self.releaseTo:
|
||||
ret &= v <= Version(self.releaseTo)
|
||||
return ret
|
||||
|
||||
def dict_satisfies_dep(dict, depinfo):
|
||||
"""determine if a package in a dictionary satisfies given dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not dict.has_key(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = dict[pkg_name]
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def installed_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.installdb.is_installed(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = packagedb.inst_packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def repo_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not packagedb.has_package(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
|
||||
for dep in deps:
|
||||
if not sat(dep):
|
||||
ctx.ui.error(_('Package %s does not satisfy dependency %s') %
|
||||
(pkg,dep))
|
||||
return False
|
||||
return True
|
||||
|
||||
def satisfies_runtime_deps(pkg):
|
||||
deps = packagedb.get_package(pkg).runtimeDependencies
|
||||
return satisfies_dependencies(pkg, deps)
|
||||
|
||||
def installable(pkg):
|
||||
"""calculate if pkg name is installable currently
|
||||
which means it has to satisfy both install and runtime dependencies"""
|
||||
if not packagedb.has_package(pkg):
|
||||
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
|
||||
return False
|
||||
elif satisfies_runtime_deps(pkg):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -1,304 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
|
||||
class FetchError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
# helper functions
|
||||
def fetch_url(url, dest, progress=None):
|
||||
fetch = Fetcher(url, dest)
|
||||
fetch.progress = progress
|
||||
fetch.fetch()
|
||||
if progress:
|
||||
pass
|
||||
#ctx.ui.info('\n')
|
||||
|
||||
|
||||
class Fetcher:
|
||||
"""Fetcher can fetch a file from various sources using various
|
||||
protocols."""
|
||||
def __init__(self, url, dest):
|
||||
if not isinstance(url, URI):
|
||||
url = URI(url)
|
||||
|
||||
self.scheme = url.scheme()
|
||||
self.url = url
|
||||
self.filedest = dest
|
||||
util.check_dir(self.filedest)
|
||||
self.percent = 0
|
||||
self.rate = 0.0
|
||||
self.progress = None
|
||||
self.existsize = 0
|
||||
|
||||
def fetch (self):
|
||||
"""Return value: Fetched file's full path.."""
|
||||
|
||||
if not self.url.filename():
|
||||
self.err(_("Filename error"))
|
||||
|
||||
if not os.access(self.filedest, os.W_OK):
|
||||
self.err(_("Access denied to write to dest dir"))
|
||||
|
||||
archivefile = os.path.join(self.filedest, self.url.filename())
|
||||
|
||||
if self.url.is_local_file():
|
||||
self.fetchLocalFile(archivefile + ".part")
|
||||
else:
|
||||
self.fetchRemoteFile(archivefile + ".part")
|
||||
|
||||
move(archivefile + ".part", archivefile)
|
||||
return archivefile
|
||||
|
||||
def _do_grab(self, fileURI, dest, totalsize):
|
||||
symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s']
|
||||
bs, tt, = 1024, int(time())
|
||||
size = existsize = self.existsize
|
||||
symbol, depth = "B/s", 0
|
||||
st = time()
|
||||
chunk = fileURI.read(bs)
|
||||
size += len(chunk)
|
||||
if self.progress:
|
||||
p = self.progress(totalsize, existsize)
|
||||
self.percent = p.update(size)
|
||||
self.complete = False
|
||||
while chunk:
|
||||
dest.write(chunk)
|
||||
chunk = fileURI.read(bs)
|
||||
size += len(chunk)
|
||||
ct = time()
|
||||
if int(tt) != int(ct):
|
||||
self.rate = (size - existsize) / (ct - st)
|
||||
while self.rate > 1000 and depth < 3:
|
||||
self.rate /= 1024
|
||||
depth += 1
|
||||
symbol, depth = symbols[depth], 0
|
||||
tt = time()
|
||||
if self.progress:
|
||||
if p.update(size):
|
||||
self.percent = p.percent
|
||||
if not self.complete:
|
||||
ctx.ui.display_progress(filename = self.url.filename(),
|
||||
percent = self.percent,
|
||||
rate = self.rate,
|
||||
symbol = symbol)
|
||||
if self.percent == 100: #FIXME: will be superseded by a
|
||||
self.complete = True # working progress interface
|
||||
|
||||
dest.close()
|
||||
|
||||
def fetchLocalFile (self, archivefile):
|
||||
url = self.url
|
||||
|
||||
if not os.access(url.path(), os.F_OK):
|
||||
self.err(_("No such file or no permission to read"))
|
||||
|
||||
dest = open(archivefile, "w")
|
||||
totalsize = os.path.getsize(url.path())
|
||||
fileObj = open(url.path())
|
||||
self._do_grab(fileObj, dest, totalsize)
|
||||
|
||||
def fetchRemoteFile (self, archivefile):
|
||||
from httplib import HTTPException
|
||||
|
||||
if os.path.exists(archivefile):
|
||||
if self.scheme == "http" or self.scheme == "https" or self.scheme == "ftp":
|
||||
self.existsize = os.path.getsize(archivefile)
|
||||
dest = open(archivefile, "ab")
|
||||
else:
|
||||
dest = open(archivefile, "wb")
|
||||
|
||||
uri = self.url.uri
|
||||
|
||||
try:
|
||||
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
|
||||
headers = fileObj.info()
|
||||
except ValueError, e:
|
||||
self.err(_('Cannot fetch %s; value error: %s') % (uri, e))
|
||||
except IOError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e))
|
||||
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))
|
||||
|
||||
try:
|
||||
totalsize = int(headers['Content-Length']) + self.existsize
|
||||
except:
|
||||
totalsize = 0
|
||||
|
||||
self._do_grab(fileObj, dest, totalsize)
|
||||
|
||||
def formatRequest(self, request):
|
||||
if self.url.auth_info():
|
||||
enc = encodestring("%s:%s" % self.url.auth_info())
|
||||
request.add_header('Authorization', 'Basic %s' % enc)
|
||||
|
||||
range_handlers = {
|
||||
'http' : HTTPRangeHandler,
|
||||
'https': HTTPRangeHandler,
|
||||
'ftp' : FTPRangeHandler
|
||||
}
|
||||
|
||||
if self.existsize and range_handlers.has_key(self.scheme):
|
||||
opener = urllib2.build_opener(range_handlers.get(self.scheme)())
|
||||
urllib2.install_opener(opener)
|
||||
request.add_header('Range', 'bytes=%d-' % self.existsize)
|
||||
|
||||
return request
|
||||
|
||||
def err (self, error):
|
||||
raise FetchError(error)
|
||||
|
||||
class HTTPRangeHandler(urllib2.BaseHandler):
|
||||
"""
|
||||
"to override the urllib2 error: 'Error 206: Partial Content'
|
||||
this reponse from the HTTP server is already what we expected to get.
|
||||
Don't give up, resume downloading..
|
||||
"""
|
||||
|
||||
def http_error_206(self, request, fp, errcode, msg, headers):
|
||||
return urllib.addinfourl(fp, headers, request.get_full_url())
|
||||
|
||||
def http_error_416(self, request, fp, errcode, msg, headers):
|
||||
# HTTP 1.1's 'Range Not Satisfiable' error..
|
||||
raise FetchError(_('Requested range not satisfiable'))
|
||||
|
||||
|
||||
class FTPRangeHandler(urllib2.FTPHandler):
|
||||
"""
|
||||
FTP Range support..
|
||||
"""
|
||||
def ftp_open(self, req):
|
||||
host = req.get_host()
|
||||
host, port = urllib.splitport(host)
|
||||
if port is None:
|
||||
port = ftplib.FTP_PORT
|
||||
|
||||
try:
|
||||
host = socket.gethostbyname(host)
|
||||
except socket.error, msg:
|
||||
raise FetchError(msg)
|
||||
path, attrs = urllib.splitattr(req.get_selector())
|
||||
dirs = path.split('/')
|
||||
dirs = map(urllib.unquote, dirs)
|
||||
dirs, file = dirs[:-1], dirs[-1]
|
||||
if dirs and not dirs[0]:
|
||||
dirs = dirs[1:]
|
||||
try:
|
||||
fw = self.connect_ftp('', '', host, port, dirs)
|
||||
type = file and 'I' or 'D'
|
||||
for attr in attrs:
|
||||
attr, value = urllib.splitattr(attr)
|
||||
if attr.lower() == 'type' and \
|
||||
value in ('a', 'A', 'i', 'I', 'd', 'D'):
|
||||
type = value.upper()
|
||||
|
||||
rawr = req.headers.get('Range', None)
|
||||
rest = int(rawr.split("=")[1].rstrip("-"))
|
||||
|
||||
fp, retrlen = fw.retrfile(file, type, rest)
|
||||
|
||||
fb, lb = rest, retrlen
|
||||
if retrlen is None or retrlen == 0:
|
||||
raise FetchError(_('Requested Range Not Satisfiable'))
|
||||
retrlen = lb - fb
|
||||
if retrlen < 0:
|
||||
# beginning of range is larger than file
|
||||
raise FetchError(_('Requested Range Not Satisfiable'))
|
||||
|
||||
headers = ""
|
||||
mtype = guess_type(req.get_full_url())[0]
|
||||
if mtype:
|
||||
headers += "Content-Type: %s\n" % mtype
|
||||
if retrlen is not None and retrlen >= 0:
|
||||
headers += "Content-Length: %d\n" % retrlen
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError, msg:
|
||||
from StringIO import StringIO
|
||||
|
||||
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
|
||||
except ftplib.all_errors, msg:
|
||||
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
|
||||
|
||||
def connect_ftp(self, user, passwd, host, port, dirs):
|
||||
fw = ftpwrapper('', '', host, port, dirs)
|
||||
return fw
|
||||
|
||||
class ftpwrapper(urllib.ftpwrapper):
|
||||
def retrfile(self, file, type, rest=None):
|
||||
self.endtransfer()
|
||||
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
|
||||
else: cmd = 'TYPE ' + type; isdir = 0
|
||||
try:
|
||||
self.ftp.voidcmd(cmd)
|
||||
except ftplib.all_errors:
|
||||
self.init()
|
||||
self.ftp.voidcmd(cmd)
|
||||
conn = None
|
||||
if file and not isdir:
|
||||
try:
|
||||
self.ftp.nlst(file)
|
||||
except ftplib.error_perm, reason:
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
# Restore the transfer mode!
|
||||
self.ftp.voidcmd(cmd)
|
||||
try:
|
||||
cmd = 'RETR ' + file
|
||||
conn = self.ftp.ntransfercmd(cmd, rest)
|
||||
except ftplib.error_perm, reason:
|
||||
if str(reason)[:3] == '501':
|
||||
# workaround for REST not suported error
|
||||
fp, retrlen = self.retrfile(file, type)
|
||||
fp = RangeableFileObject(fp, (rest,''))
|
||||
return (fp, retrlen)
|
||||
elif str(reason)[:3] != '550':
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
if not conn:
|
||||
self.ftp.voidcmd('TYPE A')
|
||||
if file: cmd = 'LIST ' + file
|
||||
else: cmd = 'LIST'
|
||||
conn = self.ftp.ntransfercmd(cmd)
|
||||
self.busy = 1
|
||||
return (urllib.addclosehook(conn[0].makefile('rb'),
|
||||
self.endtransfer), conn[1])
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Files module provides access to files.xml. files.xml is genarated
|
||||
# during the build process of a package and used in installation.
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.util import Checks
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
class File:
|
||||
"""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" ]
|
||||
|
||||
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 = [ [File], 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):
|
||||
for x in files.list:
|
||||
self[str(x.path)] = (pkg_name, x)
|
||||
|
||||
def remove_files(self, files):
|
||||
for x in files.list:
|
||||
self.delete(str(x.path))
|
||||
|
||||
def has_file(self, path):
|
||||
return self.has_key(str(path))
|
||||
|
||||
def get_file(self, path):
|
||||
path = str(path)
|
||||
if not self.has_key(path):
|
||||
return None
|
||||
else:
|
||||
return self[path]
|
||||
|
||||
def get_files(self, glob):
|
||||
import fnmatch
|
||||
|
||||
glob = str(glob)
|
||||
infos = []
|
||||
for key in self.keys():
|
||||
if fnmatch.fnmatch(key, glob):
|
||||
infos.append(self[key])
|
||||
|
||||
return infos
|
||||
@@ -1,161 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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
|
||||
|
||||
|
||||
# for python 2.3 compatibility
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import pisi
|
||||
|
||||
# not an error!
|
||||
|
||||
class CycleException(pisi.Exception):
|
||||
pass
|
||||
|
||||
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
|
||||
raise CycleException
|
||||
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,101 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
|
||||
"""PISI source/package index"""
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
import pisi.metadata as metadata
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.package import Package
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.uri import URI
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Index(XmlFile):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Sources = [ [specfile.Source], autoxml.optional, "Source"]
|
||||
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
|
||||
|
||||
def read_uri(self, filename, repo = None):
|
||||
"""Read PSPEC file"""
|
||||
|
||||
self.filepath = filename
|
||||
url = URI(filename)
|
||||
if url.is_remote_file():
|
||||
from fetcher import fetch_url
|
||||
assert repo
|
||||
dest = os.path.join(ctx.config.index_dir(), repo)
|
||||
if not os.path.exists(dest):
|
||||
os.makedirs(dest)
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
|
||||
self.filepath = os.path.join(dest, url.filename())
|
||||
|
||||
self.read(self.filepath)
|
||||
|
||||
def index(self, repo_uri):
|
||||
self.repo_dir = repo_uri
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
ctx.ui.info(_('Adding %s to package index') %fn)
|
||||
self.add_package(os.path.join(root, fn), repo_uri)
|
||||
|
||||
def update_db(self, repo):
|
||||
pkgdb = packagedb.get_db(repo)
|
||||
pkgdb.clear()
|
||||
for pkg in self.packages:
|
||||
pkgdb.add_package(pkg)
|
||||
|
||||
def add_package(self, path, repo_uri):
|
||||
package = Package(path, 'r')
|
||||
# extract control files
|
||||
util.clean_dir(ctx.config.install_dir())
|
||||
package.extract_PISI_files(ctx.config.install_dir())
|
||||
|
||||
md = metadata.MetaData()
|
||||
md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml))
|
||||
if ctx.config.options and ctx.config.options.absolute_uris:
|
||||
md.package.packageURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
# TODO: in the future we'll do all of this with purl/pfile/&helpers
|
||||
# After that, we'll remove the ugly repo_uri parameter from this
|
||||
# function.
|
||||
md.package.packageURI = util.removepathprefix(repo_uri, path)
|
||||
# check package semantics
|
||||
errs = md.errors()
|
||||
if md.errors():
|
||||
ctx.ui.error(_('Package %s: metadata corrupt') % md.package.name)
|
||||
ctx.ui.error(str(Error(*errs)))
|
||||
else:
|
||||
self.packages.append(md.package)
|
||||
|
||||
#TODO: add source
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# installation database
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# System
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
class InstallDBError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class InstallInfo:
|
||||
# some data is replicated from packagedb.inst_packagedb
|
||||
# we store as an object, hey, we can waste O(1) space.
|
||||
# this is also easier to modify in the future, without
|
||||
# requiring database upgrades! wow!
|
||||
def __init__(self, state, version, release, build, distribution):
|
||||
self.state = state
|
||||
self.version = version
|
||||
self.release = release
|
||||
self.build = build
|
||||
self.distribution = distribution
|
||||
import time
|
||||
self.time = time.localtime()
|
||||
|
||||
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 = pisi.util.join_path(ctx.config.db_dir(), 'files')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dp.close()
|
||||
|
||||
def files_name(self, pkg, version, release):
|
||||
from pisi.util import join_path as join
|
||||
pkg_dir = join(ctx.config.lib_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 is_recorded(self, pkg):
|
||||
pkg = str(pkg)
|
||||
return self.d.has_key(pkg)
|
||||
|
||||
def is_installed(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.is_recorded(pkg):
|
||||
info = self.d[pkg]
|
||||
return info.state=='i' or info.state=='ip'
|
||||
else:
|
||||
return False
|
||||
|
||||
def list_installed(self):
|
||||
list = []
|
||||
for (pkg, info) in self.d.iteritems():
|
||||
if info.state=='i' or info.state=='ip':
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
def list_pending(self):
|
||||
list = []
|
||||
for (pkg, x) in self.dp.iteritems():
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
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 = ""):
|
||||
"""install package with specific version, release, build"""
|
||||
pkg = str(pkg)
|
||||
if self.is_installed(pkg):
|
||||
raise InstallDBError(_("Already installed"))
|
||||
if ctx.config.get_option('ignore_comar') or \
|
||||
ctx.config.get_option('postpone_postinstall'):
|
||||
state = 'ip'
|
||||
self.dp[pkg] = True
|
||||
else:
|
||||
state = 'i'
|
||||
|
||||
self.d[pkg] = InstallInfo(state, version, release, build, distro)
|
||||
|
||||
def clear_pending(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
assert info.state == 'ip'
|
||||
info.state = 'i'
|
||||
self.d[pkg] = info
|
||||
del self.dp[pkg]
|
||||
|
||||
def remove(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
info.state = 'r'
|
||||
self.d[pkg] = info
|
||||
|
||||
def purge(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.d.has_key(pkg):
|
||||
del self.d[pkg]
|
||||
|
||||
|
||||
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,91 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import bsddb.db as db
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class LockedDBShelf(shelve.DBShelf):
|
||||
"""A simple wrapper to implement locking for bsddb's dbshelf"""
|
||||
|
||||
def __init__(self, dbname, mode=0644,
|
||||
filetype=db.DB_HASH, dbenv=None):
|
||||
shelve.DBShelf.__init__(self, dbenv)
|
||||
filename = os.path.join( pisi.context.config.db_dir(), dbname + '.bdb')
|
||||
if 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 __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
|
||||
#print 'open', filename
|
||||
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
|
||||
pisi.util.check_dir(pisi.context.config.db_dir())
|
||||
if self.flags != db.DB_RDONLY:
|
||||
self.lock()
|
||||
return self.db.open(filename, dbname, 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. Try later!"))
|
||||
|
||||
|
||||
def close(self):
|
||||
if self.closed:
|
||||
return
|
||||
self.db.close()
|
||||
if self.flags != db.DB_RDONLY:
|
||||
self.unlock()
|
||||
self.closed = True
|
||||
|
||||
def unlock(self):
|
||||
self.lockfile.close()
|
||||
os.unlink(self.filename + '.lock')
|
||||
@@ -1,97 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
|
||||
"""
|
||||
Metadata module provides access to metadata.xml. metadata.xml is
|
||||
generated during the build process of a package and used in the
|
||||
installation. Package repository also uses metadata.xml for building
|
||||
a package index.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
import pisi.pxml.xmlfile as xmlfile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.util as util
|
||||
|
||||
class Source:
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory]
|
||||
t_Homepage = [autoxml.String, autoxml.optional]
|
||||
t_Packager = [specfile.Packager, autoxml.mandatory]
|
||||
|
||||
class Package(specfile.Package):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
t_Build = [ autoxml.Integer, autoxml.optional]
|
||||
t_Distribution = [ autoxml.String, autoxml.mandatory]
|
||||
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
|
||||
t_Architecture = [ autoxml.String, autoxml.mandatory]
|
||||
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
|
||||
t_PackageURI = [ autoxml.String, autoxml.optional]
|
||||
|
||||
def decode_hook(self, node, errs, where):
|
||||
self.version = self.history[0].version
|
||||
self.release = self.history[0].release
|
||||
|
||||
def __str__(self):
|
||||
s = specfile.Package.__str__(self)
|
||||
s += _('Build: % s, Distribution: %s, Dist. Release: %s\n') % \
|
||||
(self.build, self.distribution, self.distributionRelease)
|
||||
s += _('Architecture: %s, Installed Size: %s\n') % \
|
||||
(self.architecture, self.installedSize)
|
||||
return s
|
||||
|
||||
class MetaData(xmlfile.XmlFile):
|
||||
"""Package metadata. Metadata is composed of Specfile and various
|
||||
other information. A metadata has two parts, Source and Package."""
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Source = [ Source, autoxml.mandatory]
|
||||
t_Package = [ Package, autoxml.mandatory]
|
||||
#t_History = [ [Update], autoxml.mandatory]
|
||||
|
||||
def from_spec(self, src, pkg):
|
||||
self.source.name = src.name
|
||||
self.source.homepage = src.homepage
|
||||
self.source.packager = src.packager
|
||||
self.package.name = pkg.name
|
||||
self.package.summary = pkg.summary
|
||||
self.package.description = pkg.description
|
||||
self.package.icon = pkg.icon
|
||||
self.package.isA = pkg.isA
|
||||
self.package.partOf = pkg.partOf
|
||||
self.package.license = pkg.license
|
||||
self.package.runtimeDependencies = pkg.runtimeDependencies
|
||||
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,422 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"Package Operations: install/remove/upgrade"
|
||||
|
||||
import os
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
import pisi.atomicoperations as atomicoperations
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# high level operations
|
||||
def install(packages):
|
||||
"""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('.pisi'): # they all have to!
|
||||
return install_pkg_files(packages)
|
||||
else:
|
||||
return install_pkg_names(packages)
|
||||
|
||||
def install_pkg_files(package_URIs):
|
||||
"""install a number of pisi package files"""
|
||||
from package import Package
|
||||
|
||||
ctx.ui.debug('A = %s' % str(package_URIs))
|
||||
|
||||
for x in package_URIs:
|
||||
if not x.endswith(ctx.const.package_suffix):
|
||||
raise Error(_('Mixing file names and package names not supported yet.'))
|
||||
|
||||
if ctx.config.get_option('ignore_dependency'):
|
||||
# simple code path then
|
||||
for x in package_URIs:
|
||||
atomicoperations.install_single_file(x)
|
||||
return # short circuit
|
||||
|
||||
# read the package information into memory first
|
||||
# regardless of which distribution they come from
|
||||
d_t = {}
|
||||
dfn = {}
|
||||
for x in package_URIs:
|
||||
package = Package(x)
|
||||
package.read()
|
||||
name = str(package.metadata.package.name)
|
||||
d_t[name] = package.metadata.package
|
||||
dfn[name] = x
|
||||
|
||||
def satisfiesDep(dep):
|
||||
# is dependency satisfied among available packages
|
||||
# or packages to be installed?
|
||||
return dependency.installed_satisfies_dep(dep) \
|
||||
or dependency.dict_satisfies_dep(d_t, dep)
|
||||
|
||||
# for this case, we have to determine the dependencies
|
||||
# that aren't already satisfied and try to install them
|
||||
# from the repository
|
||||
dep_unsatis = []
|
||||
for name in d_t.keys():
|
||||
pkg = d_t[name]
|
||||
deps = pkg.runtimeDependencies
|
||||
for dep in deps:
|
||||
if not satisfiesDep(dep):
|
||||
dep_unsatis.append(dep)
|
||||
|
||||
# now determine if these unsatisfied dependencies could
|
||||
# be satisfied by installing packages from the repo
|
||||
|
||||
# if so, then invoke install_pkg_names
|
||||
extra_packages = [x.package for x in dep_unsatis]
|
||||
if extra_packages:
|
||||
ctx.ui.info(_("""The following packages will be installed
|
||||
in the respective order to satisfy extra dependencies:
|
||||
""") + util.strlist(extra_packages))
|
||||
if not ctx.ui.confirm(_('Do you want to continue?')):
|
||||
raise Error(_('External dependencies not satisfied'))
|
||||
install_pkg_names(extra_packages)
|
||||
|
||||
class PackageDB:
|
||||
def get_package(self, key):
|
||||
return d_t[str(key)]
|
||||
|
||||
packagedb = PackageDB()
|
||||
|
||||
A = d_t.keys()
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to install.'))
|
||||
return
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies:
|
||||
if dependency.dict_satisfies_dep(d_t, dep):
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
|
||||
for x in order:
|
||||
atomicoperations.install_single_file(dfn[x])
|
||||
|
||||
def check_conflicts(order):
|
||||
"""check if upgrading to the latest versions will cause havoc
|
||||
done in a simple minded way without regard for dependencies of
|
||||
conflicts, etc."""
|
||||
B_0 = B = set(order)
|
||||
C = set()
|
||||
# calculate conflict closure
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x) # get latest version!
|
||||
#TODO: read conflicts from a conflicts db...
|
||||
for conflict in pkg.conflicts:
|
||||
if ctx.installdb.is_installed(self.pkginfo):
|
||||
Bp.add(conflict)
|
||||
C.add(conflict)
|
||||
B = Bp
|
||||
if B_0.intersection(C):
|
||||
raise Error(_("Selected packages %s and %s are in conflict.") % (x, pkg))
|
||||
if C:
|
||||
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
|
||||
#raise Error(_("Package %s conflicts installed package %s") % (x, pkg))
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
def install_pkg_names(A):
|
||||
"""This is the real thing. It installs packages from
|
||||
the repository, trying to perform a minimum number of
|
||||
installs"""
|
||||
|
||||
A_0 = A = set(A) # A was a list, remove duplicates
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to install.'))
|
||||
return
|
||||
|
||||
if ctx.config.get_option('ignore_dependency'):
|
||||
# simple code path then
|
||||
for x in A:
|
||||
atomicoperations.install_single_name(x)
|
||||
return # short circuit
|
||||
|
||||
|
||||
# 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)
|
||||
ctx.ui.info(_("""The following minimal list of packages will be installed
|
||||
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?')):
|
||||
return False
|
||||
for x in order:
|
||||
atomicoperations.install_single_name(x)
|
||||
|
||||
def upgrade(A):
|
||||
upgrade_pkg_names(A)
|
||||
|
||||
def upgrade_pkg_names(A = []):
|
||||
"""Re-installs packages from the repository, trying to perform
|
||||
a maximum number of upgrades."""
|
||||
|
||||
ignore_build = ctx.config.options and ctx.config.options.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 = set(A)
|
||||
Ap = []
|
||||
for x in A:
|
||||
if x.endswith('.pisi'):
|
||||
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
|
||||
if not ctx.installdb.is_installed(x):
|
||||
ctx.ui.info(_('Package %s is not installed.') % x)
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
pkg = packagedb.get_package(x)
|
||||
|
||||
# First check version. If they are same, check release. Again
|
||||
# if releases are same and checking buildno is premitted,
|
||||
# check build number.
|
||||
if version < pkg.version:
|
||||
Ap.append(x)
|
||||
elif version == pkg.version:
|
||||
if release < pkg.release:
|
||||
Ap.append(x)
|
||||
if release == pkg.release and build and not ignore_build:
|
||||
if build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
|
||||
ctx.ui.info(_('Package %s is already at its latest \
|
||||
version %s, release %s, build %s.')
|
||||
% (x, pkg.version, pkg.release, pkg.build))
|
||||
A = set(Ap)
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to upgrade.'))
|
||||
return True
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
|
||||
def upgradable(dep):
|
||||
#pre dep.package is installed
|
||||
(v,r,b) = ctx.installdb.get_version(dep.package)
|
||||
rep_pkg = packagedb.get_package(dep.package)
|
||||
(vp,rp,bp) = (rep_pkg.version, rep_pkg.release,
|
||||
rep_pkg.build)
|
||||
if ignore_build or (not b) or (not bp):
|
||||
# if we can't look at build
|
||||
if r >= rp: # installed already new
|
||||
return False
|
||||
elif b and bp and b >= bp:
|
||||
return False
|
||||
return True
|
||||
|
||||
# TODO: conflicts
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies:
|
||||
# add packages that can be upgraded
|
||||
if dependency.repo_satisfies_dep(dep):
|
||||
#TODO: distinguish must upgrade and upgradable
|
||||
if ctx.installdb.is_installed(dep.package):
|
||||
if not ctx.get_option('eager'):
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
else:
|
||||
if not upgradable(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
else:
|
||||
raise Error(_("Reverse dependency %s cannot be satisfied") % rev_dep)
|
||||
B = Bp
|
||||
# now, search reverse dependencies to see if anything
|
||||
# should be upgraded
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
rev_deps = packagedb.get_rev_deps(x)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
if not ctx.get_option('eager'):
|
||||
# add unsatisfied reverse dependencies
|
||||
if packagedb.has_package(rev_dep) and \
|
||||
(not dependency.installed_satisfies_dep(depinfo)):
|
||||
if not dependency.repo_satisfies_dep(dep):
|
||||
raise Error(_("Reverse dependency %s cannot be satisfied") % rev_dep)
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
else:
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
B = Bp
|
||||
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
check_conflicts(order)
|
||||
ctx.ui.info(_("""The following packages will be upgraded:\n""") +
|
||||
util.strlist(order))
|
||||
if len(order) > len(A_0):
|
||||
if not ctx.ui.confirm('Do you want to continue?'):
|
||||
return False
|
||||
for x in order:
|
||||
atomicoperations.install_single_name(x, True)
|
||||
|
||||
def remove(A):
|
||||
"""remove set A of packages from system (A is a list of package names)"""
|
||||
|
||||
# filter packages that are not installed
|
||||
A_0 = A = set(A)
|
||||
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
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the (install closure) graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
rev_deps = packagedb.get_rev_deps(x)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
# we don't deal with unsatisfied dependencies
|
||||
if packagedb.has_package(rev_dep) and \
|
||||
dependency.installed_satisfies_dep(depinfo):
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
B = Bp
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
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
|
||||
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)
|
||||
@@ -1,121 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package abstraction
|
||||
# provides methods to add/remove files, extract control files
|
||||
|
||||
# maintainer: baris and meren
|
||||
|
||||
from os.path import join, exists
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.archive as archive
|
||||
from pisi.uri import URI
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Package:
|
||||
"""PISI Package Class provides access to a pisi package (.pisi
|
||||
file)."""
|
||||
def __init__(self, packagefn, mode='r'):
|
||||
self.filepath = packagefn
|
||||
url = URI(packagefn)
|
||||
|
||||
if url.is_remote_file():
|
||||
from fetcher import fetch_url
|
||||
dest = ctx.config.packages_dir()
|
||||
self.filepath = join(dest, url.filename())
|
||||
|
||||
# FIXME: exists is not enough, also sha1sum check needed \
|
||||
# when implemented in pisi-index.xml
|
||||
if not exists(self.filepath):
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
else:
|
||||
ctx.ui.info(_('%s [cached]') % url.filename())
|
||||
|
||||
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
|
||||
|
||||
def add_to_package(self, fn):
|
||||
"""Add a file or directory to package"""
|
||||
self.impl.add_to_archive(fn)
|
||||
|
||||
def close(self):
|
||||
"""Close the package archive"""
|
||||
self.impl.close()
|
||||
|
||||
def extract(self, outdir):
|
||||
"""Extract entire package contents to directory"""
|
||||
self.extract_dir('', outdir) # means package root
|
||||
|
||||
def extract_files(self, paths, outdir):
|
||||
"""Extract paths to outdir"""
|
||||
self.impl.unpack_files(paths, outdir)
|
||||
|
||||
def extract_file(self, path, outdir):
|
||||
"""Extract file with path to outdir"""
|
||||
self.extract_files([path], outdir)
|
||||
|
||||
def extract_dir(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
copies the directory archiveroot/dir to outdir"""
|
||||
self.impl.unpack_dir(dir, outdir)
|
||||
|
||||
def extract_dir_flat(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
unpacks the *contents* of directory archiveroot/dir inside outdir
|
||||
this is the function used by the installer"""
|
||||
self.impl.unpack_dir_flat(dir, outdir)
|
||||
|
||||
def extract_PISI_files(self, outdir):
|
||||
"""Extract PISI control files: metadata.xml, files.xml,
|
||||
action scripts, etc."""
|
||||
self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
|
||||
self.extract_dir('config', outdir)
|
||||
|
||||
def read(self, outdir = None):
|
||||
if not outdir:
|
||||
outdir = ctx.config.tmp_dir()
|
||||
|
||||
# extract control files
|
||||
self.extract_PISI_files(outdir)
|
||||
|
||||
self.metadata = MetaData()
|
||||
self.metadata.read( join(outdir, ctx.const.metadata_xml) )
|
||||
errs = self.metadata.errors()
|
||||
if errs:
|
||||
util.Checks.print_errors(errs)
|
||||
raise Error, _("MetaData format wrong")
|
||||
|
||||
self.files = Files()
|
||||
self.files.read( join(outdir, ctx.const.files_xml) )
|
||||
if self.files.errors():
|
||||
raise Error, _("Invalid %s") % ctx.const.files_xml
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.metadata.package.name + '-' \
|
||||
+ self.metadata.package.version + '-' \
|
||||
+ self.metadata.package.release
|
||||
|
||||
return join( ctx.config.lib_dir(), packageDir)
|
||||
|
||||
def comar_dir(self):
|
||||
return join(self.pkg_dir(), ctx.const.comar_dir)
|
||||
@@ -1,168 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package database
|
||||
# interface for update/query to local package repository
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
# we basically store everything in PackageInfo class
|
||||
# yes, we are cheap
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import os, fcntl
|
||||
from bsddb import db
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class PackageDB(object):
|
||||
"""PackageDB class provides an interface to the package database with
|
||||
a delegated dbshelve object"""
|
||||
def __init__(self, id):
|
||||
self.d = shelve.LockedDBShelf('package-%s' % id )
|
||||
self.dr = shelve.LockedDBShelf('revdep-%s' % id )
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dr.close()
|
||||
|
||||
def has_package(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key(name)
|
||||
|
||||
def get_package(self, name):
|
||||
name = str(name)
|
||||
return self.d[name]
|
||||
|
||||
def get_rev_deps(self, name):
|
||||
name = str(name)
|
||||
if self.dr.has_key(name):
|
||||
return self.dr[name]
|
||||
else:
|
||||
return []
|
||||
|
||||
def list_packages(self):
|
||||
list = []
|
||||
for (pkg, x) in self.d.iteritems():
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
#TODO: list_upgrades?
|
||||
|
||||
def add_package(self, package_info):
|
||||
name = str(package_info.name)
|
||||
self.d[name] = package_info
|
||||
for dep in package_info.runtimeDependencies:
|
||||
dep_name = str(dep.package)
|
||||
if self.dr.has_key(dep_name):
|
||||
self.dr[dep_name].append( (name, dep) )
|
||||
else:
|
||||
self.dr[dep_name] = [ (name, dep) ]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_package(self, name):
|
||||
name = str(name)
|
||||
del self.d[name]
|
||||
|
||||
|
||||
packagedbs = {}
|
||||
|
||||
def add_db(name):
|
||||
pisi.packagedb.packagedbs[name] = PackageDB('repo-' + name)
|
||||
|
||||
def get_db(name):
|
||||
return pisi.packagedb.packagedbs[name]
|
||||
|
||||
def remove_db(name):
|
||||
del pisi.packagedb.packagedbs[name]
|
||||
#FIXME: erase database file?
|
||||
|
||||
def has_package(name):
|
||||
repo = which_repo(name)
|
||||
if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def which_repo(name):
|
||||
import pisi.repodb
|
||||
for repo in pisi.repodb.db.list():
|
||||
if get_db(repo).has_package(name):
|
||||
return repo
|
||||
return None
|
||||
|
||||
def get_package(name):
|
||||
repo = which_repo(name)
|
||||
if repo:
|
||||
return get_db(repo).get_package(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
return thirdparty_packagedb.get_package(name)
|
||||
if inst_packagedb.has_package(name):
|
||||
return inst_packagedb.get_package(name)
|
||||
raise Error(_('get_package: package %s not found') % name)
|
||||
|
||||
def get_rev_deps(name):
|
||||
repo = which_repo(name)
|
||||
if repo:
|
||||
return get_db(repo).get_rev_deps(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
return thirdparty_packagedb.get_rev_deps(name)
|
||||
if inst_packagedb.has_package(name):
|
||||
return inst_packagedb.get_rev_deps(name)
|
||||
|
||||
return None
|
||||
|
||||
def remove_package(name):
|
||||
# remove the guy from the tracking databases
|
||||
inst_packagedb.remove_package(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
thirdparty_packagedb.remove_package(name)
|
||||
|
||||
# tracking databases for non-repository information
|
||||
|
||||
thirdparty_packagedb = inst_packagedb = None
|
||||
|
||||
def init_db():
|
||||
global thirdparty_packagedb
|
||||
global inst_packagedb
|
||||
|
||||
if not thirdparty_packagedb:
|
||||
thirdparty_packagedb = PackageDB('thirdparty')
|
||||
if not pisi.packagedb.inst_packagedb:
|
||||
inst_packagedb = PackageDB('installed')
|
||||
|
||||
def finalize_db():
|
||||
global thirdparty_packagedb
|
||||
global inst_packagedb
|
||||
global packagedbs
|
||||
|
||||
if thirdparty_packagedb:
|
||||
thirdparty_packagedb.close()
|
||||
thirdparty_packagedb = None
|
||||
|
||||
if inst_packagedb:
|
||||
inst_packagedb.close()
|
||||
inst_packagedb = None
|
||||
|
||||
packagedbs.clear()
|
||||
@@ -1,81 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI package relation graph that represents the state of packagedb
|
||||
|
||||
from graph import *
|
||||
|
||||
# Cache the results from packagedb queries in a graph
|
||||
|
||||
class PGraph(Digraph):
|
||||
|
||||
def __init__(self, packagedb):
|
||||
super(PGraph, self).__init__()
|
||||
self.packagedb = packagedb
|
||||
|
||||
def add_package(self, pkg):
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
self.add_vertex(str(pkg), (pkg1.version, pkg1.release))
|
||||
|
||||
def add_plain_dep(self, pkg1name, pkg2name):
|
||||
pkg1data = None
|
||||
if not pkg1name in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg1name)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg2name in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg2name)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_dep(self, pkg, depinfo):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(depinfo.package)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_rev_dep(self, depinfo, pkg):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(depinfo.package)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_conflict(self, pkg, conflinfo):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(conflinfo.package)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
|
||||
self.add_biedge(str(pkg), str(conflinfo.package), ('c', conflinfo)
|
||||
, pkg1data, pkg2data )
|
||||
|
||||
def write_graphviz_vlabel(self, f, u):
|
||||
(v, r) = self.vertex_data(u)
|
||||
f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]')
|
||||
|
||||
@@ -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,795 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
|
||||
"""
|
||||
autoxml is a metaclass for automatic XML translation, using
|
||||
a miniature type system. (w00t!) This is based on an excellent
|
||||
high-level XML processing prototype that Gurer prepared.
|
||||
|
||||
Method names are mixedCase for compatibility with minidom,
|
||||
an old library.
|
||||
"""
|
||||
|
||||
# System
|
||||
import locale
|
||||
import codecs
|
||||
import types
|
||||
import formatter
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
#import xml.dom.minidom as mdom
|
||||
#from xml.parsers.expat import ExpatError
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
# TODO: find the appropriate xmlext implementation and import from there
|
||||
from pisi.pxml.xmlext import *
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.context as ctx
|
||||
import pisi.util as util
|
||||
import pisi.oo as oo
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# requirement specs
|
||||
|
||||
mandatory, optional = range(2) # poor man's enum
|
||||
|
||||
# basic types
|
||||
|
||||
String = types.StringType
|
||||
Text = types.UnicodeType
|
||||
Integer = types.IntType
|
||||
Long = types.LongType
|
||||
Float = types.FloatType
|
||||
|
||||
#class datatype(type):
|
||||
# def __init__(cls, name, bases, dict):
|
||||
# """entry point for metaclass code"""
|
||||
# # standard initialization
|
||||
# super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
class LocalText(dict):
|
||||
"""Handles XML tags with localized text"""
|
||||
|
||||
def __init__(self, tag = "", req = optional):
|
||||
self.tag = tag
|
||||
self.req = req
|
||||
dict.__init__(self)
|
||||
#self.locs = {}
|
||||
|
||||
def decode(self, node, errs, where = ""):
|
||||
# flags, tag name, instance attribute
|
||||
assert self.tag != ''
|
||||
nodes = getAllNodes(node, self.tag)
|
||||
if not nodes:
|
||||
if self.req == mandatory:
|
||||
errs.append(where + _(" At least one '%s' tag should have local text") %
|
||||
self.tag )
|
||||
else:
|
||||
for node in nodes:
|
||||
#lang = getNodeAttribute(node, "xml:lang")
|
||||
lang = node.getAttributeNS(XML_NS, 'lang')
|
||||
c = getNodeText(node)
|
||||
if not c:
|
||||
errs.append(where + _("'%s' language of tag '%s' is empty") %
|
||||
(lang, self.tag))
|
||||
# FIXME: check for dups and 'en'
|
||||
if not lang:
|
||||
lang = 'en'
|
||||
self[lang] = c
|
||||
|
||||
def encode(self, node, errs):
|
||||
assert self.tag != ''
|
||||
for key in self.iterkeys():
|
||||
newnode = newNode(node, self.tag)
|
||||
newnode.setAttributeNS(XML_NS, 'xml:lang', key)
|
||||
newtext = newTextNode(node, self[key])
|
||||
newnode.appendChild(newtext)
|
||||
node.appendChild(newnode)
|
||||
|
||||
def errors(self, where = unicode()):
|
||||
errs = []
|
||||
langs = [ locale.getlocale()[0][0:2], 'tr', 'en' ]
|
||||
if not util.any(lambda x : self.has_key(x), langs):
|
||||
errs.append( where + _("Tag should have at least an English or Turkish version"))
|
||||
#FIXME: check if all entries are unicode
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
L = locale.getlocale()[0][0:2] # try to read language, pathetic isn't it?
|
||||
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 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 = locale.getlocale()[0][0:2] # try to read language, pathetic isn't it?
|
||||
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 ''
|
||||
#ctx.ui.warning(_("Tag should have at least an English or Turkish version"))
|
||||
|
||||
class Writer(formatter.DumbWriter):
|
||||
"""adds unicode support"""
|
||||
|
||||
def __init__(self, file=None, maxcol=78):
|
||||
formatter.DumbWriter.__init__(self, file, maxcol)
|
||||
|
||||
def send_literal_data(self, data):
|
||||
self.file.write(data.encode("utf-8"))
|
||||
i = data.rfind('\n')
|
||||
if i >= 0:
|
||||
self.col = 0
|
||||
data = data[i+1:]
|
||||
data = data.expandtabs()
|
||||
self.col = self.col + len(data)
|
||||
self.atbreak = 0
|
||||
|
||||
|
||||
class autoxml(oo.autosuper, oo.autoprop):
|
||||
"""High-level automatic XML transformation interface for xmlfile.
|
||||
The idea is to declare a class for each XML tag. Inside the
|
||||
class the tags and attributes nested in the tag are further
|
||||
elaborated. A simple example follows:
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
|
||||
This class defines a tag and an attribute nested in Employee
|
||||
class. Name is a string and type is an integer, called basic
|
||||
types.
|
||||
While the tag is mandatory, the attribute may be left out.
|
||||
|
||||
Other basic types supported are: xmlfile.Float, xmlfile.Double
|
||||
and (not implemented yet): xmlfile.Binary
|
||||
|
||||
By default, the class name is taken as the corresponding tag,
|
||||
which may be overridden by defining a tag attribute. Thus,
|
||||
the same tag may also be written as:
|
||||
|
||||
class EmployeeXML:
|
||||
...
|
||||
tag = 'Employee'
|
||||
...
|
||||
|
||||
In addition to basic types, we allow for two kinds of complex
|
||||
types: class types and list types.
|
||||
|
||||
A declared class can be nested in another class as follows
|
||||
|
||||
class Position:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.Text, xmlfile.optional]
|
||||
|
||||
which we can add to our Employee class.
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
t_Position = [Position, xmlfile.mandatory]
|
||||
|
||||
Note some unfortunate redundancy here with Position; this is
|
||||
justified by the implementation (kidding). Still, you might
|
||||
want to assign a different name than the class name that
|
||||
goes in there, which may be fully qualified.
|
||||
|
||||
There is more! Suppose we want to define a company, with
|
||||
of course many employees.
|
||||
|
||||
class Company:
|
||||
__metaclass__ = autoxml
|
||||
t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee']
|
||||
|
||||
Logically, inside the Company/Employees tag, we will have several
|
||||
Employee tags, which are inserted to the Employees instance variable of
|
||||
Company in order of appearance. We can define lists of any other valid
|
||||
type. Here we used a list of an autoxml class defined above.
|
||||
|
||||
The mandatory flag here asserts that at least one such record
|
||||
is to be found.
|
||||
|
||||
You see, it works like magic, when it works of course. All of it
|
||||
done without a single brain exploding.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(cls, name, bases, dict):
|
||||
"""entry point for metaclass code"""
|
||||
#print 'generating class', name
|
||||
|
||||
# standard initialization
|
||||
super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
xmlfile_support = XmlFile in bases
|
||||
|
||||
cls.autoxml_bases = filter(lambda base: isinstance(base, autoxml), bases)
|
||||
|
||||
#TODO: initialize class attribute __xml_tags
|
||||
#setattr(cls, 'xml_variables', [])
|
||||
|
||||
# default class tag is class name
|
||||
if not dict.has_key('tag'):
|
||||
cls.tag = name
|
||||
|
||||
# generate helper routines, for each XML component
|
||||
names = []
|
||||
inits = []
|
||||
decoders = []
|
||||
encoders = []
|
||||
errorss = []
|
||||
formatters = []
|
||||
order = dict.keys()
|
||||
order.sort()
|
||||
#TODO: there should be at most one str member, and it should be the first
|
||||
for var in order:
|
||||
if var.startswith('t_') or var.startswith('a_') or var.startswith('s_'):
|
||||
name = var[2:]
|
||||
if var.startswith('a_'):
|
||||
x = autoxml.gen_attr_member(cls, name)
|
||||
elif var.startswith('t_'):
|
||||
x = autoxml.gen_tag_member(cls, name)
|
||||
elif var.startswith('s_'):
|
||||
x = autoxml.gen_str_member(cls, name)
|
||||
(name, init, decoder, encoder, errors, format_x) = x
|
||||
names.append(name)
|
||||
inits.append(init)
|
||||
decoders.append(decoder)
|
||||
encoders.append(encoder)
|
||||
errorss.append(errors)
|
||||
formatters.append(format_x)
|
||||
|
||||
# generate top-level helper functions
|
||||
cls.initializers = inits
|
||||
def initialize(self, **args):
|
||||
if not args.has_key('tag'):
|
||||
tag = cls.tag
|
||||
if xmlfile_support:
|
||||
XmlFile.__init__(self, tag = 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)
|
||||
|
||||
cls.__init__ = initialize
|
||||
|
||||
cls.decoders = decoders
|
||||
def decode(self, node, errs, where = unicode()):
|
||||
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()):
|
||||
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
|
||||
|
||||
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, filename, keepDoc = False):
|
||||
"read XML file and decode it into a python object"
|
||||
self.readxml(filename)
|
||||
errs = []
|
||||
self.decode(self.rootNode(), errs)
|
||||
if hasattr(self, 'read_hook'):
|
||||
self.read_hook(errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % filename)
|
||||
raise Error(*errs)
|
||||
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % filename)
|
||||
raise Error(*errs)
|
||||
|
||||
def write(self, filename, keepDoc = False):
|
||||
"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") % filename)
|
||||
raise Error(*errs)
|
||||
self.writexml(filename)
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
cls.read = read
|
||||
cls.write = write
|
||||
|
||||
|
||||
def gen_attr_member(cls, attr):
|
||||
"""generate readers and writers for an attribute member"""
|
||||
#print 'attr:', attr
|
||||
spec = getattr(cls, 'a_' + attr)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, attr):
|
||||
return getNodeAttribute(node, attr)
|
||||
def writetext(node, attr, text):
|
||||
#print 'write attr', attr, text
|
||||
node.setAttributeNS(XML_NS, attr, text)
|
||||
anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(attr, spec, anonfuns)
|
||||
|
||||
def gen_tag_member(cls, tag):
|
||||
"""generate helper funs for tag member of class"""
|
||||
#print 'tag:', tag
|
||||
spec = getattr(cls, 't_' + tag)
|
||||
anonfuns = cls.gen_tag(tag, spec)
|
||||
return cls.gen_named_comp(tag, spec, anonfuns)
|
||||
|
||||
def gen_tag(cls, tag, spec):
|
||||
"""generate readers and writers for the tag"""
|
||||
tag_type = spec[0]
|
||||
if type(tag_type) is types.TypeType and \
|
||||
autoxml.basic_cons_map.has_key(tag_type):
|
||||
def readtext(node, tagpath):
|
||||
#print 'read tag', node, tagpath
|
||||
return getNodeText(node, tagpath)
|
||||
def writetext(node, tagpath, text):
|
||||
#print 'write tag', node, tagpath, text
|
||||
addText(node, tagpath, text)
|
||||
return cls.gen_anon_basic(tag, spec, readtext, writetext)
|
||||
elif type(tag_type) is types.ListType:
|
||||
return cls.gen_list_tag(tag, spec)
|
||||
elif tag_type is LocalText:
|
||||
return cls.gen_insetclass_tag(tag, spec)
|
||||
elif type(tag_type) is autoxml or type(tag_type) is types.TypeType:
|
||||
return cls.gen_class_tag(tag, spec)
|
||||
else:
|
||||
raise Error(_('gen_tag: unrecognized tag type %s in spec') %
|
||||
str(tag_type))
|
||||
|
||||
def gen_str_member(cls, token):
|
||||
"""generate readers and writers for a string member"""
|
||||
spec = getattr(cls, 's_' + token)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, blah):
|
||||
#node.normalize() # piksemel doesn't have this
|
||||
return getNodeText(node)
|
||||
def writetext(node, blah, text):
|
||||
addText(node, "", text)
|
||||
anonfuns = cls.gen_anon_basic(token, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(token, spec, anonfuns)
|
||||
|
||||
def gen_named_comp(cls, token, spec, anonfuns):
|
||||
"""generate a named component tag/attr. a decoration of
|
||||
anonymous functions that do not bind to variable names"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
(init_a, decode_a, encode_a, errors_a, format_a) = anonfuns
|
||||
|
||||
def init(self):
|
||||
"""initialize component"""
|
||||
setattr(self, name, init_a())
|
||||
|
||||
def decode(self, node, errs, where):
|
||||
"""decode component from DOM node"""
|
||||
setattr(self, name, decode_a(node, errs, where + unicode(name) + " "))
|
||||
|
||||
def encode(self, node, errs):
|
||||
"""encode self inside, possibly new, DOM node using xml"""
|
||||
if hasattr(self, name):
|
||||
value = getattr(self, name)
|
||||
else:
|
||||
value = None
|
||||
encode_a(node, value, errs)
|
||||
|
||||
def errors(self, where):
|
||||
errs = []
|
||||
if hasattr(self, name):
|
||||
value = getattr(self,name)
|
||||
errs.extend(errors_a(value, where + name + ': ' ))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + _('Mandatory variable %s not available') % name)
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
if hasattr(self, name):
|
||||
value = getattr(self,name)
|
||||
f.add_literal_data(token + ': ')
|
||||
format_a(value, f, errs)
|
||||
f.add_line_break()
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory variable %s not available') % name)
|
||||
|
||||
return (name, init, decode, encode, errors, format)
|
||||
|
||||
def mixed_case(cls, identifier):
|
||||
"""helper function to turn token name into mixed case"""
|
||||
if identifier is "":
|
||||
return ""
|
||||
else:
|
||||
if identifier[0]=='I':
|
||||
lowly = 'i' # because of pythonic idiots we can't choose locale in lower
|
||||
else:
|
||||
lowly = identifier[0].lower()
|
||||
return lowly + identifier[1:]
|
||||
|
||||
def tagpath_head_last(cls, tagpath):
|
||||
"returns split of the tag path into last tag and the rest"
|
||||
try:
|
||||
lastsep = tagpath.rindex('/')
|
||||
except ValueError, e:
|
||||
return ('', tagpath)
|
||||
return (tagpath[:lastsep], tagpath[lastsep+1:])
|
||||
|
||||
def parse_spec(cls, token, spec):
|
||||
"""decompose member specification"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
|
||||
if len(spec)>=3:
|
||||
path = spec[2] # an alternative path specified
|
||||
elif type(token_type) is type([]):
|
||||
if type(token_type[0]) is autoxml:
|
||||
# if list of class, by default nested like in most PSPEC
|
||||
path = token + '/' + token_type[0].tag
|
||||
else:
|
||||
# if list of ordinary type, just take the name for
|
||||
path = token
|
||||
elif type(token_type) is autoxml:
|
||||
# if a class, by default its tag
|
||||
path = token_type.tag
|
||||
else:
|
||||
path = token # otherwise it's the same name as
|
||||
# the token
|
||||
return name, token_type, req, path
|
||||
|
||||
def gen_anon_basic(cls, token, spec, readtext, writetext):
|
||||
"""Generate a tag or attribute with one of the basic
|
||||
types like integer. This has got to be pretty generic
|
||||
so that we can invoke it from the complex types such as Class
|
||||
and List. The readtext and writetext arguments achieve
|
||||
the DOM text access for this datatype."""
|
||||
|
||||
name, token_type, req, tagpath = cls.parse_spec(token, spec)
|
||||
|
||||
def initialize():
|
||||
"""default value for all basic types is None"""
|
||||
return None
|
||||
|
||||
def decode(node, errs, where):
|
||||
"""decode from DOM node, the value, watching the spec"""
|
||||
text = readtext(node, token)
|
||||
#print 'read text ', text
|
||||
if text:
|
||||
try:
|
||||
value = autoxml.basic_cons_map[token_type](text)
|
||||
except:
|
||||
value = None
|
||||
errs.append(where + _('Type mismatch: read text cannot be decoded'))
|
||||
return value
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + _('Mandatory token %s not available') % token)
|
||||
return None
|
||||
|
||||
def encode(node, value, errs):
|
||||
"""encode given value inside DOM node"""
|
||||
if value:
|
||||
writetext(node, token, unicode(value))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory token %s not available') % token)
|
||||
|
||||
def errors(value, where):
|
||||
errs = []
|
||||
if value and not isinstance(value, token_type):
|
||||
errs.append(where + _('Type mismatch. Expected %s, got %s') %
|
||||
(token_type, type(value)) )
|
||||
return errs
|
||||
|
||||
def format(value, f, errs):
|
||||
"""format value for pretty printing"""
|
||||
f.add_literal_data(unicode(value))
|
||||
|
||||
return initialize, decode, encode, errors, format
|
||||
|
||||
def gen_class_tag(cls, tag, spec):
|
||||
"""generate a class datatype"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
def make_object():
|
||||
obj = tag_type.__new__(tag_type)
|
||||
obj.__init__(tag=tag, req=req)
|
||||
return obj
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node, errs, where):
|
||||
node = getNode(node, tag)
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node, errs, where + unicode("Class %s :") % tag)
|
||||
return obj
|
||||
except Error:
|
||||
errs.append(where + _('Type mismatch: DOM cannot be decoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + _('Mandatory argument not available'))
|
||||
return None
|
||||
|
||||
def encode(node, obj, errs):
|
||||
if node and obj:
|
||||
try:
|
||||
#FIXME: this doesn't look pretty
|
||||
classnode = newNode(node, tag)
|
||||
obj.encode(classnode, errs)
|
||||
node.appendChild(classnode)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
# note: we can receive an error if obj has no content
|
||||
errs.append(_('Object cannot be encoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
def errors(obj, where):
|
||||
return obj.errors(where)
|
||||
|
||||
def format(obj, f, errs):
|
||||
try:
|
||||
obj.format(f, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
def gen_list_tag(cls, tag, spec):
|
||||
"""generate a list datatype. stores comps in tag/comp_tag"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
pathcomps = path.split('/')
|
||||
comp_tag = pathcomps.pop()
|
||||
list_tagpath = util.makepath(pathcomps, sep='/', relative=True)
|
||||
|
||||
if len(tag_type) != 1:
|
||||
raise Error(_('List type must contain only one element'))
|
||||
|
||||
x = cls.gen_tag(comp_tag, [tag_type[0], mandatory])
|
||||
(init_item, decode_item, encode_item, errors_item, format_item) = x
|
||||
|
||||
def init():
|
||||
return []
|
||||
|
||||
def decode(node, errs, where):
|
||||
l = []
|
||||
nodes = getAllNodes(node, path)
|
||||
#print node, tag + '/' + comp_tag, nodes
|
||||
if len(nodes)==0 and req==mandatory:
|
||||
errs.append(where + _('Mandatory list empty'))
|
||||
ix = 1
|
||||
for node in nodes:
|
||||
dummy = newNode(node, "Dummy")
|
||||
dummy.appendChild(node)
|
||||
l.append(decode_item(dummy, errs, where + unicode("[%s]" % ix)))
|
||||
#l.append(decode_item(node, errs, where + unicode("[%s]" % ix)))
|
||||
ix += 1
|
||||
return l
|
||||
|
||||
def encode(node, l, errs):
|
||||
dom = node.ownerDocument
|
||||
if l and len(l) > 0:
|
||||
for item in l:
|
||||
if list_tagpath:
|
||||
listnode = addNode(node, list_tagpath, branch = False)
|
||||
else:
|
||||
listnode = node
|
||||
encode_item(listnode, item, errs)
|
||||
#encode_item(node, item, errs)
|
||||
else:
|
||||
if req is mandatory:
|
||||
errs.append(_('Mandatory list empty'))
|
||||
|
||||
def errors(l, where):
|
||||
errs = []
|
||||
ix = 1
|
||||
for node in l:
|
||||
errs.extend(errors_item(node, where + '[%s]' % ix))
|
||||
ix += 1
|
||||
return errs
|
||||
|
||||
def format(l, f, errs):
|
||||
# TODO: indent here
|
||||
ix = 1
|
||||
length = len(l)
|
||||
for node in l:
|
||||
f.add_flowing_data(str(ix) + ': ')
|
||||
format_item(node, f, errs)
|
||||
if ix != length:
|
||||
f.add_flowing_data(', ')
|
||||
ix += 1
|
||||
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
def gen_insetclass_tag(cls, tag, spec):
|
||||
"""generate a class datatype that is highly integrated
|
||||
don't worry if that means nothing to you. this is a silly
|
||||
hack to implement local text quickly. it's not the most
|
||||
elegant thing in the world. it's basically a copy of
|
||||
class tag"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
def make_object():
|
||||
obj = tag_type.__new__(tag_type)
|
||||
obj.__init__(tag=tag, req=req)
|
||||
return obj
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node, errs, where):
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node, errs, where)
|
||||
return obj
|
||||
except Error:
|
||||
errs.append(where + _('Type mismatch: DOM cannot be decoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + _('Mandatory argument not available'))
|
||||
return None
|
||||
|
||||
def encode(node, obj, errs):
|
||||
if node and obj:
|
||||
try:
|
||||
#FIXME: this doesn't look pretty
|
||||
obj.encode(node, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
# note: we can receive an error if obj has no content
|
||||
errs.append(_('Object cannot be encoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
def errors(obj, where):
|
||||
return obj.errors(where)
|
||||
|
||||
def format(obj, f, errs):
|
||||
try:
|
||||
obj.format(f, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
basic_cons_map = {
|
||||
types.StringType : str,
|
||||
types.UnicodeType : unicode,
|
||||
types.IntType : int,
|
||||
types.FloatType : float,
|
||||
types.LongType : long
|
||||
}
|
||||
@@ -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@uludag.org.tr>
|
||||
|
||||
try:
|
||||
zimbabwe # comment out to disable piks
|
||||
from xmlextpiks import *
|
||||
except:
|
||||
try:
|
||||
from xmlextcdom import *
|
||||
except:
|
||||
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
|
||||
from xmlextmdom import *
|
||||
|
||||
|
||||
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
|
||||
XHTML_NS = unicode(XHTML_NAMESPACE)
|
||||
XML_NS = unicode(XML_NAMESPACE)
|
||||
@@ -1,203 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Faik Uygur <faikuygur@gmail.com>
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml import ParsePath
|
||||
from Ft.Xml.Domlette import NonvalidatingReader
|
||||
|
||||
import pisi
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
# create a document and return document root
|
||||
doc = implementation.createDocument(None, tag, None)
|
||||
return doc.documentElement
|
||||
|
||||
def parse(filename):
|
||||
# parse file and return document root
|
||||
try:
|
||||
doc = ParsePath(filename)
|
||||
return doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML: %s") % (filename, str(e)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if not node.hasAttributeNS(None, attrname):
|
||||
return None
|
||||
else:
|
||||
return node.getAttributeNS(None, attrname)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
#FIXME: better tag split
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElementNS(None, tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -1,198 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlext.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
import pisi
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
impl = mdom.getDOMImplementation()
|
||||
dom = impl.createDocument(None, tag, None)
|
||||
return dom.documentElement
|
||||
|
||||
def parse(filename):
|
||||
try:
|
||||
dom = mdom.parse(filename)
|
||||
return dom.documentElement
|
||||
except ExpatError, inst:
|
||||
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElement(tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if not node.hasAttribute(attrname):
|
||||
return None
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
#FIXME: better tag split
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElement(tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -1,175 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
|
||||
"""
|
||||
some helper functions for high-level access to XML structures
|
||||
|
||||
this implementation uses piksemel, a fast C-based XML library
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an old library
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
from piksemel import *
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return [x for x in parent.childNodes if x.type() == TAG ]
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent if x.type() == TAG and x.name() == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.firstChild()
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.type() == CDATA: #FIXME: ??? TEXT type?
|
||||
# in any case, strip whitespaces... :/ you don't like it?
|
||||
return child.data().strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.tags():
|
||||
if child.name() == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
#FIXME: better tag split
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
for tag in tags:
|
||||
node = node.appendTag(tag)
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendTag(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
node = addNode(node, tagPath, branch = branch)
|
||||
return node.appendData(text)
|
||||
|
||||
|
||||
# cannot be implemented with piksemel
|
||||
#def newNode(node, tag):
|
||||
# return Node(tag)
|
||||
|
||||
# cannot be implemented with piksemel :(
|
||||
#def newTextNode(node, text):
|
||||
# return Node(text)
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
try:
|
||||
horagata # comment out to disable piks
|
||||
#from xmlfilepiks import *
|
||||
except:
|
||||
try:
|
||||
from xmlfilecdom import *
|
||||
except:
|
||||
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
|
||||
from xmlfilemdom import *
|
||||
@@ -1,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@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Faik Uygur <faikuygur@gmail.com>
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import codecs
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml.Domlette import NonvalidatingReader
|
||||
from Ft.Xml import Parse, ParsePath
|
||||
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
|
||||
|
||||
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
|
||||
self.newDocument()
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = implementation.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
del self.doc
|
||||
#self.doc = None # why?
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc.documentElement
|
||||
|
||||
def readxml(self, path):
|
||||
try:
|
||||
self.doc = ParsePath(path)
|
||||
return self.doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML:") % (path, str(e)) )
|
||||
|
||||
def writexml(self, path):
|
||||
f = file(path, 'w')
|
||||
PrettyPrint(self.rootNode(), stream = f)
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.doc.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.doc.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.doc.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc.documentElement, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.doc.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,159 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlfile
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import codecs
|
||||
|
||||
import pisi
|
||||
|
||||
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
|
||||
self.newDocument()
|
||||
|
||||
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, fileName):
|
||||
try:
|
||||
self.doc = mdom.parse(fileName)
|
||||
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,141 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module
|
||||
|
||||
this implementation uses piksemel, a fast C-based XML library
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
from piksemel import *
|
||||
|
||||
class Error(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
self.newDocument()
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = newDocument(self.rootTag)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
pass # piksemel is garbage collected!
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc
|
||||
|
||||
def readxml(self, fileName):
|
||||
try:
|
||||
self.doc = parse(fileName)
|
||||
except ParseError, inst:
|
||||
raise XmlError(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def writexml(self, fileName):
|
||||
f = codecs.open(fileName,'w', "utf-8")
|
||||
f.write(self.doc.toPrettyString())
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
#def newNode(self, tag):
|
||||
# return self.dom.createElement(tag)
|
||||
|
||||
#def newTextNode(self, text):
|
||||
# return self.dom.createTextNode(text)
|
||||
|
||||
#def newAttribute(self, attr):
|
||||
# return self.dom.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return [x for x in parent.childNodes]
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
return getChildElts(self.getNode(tagpath))
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.dom.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
raise Error('NOT IMPLEMENTED')
|
||||
#node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
raise Error('NOT IMPLEMENTED')
|
||||
#node = self.addNode(tagPath, self.newTextNode(text))
|
||||
#return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
raise Error('NOT IMPLEMENTED')
|
||||
#return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -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
|
||||
|
||||
from bsddb import db
|
||||
import os, fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.uri import URI
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Repo:
|
||||
def __init__(self, indexuri):
|
||||
self.indexuri = indexuri
|
||||
|
||||
#class HttpRepo
|
||||
|
||||
#class FtpRepo
|
||||
|
||||
#class RemovableRepo
|
||||
|
||||
|
||||
class RepoDB(object):
|
||||
"""RepoDB maps repo ids to repository information"""
|
||||
|
||||
def __init__(self):
|
||||
self.d = shelve.LockedDBShelf("repo")
|
||||
if not self.d.has_key("order"):
|
||||
self.d["order"] = []
|
||||
|
||||
def init_dbs(self):
|
||||
# initialize package/source dbs
|
||||
for x in self.list():
|
||||
packagedb.add_db(x)
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def repo_name(self, ix):
|
||||
l = self.list()
|
||||
return l[ix]
|
||||
|
||||
def swap(self, x,y):
|
||||
l = self.d["order"]
|
||||
t = l[x]
|
||||
l[x] = l[y]
|
||||
l[y] = t
|
||||
self.d["order"] = l
|
||||
|
||||
def has_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key("repo-" + name)
|
||||
|
||||
def get_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d["repo-" + name]
|
||||
|
||||
def add_repo(self, name, repo_info):
|
||||
name = str(name)
|
||||
assert (isinstance(repo_info,Repo))
|
||||
if self.d.has_key("repo-" + name):
|
||||
raise Error(_('Repository %s already exists') % name)
|
||||
self.d["repo-" + name] = repo_info
|
||||
order = self.d["order"]
|
||||
order.append(name)
|
||||
self.d["order"] = order
|
||||
packagedb.add_db(name)
|
||||
|
||||
def list(self):
|
||||
return self.d["order"]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_repo(self, name):
|
||||
name = str(name)
|
||||
del self.d["repo-" + name]
|
||||
l = self.d["order"]
|
||||
l.remove(name)
|
||||
self.d["order"] = l
|
||||
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = RepoDB()
|
||||
db.init_dbs()
|
||||
return db
|
||||
|
||||
def finalize():
|
||||
global db
|
||||
|
||||
if db:
|
||||
db.close()
|
||||
db = None
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@uludag.org.tr>
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# python standard library
|
||||
|
||||
from os.path import join
|
||||
from os import access, R_OK
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.archive import Archive
|
||||
from pisi.uri import URI
|
||||
from pisi.fetcher import fetch_url
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class SourceArchive:
|
||||
"""source archive. this is a class responsible for fetching
|
||||
and unpacking a source archive"""
|
||||
def __init__(self, bctx):
|
||||
self.url = URI(bctx.spec.source.archive.uri)
|
||||
self.archiveFile = join(ctx.config.archives_dir(), self.url.filename())
|
||||
self.archive = bctx.spec.source.archive
|
||||
self.bctx = bctx
|
||||
|
||||
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.bctx.pkg_work_dir(), clean_dir)
|
||||
@@ -1,74 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"""
|
||||
package source database
|
||||
interface for update/query to local package repository
|
||||
we basically store everything in sourceinfo class
|
||||
yes, we are cheap
|
||||
"""
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import os
|
||||
import fcntl
|
||||
from bsddb import db
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
class SourceDB(object):
|
||||
|
||||
def __init__(self):
|
||||
self.d = shelve.LockedDBShelf('source')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def has_source(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key(name)
|
||||
|
||||
def get_source(self, name):
|
||||
name = str(name)
|
||||
return self.d[name]
|
||||
|
||||
def add_source(self, source_info):
|
||||
assert not source_info.errors()
|
||||
name = str(source_info.name)
|
||||
self.d[name] = source_info
|
||||
|
||||
def remove_source(self, name):
|
||||
name = str(name)
|
||||
del self.d[name]
|
||||
|
||||
sourcedb = None
|
||||
|
||||
def init():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
return sourcedb
|
||||
|
||||
sourcedb = SourceDB()
|
||||
return sourcedb
|
||||
|
||||
def finalize():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
sourcedb.close()
|
||||
sourcedb = None
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
from os.path import basename, dirname, join
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
class SourceFetcher(object):
|
||||
def __init__(self, url, authInfo=None):
|
||||
self.url = url
|
||||
if authInfo:
|
||||
self.url.set_auth_info(authInfo)
|
||||
self.location = dirname(self.url.uri)
|
||||
|
||||
pkgname = basename(dirname(self.url.path()))
|
||||
self.dest = join(ctx.config.tmp_dir(), pkgname)
|
||||
|
||||
def fetch_all(self):
|
||||
# fetch pspec file
|
||||
self.fetch()
|
||||
pspec = join(self.dest, self.url.filename())
|
||||
self.spec = SpecFile()
|
||||
self.spec.read(pspec)
|
||||
|
||||
self.fetch_actionsfile()
|
||||
self.fetch_patches()
|
||||
self.fetch_comarfiles()
|
||||
self.fetch_additionalFiles()
|
||||
|
||||
return pspec
|
||||
|
||||
def fetch_actionsfile(self):
|
||||
actionsuri = join(self.location, ctx.const.actions_file)
|
||||
self.url.uri = actionsuri
|
||||
self.fetch()
|
||||
|
||||
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.location,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.url.uri = patchuri
|
||||
target_dir = join(ctx.const.files_dir, dir_name)
|
||||
self.fetch(target_dir)
|
||||
|
||||
def fetch_comarfiles(self):
|
||||
spec = self.spec
|
||||
for package in spec.packages:
|
||||
for pcomar in package.providesComar:
|
||||
comaruri = join(self.location,
|
||||
ctx.const.comar_dir, pcomar.script)
|
||||
self.url.uri = comaruri
|
||||
self.fetch(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.location,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.url.uri = afileuri
|
||||
target_dir = join(ctx.const.files_dir, dir_name)
|
||||
self.fetch(target_dir)
|
||||
|
||||
def fetch(self, appendDest=""):
|
||||
from fetcher import fetch_url
|
||||
|
||||
ctx.ui.info(_("Fetching %s") % self.url.uri)
|
||||
dest = join(self.dest, appendDest)
|
||||
fetch_url(self.url, dest)
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
"""
|
||||
Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
|
||||
files are specification files for PISI source packages. This module
|
||||
provides read and write routines for PSPEC files.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# standard python modules
|
||||
from os.path import basename
|
||||
|
||||
# pisi modules
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.context as ctx
|
||||
from pisi.dependency import Dependency
|
||||
import pisi.dependency
|
||||
from pisi.util import Checks
|
||||
import pisi.util as util
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
class Packager:
|
||||
|
||||
t_Name = [autoxml.Text, autoxml.mandatory]
|
||||
t_Email = [autoxml.String, autoxml.mandatory]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s <%s>" % (self.name, self.email)
|
||||
return s
|
||||
|
||||
|
||||
class AdditionalFile:
|
||||
|
||||
s_Filename = [autoxml.String, autoxml.mandatory]
|
||||
a_target = [autoxml.String, autoxml.mandatory]
|
||||
a_permission = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s -> %s " % (self.filename, self.target)
|
||||
if self.permission:
|
||||
s += '(%s)' % self.permission
|
||||
return s
|
||||
|
||||
|
||||
class Patch:
|
||||
|
||||
s_Filename = [autoxml.String, autoxml.mandatory]
|
||||
a_compressionType = [autoxml.String, autoxml.optional]
|
||||
a_level = [autoxml.Integer, autoxml.optional]
|
||||
a_target = [autoxml.String, autoxml.optional]
|
||||
|
||||
#FIXME: what's the cleanest way to give a default value for reading level?
|
||||
#def decode_hook(self, node, errs, where):
|
||||
# if self.level == None:
|
||||
# self.level = 0
|
||||
|
||||
def __str__(self):
|
||||
s = self.filename
|
||||
if self.compressionType:
|
||||
s += ' (' + self.compressionType + ')'
|
||||
if self.level:
|
||||
s += ' level:' + self.level
|
||||
if self.target:
|
||||
s += ' target:' + self.target
|
||||
return s
|
||||
|
||||
|
||||
class Update:
|
||||
|
||||
a_release = [autoxml.String, autoxml.mandatory]
|
||||
t_Date = [autoxml.String, autoxml.mandatory]
|
||||
t_Version = [autoxml.String, autoxml.mandatory]
|
||||
t_Type = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.date
|
||||
s += ", ver=" + self.version
|
||||
s += ", rel=" + self.release
|
||||
if self.type:
|
||||
s += ", type=" + self.type
|
||||
return s
|
||||
|
||||
|
||||
class Path:
|
||||
|
||||
s_Path = [autoxml.String, autoxml.mandatory]
|
||||
a_fileType = [autoxml.String, autoxml.optional]
|
||||
|
||||
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: %, sha1sum: %s') % (self.uri, self.type, self.sha1sum)
|
||||
return s
|
||||
|
||||
|
||||
class Source:
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory]
|
||||
t_Homepage = [autoxml.String, autoxml.optional]
|
||||
t_Packager = [Packager, autoxml.mandatory]
|
||||
t_Summary = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_IsA = [ [autoxml.String], autoxml.mandatory]
|
||||
t_PartOf = [autoxml.String, autoxml.mandatory]
|
||||
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]
|
||||
|
||||
|
||||
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_RuntimeDependencies = [ [Dependency], autoxml.optional]
|
||||
t_Files = [ [Path], autoxml.optional]
|
||||
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
|
||||
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
|
||||
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
|
||||
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
|
||||
t_History = [ [Update], autoxml.optional]
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.name + '-' \
|
||||
+ self.version + '-' \
|
||||
+ self.release
|
||||
|
||||
return util.join_path( ctx.config.lib_dir(), 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]
|
||||
|
||||
#we're not doing this with the init hook right now
|
||||
#def init(self, tag = "PISI"):
|
||||
#ignore tag
|
||||
#autoxml.__init__(self, tag)
|
||||
|
||||
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,98 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# generic user interface
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# A. Murat Eren <meren@uludag.org.tr>
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
installed, upgraded, removed, installing, removing = range(5)
|
||||
|
||||
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 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,108 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Simplifies working with URLs, purl module provides common URL
|
||||
# parsing and processing
|
||||
|
||||
from urlparse import urlparse
|
||||
from os.path import basename
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
class URI(object):
|
||||
"""URI class provides a URL parser and simplifies working with
|
||||
URLs."""
|
||||
|
||||
def __init__(self, uri=None):
|
||||
if uri:
|
||||
self.set_uri(uri)
|
||||
else:
|
||||
self.__scheme = None
|
||||
self.__location = None
|
||||
self.__path = None
|
||||
self.__filename = None
|
||||
self.__params = None
|
||||
self.__query = None
|
||||
self.__fragment = None
|
||||
self.__uri = None
|
||||
|
||||
self.__authinfo = None
|
||||
|
||||
def get_uri(self):
|
||||
if self.__uri:
|
||||
return self.__uri
|
||||
return None
|
||||
|
||||
def set_uri(self, uri):
|
||||
# (scheme, location, path, params, query, fragment)
|
||||
u = urlparse(uri, "file")
|
||||
self.__scheme = u[0]
|
||||
self.__location = u[1]
|
||||
self.__path = u[2]
|
||||
self.__filename = basename(self.__path)
|
||||
self.__params = u[3]
|
||||
self.__query = u[4]
|
||||
self.__fragment = u[5]
|
||||
|
||||
self.__uri = uri
|
||||
|
||||
def is_local_file(self):
|
||||
if self.scheme() == "file":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_remote_file(self):
|
||||
return not self.is_local_file()
|
||||
|
||||
def is_absolute_path(self):
|
||||
return util.absolute_path(self.__path)
|
||||
|
||||
def is_relative_path(self):
|
||||
return not self.is_absolute_path()
|
||||
|
||||
def set_auth_info(self, authTuple):
|
||||
if not isinstance(authTuple, tuple):
|
||||
raise Exception, _("setAuthInfo needs a tuple (user, pass)")
|
||||
self.__authinfo = authTuple
|
||||
|
||||
def auth_info(self):
|
||||
return self.__authinfo
|
||||
|
||||
def scheme(self):
|
||||
return self.__scheme
|
||||
|
||||
def location(self):
|
||||
return self.__location
|
||||
|
||||
def path(self):
|
||||
return self.__path
|
||||
|
||||
def filename(self):
|
||||
return self.__filename
|
||||
|
||||
def params(self):
|
||||
return self.__params
|
||||
|
||||
def query(self):
|
||||
return self.__query
|
||||
|
||||
def fragment(self):
|
||||
return self.__fragment
|
||||
|
||||
def __str__(self):
|
||||
return self.get_uri()
|
||||
|
||||
uri = property(get_uri, set_uri)
|
||||
@@ -1,558 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# misc. utility functions, including process and file utils
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
# S. Caglar Onur <caglar@uludag.org.tr>
|
||||
# A. Murat Eren <meren@uludag.org.tr>
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import sha
|
||||
import shutil
|
||||
import statvfs
|
||||
import operator
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class FileError(Error):
|
||||
pass
|
||||
|
||||
|
||||
#########################
|
||||
# spec validation utility #
|
||||
#########################
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
|
||||
def add(self, err):
|
||||
self.list.append(err)
|
||||
|
||||
def join(self, list):
|
||||
self.list.extend(list)
|
||||
|
||||
def has_tag(self, var, section, name):
|
||||
if not var:
|
||||
self.list.append(_("%s section should have a '%s' tag") % (section, name))
|
||||
|
||||
def has_error():
|
||||
return len(self.list)>0
|
||||
|
||||
def print_errors(list):
|
||||
for x in list:
|
||||
ctx.ui.error(x)
|
||||
print_errors = staticmethod(print_errors)
|
||||
|
||||
#########################
|
||||
# string/list/functional#
|
||||
#########################
|
||||
|
||||
def every(pred, seq):
|
||||
return reduce(operator.and_, map(pred, seq))
|
||||
|
||||
def any(pred, seq):
|
||||
return reduce(operator.or_, map(pred, seq))
|
||||
|
||||
def unzip(seq):
|
||||
return zip(*seq)
|
||||
|
||||
def concat(l):
|
||||
'''concatenate a list of lists'''
|
||||
return reduce( operator.concat, l )
|
||||
|
||||
def strlist(l):
|
||||
"""concatenate string reps of l's elements"""
|
||||
return "".join(map(lambda x: str(x) + ' ', l))
|
||||
|
||||
def multisplit(str, chars):
|
||||
""" split str with any of chars"""
|
||||
l = [str]
|
||||
for c in chars:
|
||||
l = concat(map(lambda x:x.split(c), l))
|
||||
return l
|
||||
|
||||
def same(l):
|
||||
'''check if all elements of a sequence are equal'''
|
||||
if len(l)==0:
|
||||
return True
|
||||
else:
|
||||
last = l.pop()
|
||||
for x in l:
|
||||
if x!=last:
|
||||
return False
|
||||
return True
|
||||
|
||||
def prefix(a, b):
|
||||
'''check if sequence a is a prefix of sequence b'''
|
||||
if len(a)>len(b):
|
||||
return False
|
||||
for i in range(0,len(a)):
|
||||
if a[i]!=b[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove_prefix(a,b):
|
||||
"remove prefix a from sequence b"
|
||||
assert prefix(a,b)
|
||||
return b[len(a):]
|
||||
|
||||
|
||||
##############################
|
||||
# Process Releated Functions #
|
||||
##############################
|
||||
|
||||
def run_batch(cmd):
|
||||
"""run command non-interactively and report return value and output"""
|
||||
ctx.ui.info(_('running ') + cmd)
|
||||
a = os.popen(cmd)
|
||||
lines = a.readlines()
|
||||
ret = a.close()
|
||||
ctx.ui.debug(_('return value %s') % ret)
|
||||
successful = ret == None
|
||||
if not successful:
|
||||
ctx.ui.error(_('Failed command: %s') % cmd + strlist(lines))
|
||||
return (successful,lines)
|
||||
|
||||
def xterm_title(message):
|
||||
"""sets message as a console window's title"""
|
||||
return
|
||||
#TODO: the last thing needed in util probably
|
||||
#this is going to be moved to pisi.cli later
|
||||
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"""
|
||||
#TODO: this, too.
|
||||
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
|
||||
|
||||
# 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 dir_size(dir):
|
||||
""" calculate the size of files under a dir
|
||||
based on the os module example"""
|
||||
# It's really hard to give an approximate value for package's
|
||||
# installed size. Gettin a sum of all files' sizes if far from
|
||||
# being true. Using 'du' command (like Debian does) can be a
|
||||
# better solution :(.
|
||||
# Not really, du calculates size on disk, this is much better -- exa
|
||||
from os.path import getsize, islink, isdir, exists
|
||||
join = join_path
|
||||
|
||||
if exists(dir) and (not isdir(dir) and not islink(dir)):
|
||||
#so, this is not a directory but file..
|
||||
return getsize(dir)
|
||||
|
||||
def sizes():
|
||||
for root, dirs, files in os.walk(dir):
|
||||
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
|
||||
return sum( sizes() )
|
||||
|
||||
def copy_file(src,dest):
|
||||
"""copy source file to destination file"""
|
||||
check_file(src)
|
||||
check_dir(os.path.dirname(dest))
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
# FIXME: this should be done in a much much simpler way
|
||||
# as it stands, it seems to be a kludge to solve
|
||||
# an unrelated problem
|
||||
def get_file_hashes(top, excludePrefix=None, removePrefix=None):
|
||||
"""Generator function iterates over a toplevel path and returns the
|
||||
(filePath, sha1Hash) tuples for all files. If excludePrefixes list
|
||||
is given as a parameter, function will exclude the filePaths
|
||||
matching those prefixes. The removePrefix string parameter will be
|
||||
used to remove prefix from filePath while matching excludes, if
|
||||
given."""
|
||||
|
||||
def sha1_sum(f, data=False):
|
||||
func = None
|
||||
if data:
|
||||
func = sha1_data
|
||||
else:
|
||||
func = sha1_file
|
||||
|
||||
try:
|
||||
return func(f)
|
||||
except FileError, e:
|
||||
if os.path.islink(f):
|
||||
ctx.ui.warning(_('Ignoring "external" link %s') % f)
|
||||
elif os.path.isdir(f):
|
||||
ctx.ui.warning(_('Ignoring directory %s') % f)
|
||||
else:
|
||||
raise e
|
||||
return None
|
||||
|
||||
# also handle single files
|
||||
if os.path.isfile(top):
|
||||
yield (top, sha1_sum(top))
|
||||
return
|
||||
|
||||
def has_excluded_prefix(filename):
|
||||
if excludePrefix and removePrefix:
|
||||
tempfnam = remove_prefix(removePrefix, filename)
|
||||
for p in excludePrefix:
|
||||
if tempfnam.startswith(p):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
for root, dirs, files in os.walk(top, topdown=False):
|
||||
#bug 339
|
||||
if os.path.islink(root) and not has_excluded_prefix(root):
|
||||
#yield the symlink..
|
||||
#bug 373
|
||||
yield (root, sha1_sum(os.readlink(root), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, root) + "/")
|
||||
continue
|
||||
|
||||
#bug 397
|
||||
for dir in dirs:
|
||||
d = join_path(root, dir)
|
||||
if os.path.islink(d) and not has_excluded_prefix(d):
|
||||
yield (d, sha1_sum(os.readlink(d), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, d) + "/")
|
||||
|
||||
#bug 340
|
||||
if os.path.isdir(root) and not has_excluded_prefix(root):
|
||||
parent, r, d, f = root, '', '', ''
|
||||
for r, d, f in os.walk(parent, topdown=False): pass
|
||||
if not f and not d:
|
||||
yield (parent, sha1_sum(parent))
|
||||
|
||||
for fname in files:
|
||||
f = join_path(root, fname)
|
||||
if has_excluded_prefix(f):
|
||||
continue
|
||||
#bug 373
|
||||
elif os.path.islink(f):
|
||||
yield (f, sha1_sum(os.readlink(f), True))
|
||||
else:
|
||||
yield (f, sha1_sum(f))
|
||||
|
||||
def copy_dir(src, dest):
|
||||
"""copy source dir to destination dir recursively"""
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def check_file_hash(filename, hash):
|
||||
"""Check the files integrity with a given hash"""
|
||||
if sha1_file(filename) == hash:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def sha1_file(filename):
|
||||
"""calculate sha1 hash of filename"""
|
||||
# Broken links can cause problem!
|
||||
try:
|
||||
m = sha.new()
|
||||
f = file(filename, 'rb')
|
||||
for line in f:
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
except IOError:
|
||||
raise FileError(_("I/O Error: Cannot calculate SHA1 hash of %s") % filename)
|
||||
|
||||
def sha1_data(data):
|
||||
"""calculate sha1 hash of given data"""
|
||||
try:
|
||||
m = sha.new()
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
except:
|
||||
raise Error(_("Cannot calculate SHA1 hash of given data"))
|
||||
|
||||
def uncompress(patchFile, compressType="gz", targetDir=None):
|
||||
"""uncompresses a file and returns the path of the uncompressed
|
||||
file"""
|
||||
if targetDir:
|
||||
filePath = join_path(targetDir,
|
||||
os.path.basename(patchFile))
|
||||
else:
|
||||
filePath = os.path.basename(patchFile)
|
||||
|
||||
if compressType == "gz":
|
||||
from gzip import GzipFile
|
||||
obj = GzipFile(patchFile)
|
||||
elif compressType == "bz2":
|
||||
from bz2 import BZ2File
|
||||
obj = BZ2File(patchFile)
|
||||
|
||||
open(filePath, "w").write(obj.read())
|
||||
return filePath
|
||||
|
||||
|
||||
def do_patch(sourceDir, patchFile, level = 0, target = ''):
|
||||
"""simple function to apply patches.."""
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sourceDir)
|
||||
|
||||
if level == None:
|
||||
level = 0
|
||||
if target == None:
|
||||
target = ''
|
||||
|
||||
check_file(patchFile)
|
||||
(successful, lines) = run_batch("patch -p%d %s< %s" %
|
||||
(level, target, patchFile))
|
||||
if not successful:
|
||||
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile,
|
||||
strlist (lines)))
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def strip_directory(top, excludelist=[]):
|
||||
for root, dirs, files in os.walk(top):
|
||||
for fn in files:
|
||||
frpath = join_path(root, fn)
|
||||
|
||||
# Some upstream sources have buggy libtool and ltmain.sh with them,
|
||||
# which causes wrong path entries in *.la files. And these wrong path
|
||||
# entries sometimes triggers compile-time errors or linkage problems.
|
||||
# Instead of patching all these buggy sources and maintain these patches,
|
||||
# PISI removes wrong paths...
|
||||
extension = os.path.splitext(frpath)[1]
|
||||
if extension == ".la":
|
||||
# FIXME: I'm regular expr. idiot, so one can convert this to python...
|
||||
os.system("sed -i -e 's~-L/var/tmp/pisi/[[:graph:]]* ~~g' %s" % frpath)
|
||||
|
||||
# real path in .pisi package
|
||||
p = '/' + removepathprefix(top, frpath)
|
||||
strip = True
|
||||
for exclude in excludelist:
|
||||
if p.startswith(exclude):
|
||||
strip = False
|
||||
ctx.ui.debug("%s [%s]" %(p, "NoStrip"))
|
||||
|
||||
if strip:
|
||||
if strip_file(frpath):
|
||||
ctx.ui.debug("%s [%s]" %(p, "stripped"))
|
||||
|
||||
|
||||
def strip_file(filepath):
|
||||
"""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)
|
||||
|
||||
if "current ar archive" in o:
|
||||
run_strip(filepath, "-g")
|
||||
return True
|
||||
|
||||
elif "SB executable" in o:
|
||||
run_strip(filepath)
|
||||
return True
|
||||
|
||||
elif "SB shared object" in o:
|
||||
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):
|
||||
fn = name + '-' + version + '-' + release
|
||||
if build:
|
||||
fn += '-' + str(build)
|
||||
fn += ctx.const.package_suffix
|
||||
return fn
|
||||
|
||||
def is_package_name(fn, package_name = None):
|
||||
"check if fn is a valid filename for given package_name"
|
||||
"if not given a package name, see if fn fits the package name rules"
|
||||
if (package_name==None) or fn.startswith(package_name + '-'):
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
# get version string, skip separator '-'
|
||||
verstr = fn[len(package_name) + 1:
|
||||
len(fn)-len(ctx.const.package_suffix)]
|
||||
import string
|
||||
for x in verstr.split('-'):
|
||||
# weak rule: version components after '-' start with a digit
|
||||
if x is '' or (not x[0] in string.digits):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def env_update():
|
||||
|
||||
ctx.ui.info(_('Updating environment...'))
|
||||
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
|
||||
profile_file = join_path(ctx.config.dest_dir(), "/etc/profile.env")
|
||||
ldconf_file = join_path(ctx.config.dest_dir(), "/etc/ld.so.conf")
|
||||
|
||||
if not os.path.exists(env_dir):
|
||||
os.makedirs(env_dir, 0755)
|
||||
|
||||
list = []
|
||||
for file in os.listdir(env_dir):
|
||||
if not os.path.isdir(join_path(env_dir, file)):
|
||||
list.append(file)
|
||||
|
||||
list.sort()
|
||||
|
||||
keys = {}
|
||||
for file in list:
|
||||
f = open(join_path(env_dir, file), "r")
|
||||
for line in f:
|
||||
if not re.search("^#", line.strip()):
|
||||
currentLine = line.strip().split("=")
|
||||
|
||||
try:
|
||||
if keys.has_key(currentLine[0]):
|
||||
keys[currentLine[0]] += ":" + currentLine[1].replace("\"", "")
|
||||
else:
|
||||
keys[currentLine[0]] = currentLine[1].replace("\"", "")
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
# generate profile.env
|
||||
f = open(profile_file, "w")
|
||||
for key in keys:
|
||||
f.write("export %s=\"%s\"\n" % (key, keys[key]))
|
||||
f.close()
|
||||
|
||||
# generate ld.so.conf
|
||||
f = open(ldconf_file, "w")
|
||||
if keys.has_key("LDPATH"):
|
||||
for path in keys["LDPATH"].split(":"):
|
||||
f.write("%s\n" % path)
|
||||
f.close()
|
||||
|
||||
# run ldconfig
|
||||
run_batch("/sbin/ldconfig -X -r %s" % ctx.config.dest_dir())
|
||||
@@ -1,193 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
#
|
||||
# History:
|
||||
# Eray wrote the first version
|
||||
# Baris added support for the fancy keywords and
|
||||
# alphanumeric version string components
|
||||
|
||||
|
||||
"""version structure"""
|
||||
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
# Basic rule is:
|
||||
# p > (no suffix) > rc > pre > beta > alpha
|
||||
keywords = {"alpha": 0,
|
||||
"beta" : 1,
|
||||
"pre" : 2,
|
||||
"rc" : 3,
|
||||
"NOKEY" : 4,
|
||||
"p" : 5}
|
||||
|
||||
# helper functions
|
||||
def has_keyword(versionitem):
|
||||
if versionitem._keyword != "NOKEY":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class VersionItem:
|
||||
_keyword = "NOKEY"
|
||||
_value = 0
|
||||
|
||||
def __init__(self, itemstring):
|
||||
|
||||
# special-case: 1.5p
|
||||
# here "p" comes as a separate itemstring and conflicts with
|
||||
# keyword "p". But keyword should allways contain an integer prefix.
|
||||
# So, skipping looking for keywords if the length() is 1 makes the
|
||||
# trick.
|
||||
if len(itemstring) > 1:
|
||||
|
||||
for keyword in keywords.keys():
|
||||
if itemstring.startswith(keyword):
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
self._keyword = keyword
|
||||
else:
|
||||
# longer match is correct
|
||||
if len(keyword) > len(self._keyword):
|
||||
self._keyword = keyword
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
if len(itemstring) == 1 and itemstring in string.ascii_letters:
|
||||
# single letter version item ('a' to 'Z')
|
||||
self._value = itemstring
|
||||
else:
|
||||
self._value = int(itemstring)
|
||||
else:
|
||||
# rest is the version item's value. And each must have
|
||||
# one!
|
||||
self._value = int(itemstring[len(self._keyword):])
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value < rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __le__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value <= rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __gt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value > rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __ge__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value >= rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __eq__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l == r and self._value == rhs._value:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class Version:
|
||||
|
||||
def __init__(self, verstring):
|
||||
self.comps = []
|
||||
for i in util.multisplit(verstring,'.-_'):
|
||||
# some version strings can contain ascii chars at the
|
||||
# back. As an example: 2.11a
|
||||
# We split '11a' as two items like '11' and 'a'
|
||||
s = re.compile("[a-z-A-Z]$").search(i)
|
||||
if s:
|
||||
head = i[:s.start()]
|
||||
tail = s.group()
|
||||
self.comps.append(VersionItem(head))
|
||||
self.comps.append(VersionItem(tail))
|
||||
else:
|
||||
self.comps.append(VersionItem(i))
|
||||
|
||||
self.verstring = verstring
|
||||
|
||||
def string(self):
|
||||
return self.verstring
|
||||
|
||||
def compare(self, rhs):
|
||||
"""this comparison routine is essentially a comparison routine
|
||||
for two rationals in (0,1) interval. we compare two sequences
|
||||
of digits one by one. We start with the leftmost digit
|
||||
in the expansion. If they are equal, we proceed to the next. If
|
||||
not we use the comparison operator. And we iterate to the left.
|
||||
The result is, 0 if two are equal, -1 if self < rhs, and +1
|
||||
if self>rhs"""
|
||||
lhs = self.comps
|
||||
rhs = rhs.comps
|
||||
# pad the short version string with zeros
|
||||
if len(lhs) < len(rhs):
|
||||
lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) )
|
||||
elif len(lhs) > len(rhs):
|
||||
rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) )
|
||||
# now let's iterate from left to right
|
||||
for (litem, ritem) in zip(lhs, rhs):
|
||||
if litem < ritem:
|
||||
return -1
|
||||
elif litem > ritem:
|
||||
return +1
|
||||
return 0
|
||||
|
||||
# premature optimization is the root of all evil
|
||||
|
||||
def __lt__(self,rhs):
|
||||
return self.compare(rhs) < 0
|
||||
|
||||
def __le__(self,rhs):
|
||||
return self.compare(rhs) <= 0
|
||||
|
||||
def __gt__(self,rhs):
|
||||
return self.compare(rhs) > 0
|
||||
|
||||
def __ge__(self,rhs):
|
||||
return self.compare(rhs) >= 0
|
||||
|
||||
def __eq__(self,rhs):
|
||||
return self.compare(rhs) == 0
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from distutils.core import setup
|
||||
from distutils.command.install import install
|
||||
|
||||
PISI_VERSION = "0.1"
|
||||
|
||||
def getRevision():
|
||||
import os
|
||||
try:
|
||||
p = os.popen("svn info 2> /dev/null")
|
||||
for line in p.readlines():
|
||||
line = line.strip()
|
||||
if line.startswith("Revision:"):
|
||||
return line.split(":")[1].strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
# doesn't working in a Subversion directory
|
||||
return None
|
||||
|
||||
def getVersion():
|
||||
rev = getRevision()
|
||||
if rev:
|
||||
return "-r".join([PISI_VERSION, rev])
|
||||
else:
|
||||
return PISI_VERSION
|
||||
|
||||
i18n_domain = "pisi"
|
||||
i18n_languages = "tr"
|
||||
|
||||
class I18nInstall(install):
|
||||
def run(self):
|
||||
install.run(self)
|
||||
for lang in i18n_languages.split(' '):
|
||||
print "Installing '%s' translations..." % lang
|
||||
os.popen("msgfmt po/%s.po -o po/%s.mo" % (lang, lang))
|
||||
if not self.root:
|
||||
self.root = "/"
|
||||
destpath = os.path.join(self.root, "usr/share/locale/%s/LC_MESSAGES" % lang)
|
||||
try:
|
||||
os.makedirs(destpath)
|
||||
except:
|
||||
pass
|
||||
shutil.copy("po/%s.mo" % lang, os.path.join(destpath, "%s.mo" % i18n_domain))
|
||||
|
||||
setup(name="pisi",
|
||||
version= getVersion(),
|
||||
description="PISI (Packages Installed Successfully as Intended)",
|
||||
long_description="PISI is the package management system of Pardus Linux.",
|
||||
license="GNU GPL2",
|
||||
author="Pardus Developers",
|
||||
author_email="pisi@uludag.org.tr",
|
||||
url="http://www.uludag.org.tr/eng/pisi/",
|
||||
package_dir = {'': ''},
|
||||
packages = ['pisi', 'pisi.cli', 'pisi.actionsapi', 'pisi.pxml'],
|
||||
scripts = ['pisi-cli'],
|
||||
cmdclass = {
|
||||
'install' : I18nInstall
|
||||
}
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
PISI test suite
|
||||
---------------
|
||||
|
||||
|
||||
There are python unit tests and shell scripts in this directory.
|
||||
|
||||
Shell scripts use the CLIs to perform package operations.
|
||||
|
||||
For this there must be a symlink "packages" to the PISI package repository
|
||||
until we can find a few sample packages to put here.
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
|
||||
<A href="http://www.cs.bilkent.edu.tr/~erayo">
|
||||
cıvık mantardır bu
|
||||
<Name>Eray Ozkural</Name>
|
||||
<Number>868</Number>
|
||||
<Description>Lazy Tin</Description>
|
||||
<Description xml:lang="tr">Tembel Teneke</Description>
|
||||
<Project>pisi</Project>
|
||||
<Project>noatun</Project>
|
||||
<Project>kdevelop</Project>
|
||||
<OtherInfo>
|
||||
<BirthDate>18071976</BirthDate>
|
||||
<Interest>AI</Interest>
|
||||
<CodesWith>
|
||||
<Person>Baris</Person>
|
||||
<Person>Gurer</Person>
|
||||
<Person>Caglar</Person>
|
||||
<Person>Meren</Person>
|
||||
</CodesWith>
|
||||
</OtherInfo>
|
||||
</A>
|
||||
@@ -1,58 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
import testcase
|
||||
|
||||
class ActionsAPITestCase(testcase.TestCase):
|
||||
def setUp(self):
|
||||
testcase.TestCase.setUp(self)
|
||||
#FIXME: test incomplete
|
||||
return
|
||||
self.f = zipfile.ZipFile("helloworld-0.1-1.pisi", "r")
|
||||
self.filelist = []
|
||||
|
||||
for file in self.f.namelist():
|
||||
self.filelist.append(file)
|
||||
|
||||
def testFileList(self):
|
||||
#FIXME: test incomplete
|
||||
return
|
||||
fileContent = ["files.xml", \
|
||||
"install/bin/helloworld", \
|
||||
"install/opt/PARDUS", \
|
||||
"install/opt/helloworld/helloworld", \
|
||||
"install/opt/uludag", \
|
||||
"install/sbin/helloworld", \
|
||||
"install/sys/PARDUS", \
|
||||
"install/sys/uludag", \
|
||||
"install/usr/bin/goodbye", \
|
||||
"install/usr/bin/helloworld", \
|
||||
"install/usr/lib/helloworld.o", \
|
||||
"install/usr/sbin/goodbye", \
|
||||
"install/usr/sbin/helloworld", \
|
||||
"install/usr/share/doc/helloworld-0.1-1/Makefile.am", \
|
||||
"install/usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp", \
|
||||
"install/usr/share/info/Makefile.am", \
|
||||
"install/usr/share/info/Makefile.cvs", \
|
||||
"install/usr/share/info/Makefile.in", \
|
||||
"install/var/goodbye", \
|
||||
"install/var/hello", \
|
||||
"metadata.xml"]
|
||||
|
||||
'''check number of files in package'''
|
||||
self.assertEqual(fileContent.__len__(), self.filelist.__len__())
|
||||
|
||||
'''check file content'''
|
||||
for file in self.filelist:
|
||||
self.assert_(fileContent.__contains__(file))
|
||||
|
||||
suite = unittest.makeSuite(ActionsAPITestCase)
|
||||
@@ -1,114 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
from os.path import exists as pathexists
|
||||
from os.path import basename, islink, join
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
from pisi import archive
|
||||
from pisi import sourcearchive
|
||||
from pisi import fetcher
|
||||
from pisi import util
|
||||
from pisi.build import BuildContext
|
||||
from pisi import uri
|
||||
|
||||
import testcase
|
||||
|
||||
class ArchiveFileTestCase(testcase.TestCase):
|
||||
|
||||
def testUnpackTar(self):
|
||||
bctx = BuildContext("tests/popt/pspec.xml")
|
||||
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
|
||||
assert bctx.spec.source.archive.type == "targz"
|
||||
|
||||
# skip fetching and directly unpack the previously fetched (by
|
||||
# fetchertests) archive
|
||||
if not achv.is_cached(interactive=False):
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack()
|
||||
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
# but testing is hard
|
||||
# "var/tmp/pisi/popt-1.7-3/work" (targetDir)
|
||||
assert pathexists(targetDir + "/popt-1.7")
|
||||
|
||||
testfile = targetDir + "/popt-1.7/Makefile.am"
|
||||
assert pathexists(testfile)
|
||||
|
||||
# check file integrity
|
||||
self.assertEqual(util.sha1_file(testfile),
|
||||
"5af9dd7d754f788cf511c57ce0af3d555fed009d")
|
||||
|
||||
def testUnpackZip(self):
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
|
||||
assert bctx.spec.source.archive.type == "zip"
|
||||
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack(clean_dir=True)
|
||||
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
assert pathexists(targetDir + "/pccts")
|
||||
|
||||
testfile = targetDir + "/pccts/history.txt"
|
||||
assert pathexists(testfile)
|
||||
|
||||
# check file integrity
|
||||
self.assertEqual(util.sha1_file(testfile),
|
||||
"f2be0f9783e84e98fe4e2b8201a8f506fcc07a4d")
|
||||
|
||||
# TODO: no link file in pccts package. Need to find a ZIP file
|
||||
# containing a symlink
|
||||
# check for symbolic links
|
||||
# testfile = targetDir + "/sandbox/testdir/link1"
|
||||
# assert islink(testfile)
|
||||
|
||||
def testMakeZip(self):
|
||||
# first unpack our dear sandbox.zip
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack(clean_dir=True)
|
||||
del achv
|
||||
|
||||
newZip = targetDir + "/new.zip"
|
||||
zip = archive.ArchiveZip(newZip, 'zip', 'w')
|
||||
sourceDir = targetDir + "/pccts"
|
||||
zip.add_to_archive(sourceDir)
|
||||
zip.close()
|
||||
|
||||
#TODO: do some more work to test the integrity of new zip file
|
||||
|
||||
def testUnpackZipCond(self):
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
url = uri.URI(bctx.spec.source.archive.uri)
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
filePath = join(ctx.config.archives_dir(), url.filename())
|
||||
|
||||
# check cached
|
||||
if util.sha1_file(filePath) != bctx.spec.source.archive.sha1sum:
|
||||
fetch = fetcher.Fetcher(bctx.spec.source.archive.uri, targetDir)
|
||||
fetch.fetch()
|
||||
assert bctx.spec.source.archive.type == "zip"
|
||||
|
||||
achv = archive.Archive(filePath, bctx.spec.source.archive.type)
|
||||
achv.unpack_files(["pccts/history.txt"], targetDir)
|
||||
assert pathexists(targetDir + "/pccts")
|
||||
testfile = targetDir + "/pccts/history.txt"
|
||||
assert pathexists(testfile)
|
||||
|
||||
suite = unittest.makeSuite(ArchiveFileTestCase)
|
||||
@@ -1,102 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import types
|
||||
|
||||
import pisi
|
||||
import pisi.api
|
||||
from pisi.pxml import xmlfile
|
||||
from pisi.pxml import autoxml
|
||||
import pisi.util as util
|
||||
|
||||
class AutoXmlTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
class OtherInfo:
|
||||
__metaclass__ = autoxml.autoxml
|
||||
t_BirthDate = [types.StringType, autoxml.mandatory]
|
||||
t_Interest = [types.StringType, autoxml.optional]
|
||||
t_CodesWith = [ [types.StringType], autoxml.optional, 'CodesWith/Person']
|
||||
|
||||
class A(xmlfile.XmlFile):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
t_Name = [types.StringType, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Number = [types.IntType, autoxml.optional]
|
||||
t_Email = [types.StringType, autoxml.optional]
|
||||
a_href = [types.StringType, autoxml.mandatory]
|
||||
t_Projects = [ [types.StringType], autoxml.mandatory, 'Project']
|
||||
t_OtherInfo = [ OtherInfo, autoxml.optional ]
|
||||
s_Comment = [ autoxml.Text, autoxml.mandatory]
|
||||
|
||||
self.A = A
|
||||
|
||||
def testDeclaration(self):
|
||||
self.assertEqual(len(self.A.decoders), 8) # how many fields in A?
|
||||
self.assert_(hasattr(self.A, 'encode'))
|
||||
|
||||
def testReadWrite(self):
|
||||
a = self.A()
|
||||
|
||||
# test initializer
|
||||
self.assertEqual(a.href, None)
|
||||
|
||||
# test read
|
||||
a.read('tests/a.xml')
|
||||
self.assert_(a.href.startswith('http'))
|
||||
self.assertEqual(a.number, 868)
|
||||
self.assertEqual(a.name, 'Eray Ozkural')
|
||||
self.assertEqual(len(a.projects), 3)
|
||||
self.assertEqual(len(a.otherInfo.codesWith), 4)
|
||||
|
||||
self.assert_(not a.errors())
|
||||
|
||||
a.print_text(file('/tmp/a', 'w'))
|
||||
la = file('/tmp/a').readlines()
|
||||
self.assert_( util.any(lambda x:x.find('18071976')!=-1, la) )
|
||||
a.write('/tmp/a.xml')
|
||||
|
||||
def testWriteRead(self):
|
||||
a = self.A()
|
||||
a.name = "Baris Metin"
|
||||
a.email = "baris@uludag.org.tr"
|
||||
a.description['tr'] = u'Melek, melek'
|
||||
a.comment = u'Bu da zibidi aslinda ama caktirmiyor'
|
||||
a.href = 'http://cekirdek.uludag.org.tr/~baris'
|
||||
a.otherInfo.birthDate = '30101979'
|
||||
a.projects = [ 'pisi', 'tasma', 'plasma' ]
|
||||
errs = a.errors()
|
||||
if errs:
|
||||
self.fail( 'We got a bunch of errors: ' + str(errs))
|
||||
a.write('/tmp/a2.xml')
|
||||
a2 = self.A()
|
||||
a2.read('/tmp/a2.xml')
|
||||
self.assertEqual(a, a2)
|
||||
|
||||
class LocalTextTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
a = autoxml.LocalText()
|
||||
a['tr'] = u'Zibidi'
|
||||
a['en'] = u'ingiliz hiyarlari ne anlar zibididen'
|
||||
self.a = a
|
||||
|
||||
def testStr(self):
|
||||
s = str(self.a)
|
||||
self.assert_(s!= None and len(s)>=6)
|
||||
|
||||
suite1 = unittest.makeSuite(AutoXmlTestCase)
|
||||
suite2 = unittest.makeSuite(LocalTextTestCase)
|
||||
suite = unittest.TestSuite((suite1, suite2))
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "beta functionality test script"
|
||||
echo "working directory:" `pwd`
|
||||
echo "cleaning destination dir: tmp"
|
||||
PATH=$PATH:.
|
||||
set -x # xtrace
|
||||
set -e # errexit
|
||||
rm -rf tmp
|
||||
#echo "*** build tests"
|
||||
pisi-cli -Dtmp build http://svn.uludag.org.tr/pardus/pardus-devel/system/base/zip/pspec.xml http://svn.uludag.org.tr/pardus/pardus-devel/system/base/unzip/pspec.xml
|
||||
|
||||
#partial-builds
|
||||
pisi-cli -Dtmp build-setup http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
|
||||
pisi-cli -Dtmp build-build http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
|
||||
pisi-cli -Dtmp build-install http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
|
||||
pisi-cli -Dtmp build-package http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
|
||||
|
||||
#echo "*** repository tests"
|
||||
|
||||
pisi-cli -Dtmp index .
|
||||
pisi-cli -Dtmp add-repo repo1 pisi-index.xml
|
||||
pisi-cli -Dtmp update-repo repo1
|
||||
pisi-cli -Dtmp list-repo
|
||||
|
||||
#echo "*** package ops"
|
||||
pisi-cli -Dtmp info *.pisi
|
||||
# pisi-cli list-available
|
||||
pisi-cli -Dtmp install --ignore-comar zip
|
||||
pisi-cli -Dtmp list-installed
|
||||
pisi-cli -Dtmp remove --ignore-comar unzip
|
||||
pisi-cli -Dtmp install --ignore-comar zip*.pisi
|
||||
pisi-cli -Dtmp install --ignore-comar hdparm*.pisi flex*.pisi grep*.pisi
|
||||
pisi-cli -Dtmp remove-repo repo1
|
||||
# pisi-cli list-available
|
||||
|
||||
echo "*** database contents"
|
||||
for x in `find tmp -iname '*.bdb'`; do
|
||||
echo "contents of database " $x;
|
||||
tools/cat-db.py $x;
|
||||
done
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
pwd
|
||||
PATH=$PATH:.
|
||||
set -x
|
||||
mkdir tmp
|
||||
pisi-cli -Dtmp clean
|
||||
pisi-cli -B -Dtmp remove unzip
|
||||
pisi-cli -Dtmp remove-repo repo1
|
||||
set -e
|
||||
pisi-cli -Dtmp -E --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml
|
||||
pisi-cli -Dtmp index .
|
||||
pisi-cli -Dtmp -d add-repo repo1 pisi-index.xml
|
||||
pisi-cli -Dtmp list-repo
|
||||
pisi-cli -Dtmp update-repo repo1
|
||||
pisi-cli -Dtmp list-available
|
||||
pisi-cli -Dtmp --ignore-comar -y install zip
|
||||
pisi-cli -Dtmp list-installed
|
||||
pisi-cli -Dtmp --ignore-comar -y remove unzip
|
||||
pisi-cli -Dtmp info zip*.pisi
|
||||
pisi-cli -d -Dtmp --ignore-comar -y install zip*.pisi
|
||||
pisi-cli -Dtmp list-pending
|
||||
pisi-cli -Dtmp configure-pending
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
export PATH=$PATH:.
|
||||
set -x # xtrace
|
||||
set -e # errexit
|
||||
|
||||
echo "beta functionality test script for testing remote repos"
|
||||
echo "working directory:" `pwd`
|
||||
echo "cleaning destination dir: tmp"
|
||||
rm -rf tmp
|
||||
#echo "*** repository tests"
|
||||
pisi-cli -Dtmp add-repo pardus http://paketler.uludag.org.tr/pardus-devel/pisi-index.xml
|
||||
pisi-cli -Dtmp update-repo pardus
|
||||
pisi-cli -Dtmp list-repo
|
||||
|
||||
#echo "*** package ops"
|
||||
pisi-cli -Dtmp list-available
|
||||
pisi-cli -Dtmp info python
|
||||
pisi-cli -Dtmp -By install python
|
||||
|
||||
echo "*** database contents"
|
||||
for x in `find tmp -iname '*.bdb'`; do
|
||||
echo "contents of database " $x;
|
||||
tools/cat-db.py $x;
|
||||
done
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
pwd
|
||||
PATH=$PATH:.
|
||||
set -x -e
|
||||
|
||||
pisi-cli -Dtmp -E --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml
|
||||
pisi-cli -Dtmp --yes-all --ignore-comar install unzip-5.50-1.pisi zip-2.3-1.pisi
|
||||
|
||||
mkdir -p myrepo
|
||||
cd myrepo
|
||||
mkdir -p tmp
|
||||
../pisi-cli -Dtmp -E --ignore-build-no build ../tests/zip2/pspec.xml ../tests/unzip2/pspec.xml
|
||||
cd ..
|
||||
pisi-cli -Dtmp --absolute-uris index myrepo
|
||||
pisi-cli -Dtmp remove-repo repo1
|
||||
pisi-cli -Dtmp add-repo repo1 pisi-index.xml
|
||||
pisi-cli -Dtmp list-repo
|
||||
pisi-cli -Dtmp update-repo repo1
|
||||
pisi-cli -Dtmp list-available
|
||||
pisi-cli -Dtmp --install-info list-installed
|
||||
pisi-cli -Dtmp list-upgrades
|
||||
pisi-cli -Dtmp --ignore-comar upgrade zip
|
||||
pisi-cli -Dtmp --install-info list-installed
|
||||
@@ -1,61 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
from pisi.configfile import ConfigurationFile
|
||||
|
||||
# NB: no need for pisi testcase in these things that do well without pisi init
|
||||
class ConfigFileTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cf = ConfigurationFile("tests/pisi.conf")
|
||||
|
||||
def testSections(self):
|
||||
cf = self.cf
|
||||
if not cf.general:
|
||||
self.fail("No 'general' section found in ConfigurationFile")
|
||||
if not cf.build:
|
||||
self.fail("No 'build' section found in ConfigurationFile")
|
||||
if not cf.dirs:
|
||||
self.fail("No 'dirs' section found in ConfigurationFile")
|
||||
|
||||
def testValues(self):
|
||||
cf = self.cf
|
||||
|
||||
# test values from pisi.conf file
|
||||
self.assertEqual(cf.general.destinationdirectory, "/testing")
|
||||
self.assertEqual(cf.dirs.archives_dir, "/disk2/pisi/archives")
|
||||
|
||||
# test default values
|
||||
self.assertEqual(cf.dirs.tmp_dir, "/var/tmp/pisi")
|
||||
|
||||
def testAccessMethods(self):
|
||||
cf = self.cf
|
||||
|
||||
self.assertEqual(cf.build.host, cf.build["host"])
|
||||
self.assertEqual(cf.dirs.index_dir, cf.dirs["index_dir"])
|
||||
|
||||
def testFlagsExists(self):
|
||||
cf = self.cf
|
||||
|
||||
#build
|
||||
self.assert_(cf.build.CFLAGS)
|
||||
self.assert_(cf.build.CXXFLAGS)
|
||||
|
||||
#general
|
||||
self.assert_(cf.general.destinationdirectory)
|
||||
|
||||
#dirs
|
||||
self.assert_(cf.dirs.index_dir)
|
||||
self.assert_(cf.dirs.tmp_dir)
|
||||
self.assert_(cf.dirs.packages_dir)
|
||||
|
||||
suite = unittest.makeSuite(ConfigFileTestCase)
|
||||
@@ -1,67 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
import pisi.context as ctx
|
||||
|
||||
class ContextTestCase(unittest.TestCase):
|
||||
|
||||
def testConstness(self):
|
||||
const = ctx.const
|
||||
|
||||
# test if we can get a const attribute?
|
||||
try:
|
||||
test = const.package_suffix
|
||||
self.assertNotEqual(test, "")
|
||||
except AttributeError:
|
||||
self.fail("Couldn't get const attribute")
|
||||
|
||||
# test binding a new constant
|
||||
const.test = "test binding"
|
||||
|
||||
# test re-binding (which is illegal)
|
||||
try:
|
||||
const.test = "test rebinding"
|
||||
# we shouldn't reach here
|
||||
self.fail("Rebinding a constant works. Something is wrong!")
|
||||
except:
|
||||
# we achived our goal with this error. infact, this is a
|
||||
# ConstError but we can't catch it directly here
|
||||
pass
|
||||
|
||||
# test unbinding (which is also illegal)
|
||||
try:
|
||||
del const.test
|
||||
# we shouldn't reach here
|
||||
self.fail("Unbinding a constant works. Something is wrong!")
|
||||
except:
|
||||
# we achived our goal with this error. infact, this is a
|
||||
# ConstError but we can't catch it directly here
|
||||
pass
|
||||
|
||||
def testConstValues(self):
|
||||
const = ctx.const
|
||||
|
||||
constDict = {
|
||||
"actions_file": "actions.py",
|
||||
"setup_func": "setup",
|
||||
"metadata_xml": "metadata.xml"
|
||||
}
|
||||
|
||||
for k in constDict.keys():
|
||||
if hasattr(const, k):
|
||||
value = getattr(const, k)
|
||||
self.assertEqual(value, constDict[k])
|
||||
else:
|
||||
self.fail("Constants does not have an attribute named %s" % k)
|
||||
|
||||
|
||||
suite = unittest.makeSuite(ContextTestCase)
|
||||
@@ -1,28 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi.dependency import *
|
||||
|
||||
class DependencyTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def testDep(self):
|
||||
# a real life story bug #603
|
||||
gtkmmdep = Dependency()
|
||||
gtkmmdep.versionFrom = '1.9.1'
|
||||
gtkmmdep.package = 'atk'
|
||||
self.assert_( not gtkmmdep.satisfies('atk', '1.8.0', 1) )
|
||||
|
||||
suite = unittest.makeSuite(DependencyTestCase)
|
||||
@@ -1,51 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import shutil
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
from pisi.specfile import SpecFile
|
||||
from pisi import fetcher
|
||||
from pisi import util
|
||||
from pisi import uri
|
||||
|
||||
import testcase
|
||||
class FetcherTestCase(testcase.TestCase):
|
||||
def setUp(self):
|
||||
testcase.TestCase.setUp(self, database = False)
|
||||
|
||||
self.spec = SpecFile()
|
||||
self.spec.read("tests/helloworld/pspec.xml")
|
||||
self.url = uri.URI(self.spec.source.archive.uri)
|
||||
self.destpath = ctx.config.archives_dir()
|
||||
self.fetch = fetcher.Fetcher(self.url, self.destpath)
|
||||
|
||||
def testFetch(self):
|
||||
self.fetch.fetch()
|
||||
fetchedFile = os.path.join(self.destpath, self.url.filename())
|
||||
if os.access(fetchedFile, os.R_OK):
|
||||
self.assertEqual(util.sha1_file(fetchedFile),
|
||||
self.spec.source.archive.sha1sum)
|
||||
os.remove(fetchedFile)
|
||||
|
||||
def testResume(self):
|
||||
resume_test_file = "tests/helloworld/hello-1.3.tar.gz.part"
|
||||
shutil.copy(resume_test_file, ctx.config.archives_dir())
|
||||
self.fetch.fetch()
|
||||
fetchedFile = os.path.join(self.destpath, self.url.filename())
|
||||
if os.access(fetchedFile, os.R_OK):
|
||||
self.assertEqual(util.sha1_file(fetchedFile),
|
||||
self.spec.source.archive.sha1sum)
|
||||
os.remove(fetchedFile)
|
||||
|
||||
suite = unittest.makeSuite(FetcherTestCase)
|
||||
@@ -1,31 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
import pisi.installdb
|
||||
import pisi.files as files
|
||||
from pisi import util
|
||||
|
||||
import testcase
|
||||
|
||||
class FilesTestCase(testcase.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
testcase.TestCase.setUp(self, database=False)
|
||||
|
||||
def testFileInfo(self):
|
||||
f1 = files.File(path = '/usr/bin/zibidi')
|
||||
f2 = files.File(path = '/usr/bin/kopus', type='exec', size=13, hash="abugubu")
|
||||
|
||||
suite = unittest.makeSuite(FilesTestCase)
|
||||
@@ -1,33 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import graph
|
||||
|
||||
class GraphTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.g0 = graph.Digraph()
|
||||
self.g0.from_list([ (1,2), (1,3), (2,3), (3,4), (4, 5), (4,1)])
|
||||
|
||||
self.g1 = graph.Digraph()
|
||||
self.g1.from_list([ (0,2), (0,3), (3,4), (2,4), (0,5), (5,4) ])
|
||||
|
||||
def testCycle(self):
|
||||
self.assert_(not self.g0.cycle_free())
|
||||
self.assert_(self.g1.cycle_free())
|
||||
|
||||
def testTopologicalSort(self):
|
||||
order = self.g1.topological_sort()
|
||||
self.assertEqual(order[0], 0)
|
||||
self.assertEqual(order[len(order)-1], 4)
|
||||
|
||||
suite = unittest.makeSuite(GraphTestCase)
|
||||
@@ -1,102 +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.
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
from pisi.actionsapi import pisitools
|
||||
from pisi.actionsapi import shelltools
|
||||
from pisi.actionsapi import libtools
|
||||
from pisi.actionsapi import get
|
||||
|
||||
WorkDir = "helloworld"
|
||||
|
||||
def setup():
|
||||
autotools.configure()
|
||||
|
||||
def build():
|
||||
autotools.make()
|
||||
|
||||
def install():
|
||||
autotools.install()
|
||||
|
||||
'''/opt/helloworld/'''
|
||||
pisitools.dodir("/opt/helloworld")
|
||||
|
||||
'''/usr/share/doc/helloworld-0.1-1/Makefile.am'''
|
||||
pisitools.dodoc("Makefile.am")
|
||||
|
||||
'''/opt/helloworld/helloworld'''
|
||||
pisitools.doexe("src/helloworld", "/opt/helloworld")
|
||||
|
||||
'''/usr/share/info/Makefile.am'''
|
||||
'''/usr/share/info/Makefile.cvs'''
|
||||
'''/usr/share/info/Makefile.in'''
|
||||
pisitools.doinfo("Makefile.*")
|
||||
|
||||
'''/usr/lib/helloworld.o'''
|
||||
pisitools.dolib("src/helloworld.o")
|
||||
|
||||
'''/opt/hello'''
|
||||
pisitools.insinto("/opt/", "src/helloworld", "hello")
|
||||
'''/opt/hi'''
|
||||
pisitools.insinto("/opt/", "src/helloworld", "hi")
|
||||
|
||||
'''/opt/hello -> /var/hello'''
|
||||
pisitools.domove("/opt/hello", "/var/")
|
||||
'''/opt/hi -> /var/goodbye'''
|
||||
pisitools.domove("/opt/hi", "/var/", "goodbye")
|
||||
|
||||
'''/usr/bin/helloworld'''
|
||||
pisitools.dobin("src/helloworld")
|
||||
'''/bin/helloworld'''
|
||||
pisitools.dobin("src/helloworld", "/bin")
|
||||
|
||||
'''/usr/sbin/helloworld'''
|
||||
pisitools.dosbin("src/helloworld")
|
||||
'''/sbin/helloworld'''
|
||||
pisitools.dosbin("src/helloworld", "/sbin")
|
||||
|
||||
'''Hello, world! -> Goodbye, world!'''
|
||||
pisitools.dosed("src/helloworld.cpp", "Hello, world!", "Goodbye, world!")
|
||||
|
||||
'''/usr/sbin/goodbye --> helloworld'''
|
||||
pisitools.dosym("helloworld", "/usr/sbin/goodbye")
|
||||
'''/usr/bin/goodbye --> helloworld'''
|
||||
pisitools.dosym("helloworld", "/usr/bin/goodbye")
|
||||
|
||||
'''/home/pardus/'''
|
||||
pisitools.dodir("/home/pardus")
|
||||
'''delete pardus'''
|
||||
pisitools.removeDir("/home/pardus")
|
||||
'''delete home'''
|
||||
pisitools.removeDir("/home")
|
||||
|
||||
'''src/helloworld.cpp --> /usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp'''
|
||||
pisitools.newdoc("src/helloworld.cpp", "goodbyeworld.cpp")
|
||||
|
||||
'''/opt/pardus'''
|
||||
shelltools.touch("%s/opt/pardus" % get.installDIR())
|
||||
|
||||
'''/opt/pardus --> /opt/uludag'''
|
||||
shelltools.copy("%s/opt/pardus" % get.installDIR(), "%s/opt/uludag" % get.installDIR())
|
||||
'''/opt/pardus --> /opt/Pardus'''
|
||||
shelltools.move("%s/opt/pardus" % get.installDIR(), "%s/opt/PARDUS" % get.installDIR())
|
||||
|
||||
'''/opt/ --> /sys/'''
|
||||
shelltools.copytree("%s/opt/" % get.installDIR(), "%s/sys/" % get.installDIR())
|
||||
|
||||
'''delete /sys/helloworld/helloworld'''
|
||||
shelltools.unlink("%s/sys/helloworld/helloworld" % get.installDIR())
|
||||
'''delete /sys/helloworld'''
|
||||
shelltools.unlinkDir("%s/sys/helloworld" % get.installDIR())
|
||||
|
||||
'''generate /usr/lib/helloworld.o'''
|
||||
libtools.gen_usr_ldscript("helloworld.o")
|
||||
Binary file not shown.
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
|
||||
<!DOCTYPE PISI SYSTEM "http://www.uludag.org.tr/projeler/pisi/pisi-spec.dtd">
|
||||
|
||||
<PISI>
|
||||
<Source>
|
||||
<Name>helloworld</Name>
|
||||
<Homepage>http://www.uludag.org.tr</Homepage>
|
||||
<Packager>
|
||||
<Name>S.Çağlar Onur</Name>
|
||||
<Email>caglar@uludag.org.tr</Email>
|
||||
</Packager>
|
||||
<License>GPL-2</License>
|
||||
<IsA>category</IsA>
|
||||
<PartOf>component</PartOf>
|
||||
<Summary xml:lang="en">Dummy HelloWorld for testing ActionsAPI</Summary>
|
||||
<Description xml:lang="en">Dummy HelloWorld for testing ActionsAPI</Description>
|
||||
<Archive type="targz" sha1sum="c8927baab2aae5fb4d1243067ca5244e3120f2ff">http://cekirdek.uludag.org.tr/~eray/hello-1.3.tar.gz</Archive>
|
||||
</Source>
|
||||
|
||||
<Package>
|
||||
<Name>helloworld</Name>
|
||||
<Files>
|
||||
<Path fileType="binary">/</Path>
|
||||
</Files>
|
||||
</Package>
|
||||
|
||||
<History>
|
||||
<Update release="1">
|
||||
<Date>2005-08-21</Date>
|
||||
<Version>0.1</Version>
|
||||
</Update>
|
||||
</History>
|
||||
</PISI>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
import pisi.installdb
|
||||
from pisi import util
|
||||
|
||||
import testcase
|
||||
class InstallDBTestCase(testcase.TestCase):
|
||||
|
||||
def testRemoveDummy(self):
|
||||
ctx.installdb.remove('installtest')
|
||||
self.assert_(not ctx.installdb.is_installed('installtest'))
|
||||
|
||||
def testInstall(self):
|
||||
ctx.installdb.purge('installtest')
|
||||
ctx.installdb.install('installtest', '0.1', '2', '3')
|
||||
|
||||
def testRemovePurge(self):
|
||||
ctx.installdb.install('installtest', '0.1', '2', '3')
|
||||
self.assert_(ctx.installdb.is_installed('installtest'))
|
||||
ctx.installdb.remove('installtest')
|
||||
self.assert_(ctx.installdb.is_removed('installtest'))
|
||||
ctx.installdb.purge('installtest')
|
||||
self.assert_(not ctx.installdb.is_recorded('installtest'))
|
||||
|
||||
suite = unittest.makeSuite(InstallDBTestCase)
|
||||
@@ -1,40 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import metadata
|
||||
from pisi import util
|
||||
|
||||
class MetaDataTestCase(unittest.TestCase):
|
||||
|
||||
def testRead(self):
|
||||
md = metadata.MetaData()
|
||||
md.read('tests/popt/metadata.xml')
|
||||
|
||||
self.assertEqual(md.package.license, ["As-Is"])
|
||||
|
||||
self.assertEqual(md.package.version, "1.7")
|
||||
|
||||
self.assertEqual(md.package.installedSize, 149691)
|
||||
return md
|
||||
|
||||
def testWrite(self):
|
||||
md = self.testRead()
|
||||
md.write('/tmp/metadata-test.xml')
|
||||
|
||||
def testVerify(self):
|
||||
md = self.testRead()
|
||||
if md.errors():
|
||||
self.fail("Couldn't verify!")
|
||||
|
||||
|
||||
suite = unittest.makeSuite(MetaDataTestCase)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user