Move pisi-1.1_rc4 tag

This commit is contained in:
Fatih Aşıcı
2009-10-27 10:13:46 +00:00
parent 3133576b88
commit c9c68b1fa7
226 changed files with 0 additions and 33754 deletions
-22
View File
@@ -1,22 +0,0 @@
Current maintainer
------------------
Faik Uygur <faik [at] pardus.org.tr>
Authors and contributors
------------------------
Ahmet Aygun <ahmet [at] pardusman.org>
A. Murat Eren <meren [at] pardus.org.tr>
Bahadır Kandemir <bahadir [at] pardus.org.tr>
Barış Metin <baris [at] pardus.org.tr>
Faik Uygur <faik [at] pardus.org.tr>
Furkan Duman <coderlord [at] gmail.com>
Görkem Çetin <gorkem [at] pardus.org.tr>
Gürer Özen <gurer [at] pardus.org.tr>
İsmail Dönmez <ismail [at] pardus.org.tr>
Onur Küçük <onur [at] pardus.org.tr>
S. Çağlar Onur <caglar [at] pardus.org.tr>
Inactive contributors
---------------------------------
Eray Özkural < erayo@cs.bilkent.edu.tr >
-150
View File
@@ -1,150 +0,0 @@
Like every serious project, there are guidelines.
"Coding Standards" for serious.
Guidelines
----------
0. Before reading any further please observe
PEP 8: Style Guide for Python Code
http://www.python.org/peps/pep-0008.html
In particular this means no lameCaps
1. When using dirnames, don't expect the dir to end
with a trailing slash, and please use the dirnames
in pisiconfig. Use util.join_path instead of os.path.join
2. Python indentation is usually 4 spaces.
3. Follow python philosophy of 'batteries included'
4. Use exceptions, don't return error codes
5. Don't make the PISI code have runtime dependencies on
a particular distribution (as much as possible).
6. Don't assume narrow use cases. Allow for a mediocre
amount of generalization in your code, for pieces that
will be required later.
7. If you are changing something, check if that change
breaks anything and fix breakage. For instance a
name. Running the tests is not always enough!
8. A good design ensures separation of concerns. Every module
has a specific documented responsibility. Don't make the
horse clean your windows.
9. To ensure readability avoid nesting python constructs
more than 3 levels deep. Python is a good language (unlike C),
so you can define inner functions in a convenient way, use
such decomposition techniques to break down your code into
manageable chunks. The worst code you can write is one huge
procedure that goes on for 1000 (or more) lines.
10. Use a particular abstraction like a class or function only
if it makes sense. Don't just define things because they can
be defined. Define only things that will/may be used.
11. If you are doing an expensive task like searching through
10000 text chunks, please use an efficient data structure
and algorithm. We are not MS engineers who know no data
structure beyond doubly linked lists and no algorithm beyond
quicksort.
12. Resist the temptation to develop kludges and workarounds in
response to pressure. Take your time to solve the problems by
the book. The payoff comes later.
13. Same thing goes for premature optimizations. Knuth and Dijkstra
are watching over your shoulder. :)
Branches and SVN
----------------
There are two branches of pisi, one is called pisi-devel and
new features that are large enough to cause instability go
into that branch. The trunk version is supposed to be stable at
all times. This means that you *must* run unit tests and other
test scripts after committing any change that cannot be tested
in isolation. Run the unit tests periodically to catch unseen
bugs. A release from the stable branch *must not* break any tests
whatsoever, so extensive use of the test suite must precede any
release.
Unit testing
------------
Unit tests are located in unittests directory. Running the tests is
trivial. But you must synchronize your code and data with the test
code, which can be a tedious work if you lose discipline.
Sample data files are located in the same directory with test modules.
For running the entire test suite, use the following command:
$ ./tests/run.py
The following command will run tests in specfiletests and archivetests
in unittests dir:
$ ./tests/run.py specfile archive
Do not depend on the output of unittests. Instead of producing an
output message/data in your tests, check the data internally. By
definition, unittest should just report succeeding and failing cases.
If you didn't, take a look at the links below for having an idea of
unit testing.
http://www.extremeprogramming.org/rules/unittests.html
http://www.extremeprogramming.org/rules/unittests2.html
Other tests
-----------
There are a couple of nice test scripts for testing the basic
capabilities of the command line interface such as building and
upgrading. Unlike unit tests, you have to take a look at the output
to understand that the scripts are doing well :)
Misc. Suggestions
-----------------
1. Demeter's Law
In OO programming, try to invoke Demeter's law.
One of the "rules" there is not directly accessing any
objects that are further than, 2/3 refs, away. So the
following code is OK.
destroy_system(a.system().name())
but the following isn't as robust
destroy_system(object_store.root().a.system.name())
As you can tell, this introduces too many implementation
dependencies. The rule of thumb is that, in these cases
this statement must have been elsewhere.... It may be a
good idea to not count the object scope in this case,
so in Python self.a means only one level of reference,
not two.
One quibble with this: it may be preferable not to insist
on this where it would be inefficient. So if everything
is neatly packed into one object contained in another
object, why replicate everything in the upper level? If
the semantics prevents dependency changes, then chains
of 3 or even 4 could be acceptable.
OTOH, in Python and C++, it's not always good to implement
accessor/modifier pairs for every property of an object.
It would be much simpler if you are not doing any special
processing on the property (e.g. if what the type system
does is sufficient).
The main rule of thumb in Demeter's Law is avoiding
putting more than, say, 10 methods in a class. That works
really well in practice, forcing refactoring every now
and then.
2. We all know, you're using LISP but didn't want to tell
us. Don't be scared, as a success story and for your encouragment
there are tens of people somewhere with LISP releated jobs.
3. If you are studying Data structures and Algorithms, and if
your first assignment is to implement a basic FIFO queue,
don't implement it. Just show your teacher the syntax of LISP,
tell him how beautiful it is, and show how an autistic person
can count lots of parenthesis with a "one second" look, you'll
probably get A+.
4. If you are interested in "Playstation 2 Linux Games Programming"
or "How to extend C programs with Guile", please don't exercise
your valuable skills in this project.
-346
View File
@@ -1,346 +0,0 @@
NOTE! The GPL below is copyrighted by the Free Software Foundation, but
the instance of code that it refers to (the kde programs) are copyrighted
by the authors who actually wrote it.
---------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
-16
View File
@@ -1,16 +0,0 @@
You can install PiSi on your system with a single command.
# ./setup.py install
If you are on a Pardus system, you should delete /usr/lib/pardus/pisi
when you are installing the SVN version
PiSi requires piksemel XML processing library, the newest version
bsd bindings called "bsddb3" and Pardus configuration manager COMAR
in order to run.
If you are upgrading from an old PiSi release you may have to
run the following command as root:
# pisi rebuild-db
-7
View File
@@ -1,7 +0,0 @@
recursive-include po *
recursive-include tests *.py
recursive-include tools *
recursive-include doc *
include . *.dtd
include . README NEWS CODING COPYING
-18
View File
@@ -1,18 +0,0 @@
PISI - Packages Installed Succesfully as Intended
PISI is a new package manager for the PARDUS
distribution. In Turkish PISI means "kitty", and
like a kitty, it is featureful and small.
Some of its distinctive features:
- Implemented in python
- Efficient and small
- Package sources are written in XML and python
- Uses LZMA for a better compression ratio
- Fast database access implemented with berkeley DB
- Integrates low-level and high-level package operations (dependency resolution)
- Framework approach to build applications and tools upon
- Comprehensive CLI and a user-friendly qt GUI (distributed separately)
- Extremely simple package construction
-5
View File
@@ -1,5 +0,0 @@
For upgrading from pisi 1.0 and before
You have to move the packages in /var/lib/pisi under /var/lib/pisi/package and remove /var/db/pisi/ directory.
db version has changed, so a rebuild-db will be unfortunately necessary.
-79
View File
@@ -1,79 +0,0 @@
% ALGORITHM STYLE -- Released 8 April 1996
% for LaTeX-2e
% Copyright -- 1994 Peter Williams
% E-mail Peter.Williams@dsto.defence.gov.au
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{algorithm}
\typeout{Document Style `algorithm' - floating environment}
\RequirePackage{float}
\RequirePackage{ifthen}
\newcommand{\ALG@within}{nothing}
\newboolean{ALG@within}
\setboolean{ALG@within}{false}
\newcommand{\ALG@floatstyle}{ruled}
\newcommand{\ALG@name}{Algorithm}
\newcommand{\listalgorithmname}{List of \ALG@name s}
% Declare Options
% first appearance
\DeclareOption{plain}{
\renewcommand{\ALG@floatstyle}{plain}
}
\DeclareOption{ruled}{
\renewcommand{\ALG@floatstyle}{ruled}
}
\DeclareOption{boxed}{
\renewcommand{\ALG@floatstyle}{boxed}
}
% then numbering convention
\DeclareOption{part}{
\renewcommand{\ALG@within}{part}
\setboolean{ALG@within}{true}
}
\DeclareOption{chapter}{
\renewcommand{\ALG@within}{chapter}
\setboolean{ALG@within}{true}
}
\DeclareOption{section}{
\renewcommand{\ALG@within}{section}
\setboolean{ALG@within}{true}
}
\DeclareOption{subsection}{
\renewcommand{\ALG@within}{subsection}
\setboolean{ALG@within}{true}
}
\DeclareOption{subsubsection}{
\renewcommand{\ALG@within}{subsubsection}
\setboolean{ALG@within}{true}
}
\DeclareOption{nothing}{
\renewcommand{\ALG@within}{nothing}
\setboolean{ALG@within}{true}
}
\DeclareOption*{\edef\ALG@name{\CurrentOption}}
% ALGORITHM
%
\ProcessOptions
\floatstyle{\ALG@floatstyle}
\ifthenelse{\boolean{ALG@within}}{
\ifthenelse{\equal{\ALG@within}{part}}
{\newfloat{algorithm}{htbp}{loa}[part]}{}
\ifthenelse{\equal{\ALG@within}{chapter}}
{\newfloat{algorithm}{htbp}{loa}[chapter]}{}
\ifthenelse{\equal{\ALG@within}{section}}
{\newfloat{algorithm}{htbp}{loa}[section]}{}
\ifthenelse{\equal{\ALG@within}{subsection}}
{\newfloat{algorithm}{htbp}{loa}[subsection]}{}
\ifthenelse{\equal{\ALG@within}{subsubsection}}
{\newfloat{algorithm}{htbp}{loa}[subsubsection]}{}
\ifthenelse{\equal{\ALG@within}{nothing}}
{\newfloat{algorithm}{htbp}{loa}}{}
}{
\newfloat{algorithm}{htbp}{loa}
}
\floatname{algorithm}{\ALG@name}
\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}}
-158
View File
@@ -1,158 +0,0 @@
% ALGORITHMIC STYLE -- Released 8 APRIL 1996
% for LaTeX version 2e
% Copyright -- 1994 Peter Williams
% E-mail PeterWilliams@dsto.defence.gov.au
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{algorithmic}
\typeout{Document Style `algorithmic' - environment}
%
\RequirePackage{ifthen}
\RequirePackage{calc}
\newboolean{ALC@noend}
\setboolean{ALC@noend}{false}
\newcounter{ALC@line}
\newcounter{ALC@rem}
\newlength{\ALC@tlm}
%
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
%
\ProcessOptions
%
% ALGORITHMIC
\newcommand{\algorithmicrequire}{\textbf{Require:}}
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
\newcommand{\algorithmiccomment}[1]{\{#1\}}
\newcommand{\algorithmicend}{\textbf{end}}
\newcommand{\algorithmicif}{\textbf{if}}
\newcommand{\algorithmicthen}{\textbf{then}}
\newcommand{\algorithmicelse}{\textbf{else}}
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
\newcommand{\algorithmicfor}{\textbf{for}}
\newcommand{\algorithmicforall}{\textbf{for all}}
\newcommand{\algorithmicdo}{\textbf{do}}
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
\newcommand{\algorithmicwhile}{\textbf{while}}
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
\newcommand{\algorithmicloop}{\textbf{loop}}
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
\newcommand{\algorithmicuntil}{\textbf{until}}
\def\ALC@item[#1]{%
\if@noparitem \@donoparitem
\else \if@inlabel \indent \par \fi
\ifhmode \unskip\unskip \par \fi
\if@newlist \if@nobreak \@nbitem \else
\addpenalty\@beginparpenalty
\addvspace\@topsep \addvspace{-\parskip}\fi
\else \addpenalty\@itempenalty \addvspace\itemsep
\fi
\global\@inlabeltrue
\fi
\everypar{\global\@minipagefalse\global\@newlistfalse
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
\penalty\z@ \fi
\everypar{}}\global\@nobreakfalse
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
\sbox\@tempboxa{\makelabel{#1}}%
\global\setbox\@labels
\hbox{\unhbox\@labels \hskip \itemindent
\hskip -\labelwidth \hskip -\ALC@tlm
\ifdim \wd\@tempboxa >\labelwidth
\box\@tempboxa
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
\hskip \ALC@tlm}\ignorespaces}
%
\newenvironment{algorithmic}[1][0]{
\let\@item\ALC@item
\newcommand{\ALC@lno}{%
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
{{\footnotesize \arabic{ALC@line}:}}{}%
}
\let\@listii\@listi
\let\@listiii\@listi
\let\@listiv\@listi
\let\@listv\@listi
\let\@listvi\@listi
\let\@listvii\@listi
\newenvironment{ALC@g}{
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
\listparindent\z@ \rightmargin\z@
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
\leftmargin 1em
\addtolength{\ALC@tlm}{\leftmargin}
}
}
{\end{list}}
\newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
{}{\ \algorithmiccomment{##1}}}
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
\newcommand{\ENSURE}{\item[\algorithmicensure]}
\newcommand{\STATE}{\ALC@it}
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
\renewcommand{\\}{\@centercr}
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\ELSIF}[2][default]%
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
\ALC@com{##1}\begin{ALC@for}}
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
\algorithmicdo%
\ALC@com{##1}\begin{ALC@for}}
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
\algorithmicdo%
\ALC@com{##1}\begin{ALC@whl}}
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
\ALC@com{##1}\begin{ALC@loop}}
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
\ALC@com{##1}\begin{ALC@rpt}}
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
\ifthenelse{\boolean{ALC@noend}}{
\newcommand{\ENDIF}{\end{ALC@if}}
\newcommand{\ENDFOR}{\end{ALC@for}}
\newcommand{\ENDWHILE}{\end{ALC@whl}}
\newcommand{\ENDLOOP}{\end{ALC@loop}}
}{
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
}
\renewcommand{\@toodeep}{}
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
\itemsep\z@ \itemindent\z@ \listparindent\z@%
\partopsep\z@ \parskip\z@ \parsep\z@%
\labelsep 0.5em \topsep 0.2em%
\ifthenelse{\equal{#1}{0}}
{\labelwidth 0.5em }
{\labelwidth 1.2em }
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
\ALC@tlm\labelsep
}
}
{\end{list}}
-167
View File
@@ -1,167 +0,0 @@
component'larla ilgili notlar
1. Temel feature'lar (Eray)
--------------------
Iki yerde component tag'i tanimladik simdiye kadar, bir
source'larda bir de binary'lerde. Binary'de tanimlanan component default
olarak source'daki tanimi inherit ediyor ve tanimlanan component'i override
edebiliyor.
Bir component temel olarak bir meta-package, icerisinde paketler olan
bir paket. Bir component'in icerisinde bir takim source'lar ve bir takim
binary'ler bulunuyor diye bakabiliriz. Sanirim query'leri bu sekilde yapmak
mumkun olmali.
$ pisi list-components
$ pisi info system.base
Source packages in system.base:
....
....
Binary packages in system.base:
....
....
gibi ozellikler eklemeyi umit ediyorum.
Source'larin component tag'leri de, gene sadece bir senaryo konusuyorum,
direkt olarak directory structure'indan inherit alinacak. Ayni zamanda o pspec
icin de bir tane component tanimlananack default olarak, ve bu component o
scope'da tanimlanmis olan butun binary package'lari icerecek.
Ornegin diyelim ki a/b/c/pspec.xml var ve c1 c2 c3 seklinde uc tane paket
tanimliyor. Hic bir component tanimi yapilmadigi zaman otomatik olarak bir
a.b.c component'i olusturulacak, ve bu component'in icerisinde c1 c2 c3
bulunacak.
$ pisi info a.b.c
Source packages:
c - oldur beni yarim sen olmazsan biterim
Binary packages:
c1 - bu aksam demlenmemek sonum olur benim
c2 - sincaplarla konustum butun gece
c3 - her gul gordugumde icim kan aglar
Bu varsayilan davranis, ama bunu degistirmek mumkun olacak. Burada yamuk
gozukebilecek bir sey var, o da tek bir paket oldugunda sanki biraz
redundancy olmasi, o takdirde bir optimizasyon olarak, diyelim ki
a/b/c/pspec.xml'in icerisinde tek bir paket tanimli c1
$ pisi info a.b
....
c1 - dil dil dillerdeyim
olabilir bu durumda, bu genel agac mantiginda bir sorun yaratmayacaktir.
2. Temel tanım (Barış)
fiziksel aitlik: kdebase'den çıkan kcontrol gibi; grup aitliği: pdf
göstericileri gibi...
3. Database XML ayrımı (Barış)
Component database'i ile component.xml ayrı olmalı. Component database,
pisi'nin pisi-index.xml dosyasını okuyarak oluşturacağı bir veritabanı. Hangi
paketler hangi componentlere dahil, vs. sorguları bu veritabanından
yapılacak.
Pspec dosyasını hatırlayalım. İçerisinde bir <PartOf> diye bir tag var.
Oluşturulacak paketin hangi component'e ait olduğunu belirtiyor. Bu bilgi
pisi-index.xml dosyasına da koyulmalı.
4. PL modüllerine benzerlik (Eray)
component tag'leri Java ya da python'daki gibi
directory yapisindan cikiyor. Yani pisi bir programlama dili olsaydi
component'lar module'ler ya da package'larla es anlamli olacakti.
5. Mereology ve Eray'ın açıklama çabaları (Eray)
PartOf iliskisi hakkında su kadarini soylemek yeter: eger a b'nin bir parcasiysa,
a'nin fonksiyonu b'nin fonksiyonunun bir parcasidir. Bu da fiziksel sistemler
icin bir principle of compositionality'nin varligindan hareket eder [*]
Genel olarak da software engineering ve AI camiasinda module'un tanimi gayet
iyi bilinir. Bir modul icerisindeki bagimliliklar yogundur. Moduller arasindaki
bagimliliklar zayiftir.
Bu tanim sadece software engineering'de degil, nesneler arasindaki
benzerliklerin incelendigi bir cok disiplinde kullanilan informal bir tanim,
ama tabii ki formule dokulmus bir ton hali var.Sırf bu tanımı taban alarak
yazılmış başarılı kümeleme (clustering) algoritmaları var.
Modulerlik tanimi verilen *fiziksel* modul ve parcasi olma iliskisiyle
birlesince birlikte install edilip remove edilme yahut ortak bağımlılıklara
sahip olma tanımlarına götürür.
Bu tanımı analiz edebilmek için parcasi olma iliskisininin anlamini
korumamiz yeterli. Temel olarak
  kol insanin parcasidir
iliskisi burada yer aliyor.  Eger
  a partof b
turu iliskilerde a paket ya da component, ve b component ise, a ve b'nin
iliskisinin kol ve insan iliskisi gibi olmasini bekleriz. Eger bu parca-butun
iliskisini ihlal ediyorsa o zaman muhtemelen yanlis bir iliski bulunmus
demektir.
Bunun turlu sonuclari da onem sirasina gore soyle dizilebilir:
1. Fonksiyonlarin bolunebilmesi prensibinden: (basit bir sonuç)
   a's function is part-of b's function
orneğin kolun fonksiyonu insanın total fonksiyonunun bir parçasıdır.
paket ornegi: pisi'nin fonksiyonu olan paket yükleme/çıkarma system.base'in fonksiyonunun, yani temel pardus sisteminin fonksiyonunun bir parçasıdır.
2. Karmasik sistemlerde birbirine dayanan ufak parcalarin kararli yapilar
meydana getirmesi prensibinden: (evrimsel sonuç)
   if a is a part-of c, and b is a part-of c, then it follows that a and b
may:
     a. have many interdependencies
     b. share in their origins
  (which are about the same thing)
2b. sonucunun bizim durumumuza uygulanması, source code'un aynı kaynaktan
çıkması, birlikte inşa edilmeleri gibi şartları getirir. Bunun oldukça
olasi, en azindan insa metodlarinin ve kaynaklarının birbirine benzemesini
bekleriz. Yalniz, 2.a'daki bagimliliklar sadece insa ile degil ayni zamanda
calisma ile de alakalidir. Kaynakları farklı parçaların birbirine bağımlı
hale gelebilecegini unutmamak gerekir.
Fedora'nin yaklasiminin hos tarafi, boyle teknik ayrintilara girmeden "package
group" mantigini kullanmasiydi, ama bizim belli bir anlami olan parca-butun
iliskisini korumamiz daha mantikli bir yazilim ontology'si ortaya
cikaracaktir. Onlarin yaklasimi ise "anything goes", grup ile kategori'nin
temel bir farki yok cunku, herhangi bir bakis acisi olabilir "grup".
6. Modulleri test etmek (!!) (Eray)
------------------------------------
PISI'deki seçilen implementation detaylari bir tanima cok fazla commitment
yapmiyor, implementation'ın getirdiği tek şey paketleri bir ağaca koymak.
Component'ların seçiminin ne kadar kaliteli olduğunu belirleyemiyor.
Paketlerin ve componentlarin secimi yapiyor, ki kritik olan o.
Yalnız modulerlik tanimindan ve part-of iliskisinin
  if a is part of b, and b is part of c, then a is part of c.
  if a is part of b, and b is not part of c, then a is not part of c.
gibi sonuclari getirmesinden hareketle (ki bunlar klasik computational
ontology) kismen test edebilecegimiz bir sekil aliyor ornegin bagimlilik
graph'ini cluster ederek, ya da her modul icin bir modulerlik sayisi
hesaplayarak. Daha az formal olarak da bu sonuçları kafamızda yürüterek
yaptığımız componentların ne kadar akla yatkın olduğunu bulabiliriz.
7. Cagların frugalware önerisi
------------------------------
http://ftp.frugalware.org/pub/frugalware/frugalware-current/source/
adresindeki yerleşimin hem source hem de binary depo için uygulanmasını ve
kategori, componentların da bundan çıkartılmasını öneriyorum.
Binary file not shown.
-381
View File
@@ -1,381 +0,0 @@
%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:
\documentclass[a4paper,11pt]{article}
\usepackage{graphicx}
\usepackage{algorithm}
\usepackage{algorithmic}
\usepackage{amsmath}
\usepackage{amstext}
\usepackage{amsfonts}
\usepackage{amsbsy}
\usepackage{amsthm}
\usepackage{prettyref}
%\newrefformat{alg}{Algorithm~\ref{#1}}
%\newrefformat{eq}{Equation~\ref{#1}}
%\newrefformat{lem}{Lemma~\ref{#1}}
%\newrefformat{thm}{Theorem~\ref{#1}}
%\newrefformat{chp}{Chapter~\ref{#1}}
%\newrefformat{sec}{Section~\ref{#1}}
%\newrefformat{apx}{Appendix~\ref{#1}}
%\newrefformat{tab}{Table~\ref{#1}}
%\newrefformat{fig}{Figure~\ref{#1}}
%usepackage[active]{srcltx}
\title{ Dependency Resolution in PISI}
\author{Eray \"{O}zkural}
\date{\today}
\begin{document}
\maketitle
\section{Introduction}
Dependency resolution in package management systems have a
significance in that they are the key to providing system stability
and internet upgrades. The scale of package databases requires the
dependency resolution mechanism to be efficient and correct,
motivating a closer look at the theory.
\section{Review}
Dependency resolution has been taken in the most general setting as
the famous SAT problem of propositional logic. If we consider a system
$D$ of dependency statements $D_i$, each statement can be taken as a
proposition in propositional logic which states, for instance:
$D_i$: if package $a$ is installed or package $b$ is installed, then
package $i$ is installable.\\
...
The system is thus understood as the conjunction of such facts, giving
us a logical programming formulation to determine installation
conditions. Note that for simplicity we do not consider the nuances in
upgrade and remove operations at the moment.
However, using a SAT solver for this operation may be shooting a fly
with a bazooka. We observe that only certain forms of propositions
will be necessary for a dependency system. Furthermore, as we shall
see further constraints and optimizations may be required of the
system that are not modelled well with the SAT problem.
We use a graph theoretic approach instead. A directed graph (digraph)
$G=(V,E)$ is formally a set of vertices $V$ and a set of edges $E$
where each edge $(u,v)$ represents an edge from a vertex to another.
Accessor functions $V(G)$ and $E(G)$ yield the vertex and edge set of
the graph $G$. Topological sort of a graph gives a total ordering of
the vertices in which there are only forward edges. A vertex induced
subgraph of $G$ by vertex set $A$ contains only the vertex set $A$ and
edges incident to members of $A$.
\section{Package operation planning}
The dependency resolution problem may be viewed as a simple forward
chaining problem, where we would like to begin from an initial state
$S_0$ and by following allowable system transitions $t_i: S \to S$,
arrive at a desired system state $S_f$ (where $S$ is the set of all states).
A system state $S_i$ is defined as the set of installed packages on
the system together with their versions, i.e. $S_i = \{ (x,v) : x
\text{ is installed}, v=version(x)\} $. An atomic system transition
$t_i$ chains one system state into another, making one ACID change on
the system. The usual atomic transitions are the single package
install, remove and reinstall (upgrade or downgrade) operations found
in low-level package management code of PISI. Note that in PISI, an
upgrade operation is identical to a remove operation followed by an
install operation (which sets it apart from some other packaging
systems).
A package operation plan is thus naturally conceived of as a sequence
of atomic system transitions. Given an initial state and a final
state, the job of the package operation planner is to determine
whether there is a plan, and if so find the "best" one.
Where there are no versions involved (e.g. upgrade/downgrade), we will
replace the pair $(x,v)$ with $x$ in the definitions for simplicity.
\subsection{System consistency}
It is worth mentioning here the concept of system consistency. As in a
database transaction, it is not acceptable that the system violates an
invariant afterwards. In the context of PISI, system consistency is
composed of two conditions for the current set of installed packages.
\begin{enumerate}
\item All package dependencies are satisfied (we may call this a
closed system)
\item No package conflicts are present.
\end{enumerate}
Therefore, by atomic transition we also mean one that does not corrupt
system consistency. The system is thus never in an inconsistent
state. We will explain the conflicts later, for the present let us
look at the dependency condition.
\subsection{Solving the simplest case with topological sorting}
We will now concentrate on a simple form of the problem which can be
solved with topological sorting. This form is not concerned with
versions. Neither do we consider remote repositories. From an initial
set of packages $S_0$, we would like to install in addition a new set
$A$ of packages obtaining $S_f = S_0 \cup A$, for a static set of package
relations.
The only relations considered are of the form: $a$ Depends on $b$, or
more briefly $aDb$. The graph of all such simple dependency relations
is a digraph $G$. For each dependency relation $aDb$, there is an
edge $a \to b$ in $G$. Accessing graph $G$ usually requires a database
operation and is therefore expensive.
We now consider the digraph $G_A$ of the minimal set of simple
dependency relations which contains all information required to
construct a plan to install packages $A$. $G_A$ is a vertex induced
graph such that the fringe of $A$, e.g. vertices with out-degree $0$
depend only on packages that are already installed (or none). Vertices
of $G_A$ are taken from $S_f$. First, let us explain the labelling
scheme. Already installed vertices are labelled with 'i'. Packages to
be added are labelled with 'a', and packages to be installed due to
dependencies are labelled with 'd'. We construct the graph as follows
\begin{algorithm}
\caption{$\textsc{Make-}G_A(G, A)$}
\label{alg:cons-graph}
\begin{algorithmic}[1]
\STATE $G_A \gets$ vertex induced subgraph of $G$ by $A$ labelled with 'a'
\REPEAT
\STATE done $\gets$ true
\FOR{each $u \in V(G_A)$ with out-degree $0$}
\FOR{ $v \in adj(u) $ of $G$}
\IF{$v \notin V(G_A)$}
\STATE done $\gets$ false
\IF{$v$ is installed}
\STATE label $v$ with 'i'
\ELSE
\STATE label $v$ with 'd'
\ENDIF
\STATE add $(u,v)$ to $G_A$
\ENDIF
\ENDFOR
\ENDFOR
\UNTIL{done}
\end{algorithmic}
\end{algorithm}
By this iterative expansion, we do a minimum number of database
accesses to $G$ and construct a dependency graph in memory. If the
$G_A$'s fringe has vertices with non 'i'-labels, then $A$ cannot be
installed. Otherwise, we find a topological sort $L$ of $G_A$, and in
the reverse order, install packages for vertices labelled with
'a' or 'd'. Observe that, by definition of a topological sort,
installing packages in the reverse order of a topological sort
guarantees that no package is installed before all of its dependencies
are installed. Thus, this yields a consistency-preserving plan.
\subsection{Dependency conditions}
In the PISI specification, we allow a dependency to specify a local
condition, for instance a program may require a dependency on
\texttt{libx} with pardus source release $3$ or greater. Another
program may require a dependency on a particular source release. These
conditions are local because they can be computed over the elements of
system state $S_i$, e.g. package (name, version) pairs. Let us denote this
condition by a predicate $P(b)$ such that $aDb$ iff $P(b)$. The
predicate $P$ for the dependency $aDb$ can be stored as edge data for
$(u,v)$ on the graph.
% thus when we say $P(u,v)$, this means the predicate stored on
%$(u,v)$ edge for vertex $v$.
In this case, the vertices of the package dependency graph $G$ and the
planning graph $G_A$ retain the version information along with the
package name. The dependency relation thus holds between two pairs
$(p_1,v_1)$ and $(p_2,v_2)$, satisfying a given predicate
$P(p_2,v_2)$. When constructing the graph, we therefore take this
predicate into account and admit a new edge $(u,v)$, and thus a new
vertex $v$ into $G_A$ if and only if the target vertex satisfies
$P(v)$.
\subsection{Conflicts and COMAR dependencies}
The tags \texttt{Conflicts} and \texttt{Provides} in PISI are
inherited from Debian distribution. A conflict between two packages
($a$ conflicts with $b$) is a symmetric relation that prevents the
packages $a,b$ from being installed simultaneously (It is
sufficient that only one direction of the relation is declared, the
other direction is inferred). Provision in the form of $a$ provides $A$
denotes that $a$ implements a virtual package abstraction $A$.
In PISI, a package can provide an object of a COMAR Object Model (OM),
and is currently the only model of a ``virtual package''. In the
following example, let $a_1,a_2,\ldots,a_n$ provide the OM $A$. A package
can depend on another package's OM, for instance $b$ comar-depends on
$A$ (or in short form $bDA$) (Currently, conditions on virtual
dependencies are not supported). In this case, it is sufficient that
only one of the $a_i$ are installed. To resolve this, the user is
asked to choose from a list of alternatives immediately, since
otherwise there is unavoidable combinatorial explosion (in the form of
having to consider $\Pi_{bDA}num(A)$ graphs in the worst case where
$num(A)$ is the number of alternatives for comar OM $A$; the problem
is that there seems to be no simple solution to solve satisfiability
with arbitrary disjunctions in package dependency, short of a $SAT$
solver).
The resolution of conflicts to maintain system consistency condition
$2$ is easier to achieve. This can be satisfied by disallowing
installation of a package that would violate the condition, or
removing currently installed packages which conflict with the newly
installed package and its dependencies. In most package managers, the
second option is confirmed by the user for making it easier. In the
install operation, after constructing the partial dependency graph
$G_A$, we merely have to check whether any conflict appears among the
vertices of $G_A$. If so, then the operation is untenable, since $G_A$
shows the future state of the installed system. Since a conflict is
symmetric, it is represented as a bidirectional edge $a \leftrightarrow b$. To
distinguish dependencies from conflicts, the edges would have to be
labelled in this case, for instance with 'd' and 'c'. The removal
option can be implemented by invoking a multi-package remove operation
on the packages in conflict.
\subsection{Remove operation}
Dependency resolution for remove operation is similar to install. The
only significant difference is that we remove the packages in the
topological order rather than installing packages in the reverse
topological order.
\section{Remote repositories and upgrade operation}
The upgrade operation is more complicated. First of all, the system
has to distinguish between the current relation graph (e.g.
dependencies and conflicts), and the future relation graph which may
be different in rather important aspects. In theory, we allow any
dependency and conflict to change. Therefore, we have a $G_0$ which
represents the current relations (among installed packages) in the
system, and a $G_f$ which is probably taken from a remote package
repository. We begin by noting that $G_0$ and $G_f$ have to be
compatible. That is, to say, if a package $(p_1,v_1)$ is shared across
two graphs, then the declarations made by the package are one and the
same.
$G_0$ can be calculated from the package information (e.g. metadata)
of the installed packages and is stored by PISI in a dedicated
database. $G_f$ is most likely constructed from a PISI Index file
corresponding to a particular package repository. Accessing both of
these entities is expensive and we should take care to minimize access
as in the previous section.
To preserve consistency during individual transitions, the planner can
choose to remove a minimum number of packages from the system to bring
it to a clean state, and then install the new versions of these
packages in the correct order. Let us assume that it is indeed
possible to achieve this ``clean state''. Apparently, this is not
always possible because other packages may depend on the package(s) to
upgrade. At any rate, to achieve this, first we need to
calculate subgraphs of $G_0$ and $G_f$. We can calculate alternative
plans from these subgraphs if need be.
Let $A$ be the set of packages to be upgraded from a given repository.
$G_{A,0}$ is the subgraph of $G_{0}$ induced by the ``upgrade
closure'' of $A$. The ``upgrade closure'' of a set $A$ of packages is
defined as a minimal set of packages $B \supseteq A$ such that there is no
package in $B$ that requires an upgrade for $A$ to be upgraded. This
is found by assuming that the current system state $S_0$ is
consistent, and by constructing a relation graph of the future state
of the system to detect the dependencies that have changed.
Obviously, to make a plan, we must first know the goal state. In a
multi-package upgrade, the exact details of the goal state depend on
the graph $G_f$ of the repository. Thus, we construct a graph
$G_{A,f}$ that is a vertex-induced subgraph of $G_f$ such that it
contains all information relevant to upgrading packages $A$. We begin
by a vertex induced subgraph of $G_f$ by $A$. These are the packages
that will be upgraded in any case. Then, we make a pass on the
vertices, and look at all the outgoing edges, we compare whether this
edge has changed in any substantial way from the previous version. In
particular, we are interested in whether the predicate of the edge is
valid for the version of the same package in our current system. Every
compared vertex in this manner is marked done, and the edges not valid
for the current system pull new unmarked vertices into $G_f$, this
continues until there are no unmarked vertices left. Hence, the
vertices of $G_f$ are the packages that must be upgraded.
To actually carry out the upgrade a strategy is to upgrade all the
packages in $G_{A,f}$ in some order. A good order is again the reverse
topological order order, in fact, the upgrade operation is merely a
special case of a multi-package installation code that can install
from a remote repository, since a multi-package installation can
contain upgrades in addition to new packages. However, in case no
package depends on the packages to be upgraded, then we can carry out
a completely consistency-preserving plan as discussed above. The
conflicts are resolved in the usual fashion, by removing those
packages in conflict with new packages that are installed. This can be
accomplished by invoking a remove operation prior to the upgrade
operation.
\section{Examples}
\subsection{A single package upgrade}
goal: upgrade $(a,1)$ to $(a,2)$\\
\\
rules:\\
$(a,1)$ depends on $(b,1), (c,1)$ \\
$(a,1)$ conflicts with $(d,1)$\\
$(a,2)$ depends on $(c,3), (d,2)$\\
$(a,2)$ conflicts with $(b,1)$\\
\\
initial state:\\
$(a,1), (b,1), (c,1)$ installed \\
In this case, we can find a consistency-preserving plan in terms of
install and remove operations.
\\
plan:\\
remove $(a,1)$\\
remove $(b,1)$\\
remove $(c,1)$\\
install $(c,3)$\\
install $(d,2)$\\
install $(a,2)$\\
\subsection{Another upgrade}
goal: upgrade $(b,1) \to (b,2)$\\
\\
current dep: $(a,1) \to[=1] (b,1) \to[=1] (c,1) \to[=1] (d,1)$\\
repo dep: \nobreakspace{} \nobreakspace$(a,1) \to[=2] (b,2) \to[=2] (c,2) \to[=1] (d,1)$\\
In this case, we cannot remove $(b,1)$ because it's locked in the
chain. In fact, here there is no consistency-preserving plan in terms
of atomic single package transitions: install, remove, upgrade. In
these cases, it seems best to resort to upgrade in place, and in the
reverse topological order of dependencies.
\\
plan:\\
upgrade $(c,1) \to (c,2)$\\
upgrade $(b,1) \to (b,2)$\\
\subsection{A multi package remove}
goal: remove $(a,2), (b,3), (c,2)$\\
\\
rules:\\
$(c,2)$ depends $(a,2)$\\
$(d,2)$ depends on $(b,3), (c,2)$\\
$(e,1)$ depends on $(a,2)$\\
$(f,2)$ depends on $(e,1)$\\
$(g,2)$ depends on $(e,1)$\\\\
plan:\\
remove $(f,2)$\\
remove $(g,2)$\\
remove $(e,1)$\\
remove $(d,2)$\\
remove $(b,3)$\\
remove $(c,2)$\\
remove $(a,2)$\\
\end{document}
Binary file not shown.
Binary file not shown.
-187
View File
@@ -1,187 +0,0 @@
\documentclass[a4paper,11pt]{article}
\title{P\.IS\.I Packages: Version Policy v0.2}
\date{\today}
\author{Eray \"Ozkural and T. Bar\i{}\c s Metin}
\begin{document}
\maketitle
\section*{Revision History}
\begin{itemize}
\item v0.1: Bar\i\c s Metin wrote the first version preparing the outline,
detailed Source Version Section, and started the Section on Release Number.
\item v0.2: Eray \"Ozkural wrote a detailed introduction, added
explanations of release and build numbers, reorganized a bit.
\end{itemize}
\section{Introduction}
This document explains the \emph{version policy} that applies to
P\.IS\.I packages. Classically, the issue of distinguishing source and
binary distributions unambiguously has not received a rigorous
treatment in the context of LINUX distributions. We have identified
several shortcomings of the usual practices of extending the original
version with suffixes and prefixes, colorfully illustrated in the
following common problems.
\begin{description}
\item[The problem of future downgrades]
The distribution chooses to use a previous version of the package in
the next release. There is no way to indicate this, so ad-hoc
solutions such as version prefixes are used. It is
impossible to denote a future dependency that requires at least this
distribution source release in this case, either.
\item[The problem of redundant distributions]
A trivial patch has been applied to the source. While few binary
packages have been affected by this change, all binary packages
built from the source are redistributed.
\item[The problem of underdetermined rebuilds]
There have been rapid changes in the system, and although no
changes have been made to the package source, a new binary
distribution must be prepared.
\end{description}
We have devised a slightly new approach in order to alleviate these
problems. Our solution consists of encoding the history of source and
binary package developments in separate version strings we call release and
build numbers.
Since the source version is usually used by the users and developers
to identify software, we retain the notion of a source version in
P\.IS\.I as a convenience.
In the following sections, we explain the components of our
versioning scheme.
\subsection{Source Version}
Source version is the version number provided by the
upstream maintainer of the source archive used in package. It must
always be the same as the upstream version used.
\textbf{Example}: If the upstream archive name is
\emph{bash-3.0.tar.gz} the version number of the package is \emph{3.0}
\subsubsection{Version Suffixes}
There is a pre-defined list of suffixes a package version can
take.
\begin{itemize}
\item \textbf{alpha} Source/Package is in alpha state
\item \textbf{beta} Source/Package is in beta state
\item \textbf{pre} Source/Pacgage passed the beta state but stable
version is not relased yet.
\item \textbf{rc} Source/Package is a release-candidate.
\item \textbf{m} Source/Package is a milestone before stable version.
\item \textbf{p} Source/Package is released and some patches are
applied after the release. This is the patch level.
\end{itemize}
The suffix should be written after the special separator
character \textbf{\_}. And there must allways be a number after a
suffix. \textbf{Example}: packagename-1.0\_beta1
The basic order of the priorities for suffixes is:\newline
\emph{p $>$ (no suffix) $>$ m $>$ rc $>$ pre $>$ beta $>$ alpha}.
The scope of a source version string is global in the literal
sense. It shall not vary from repository to repository.
The support for these special suffixes as well as usual alphanumeric
version string ordering has been implemented in P\.IS\.I.
\section{Identifying Package Sources}
A P\.IS\.I source has three identity elements written under
\texttt{SOURCE} tag: name, source version, and source release number.
We usually say just version and release number/release instead of
source version and source release number, respectively. Name is available in
the \texttt{<Name>} tag. Version and release are available in the last
\texttt{<Update>} element of \texttt{<History>} tag of a \texttt{PSPEC}.
The name of a source package is constant throughout its revision
history. The version is the original version, given by its
programmers. Release is a positive integer. Name and release
is sufficient to uniquely identify a particular PISI source revision.
That is, version and release are independent.
\subsection{Release Number}
Release number is the number of the changes that are made to the
package source since the initial version in the distribution source. A
change can be a patch applied to the source archive, modification in
the actions.py, pspec.xml or any file in the source package
directory. This change is indicated in \texttt{<Update>} tags manually
by the package maintainer.
The initial release of a package is by default \texttt{1}. The release
number always increments by $1$ in each revision in the
\texttt{History}, even the slightest ones, but it never decrements.
The scope of the release number is a given distribution, regardless of
its version, e.g. Pardus.
In the future, PISI will have strict checks for release numbers.
\subsection{Dependency Specifications}
We allow a package to use both source version and release to identify
a particular version or a range of package versions.
\section{Identifying Binary packages}
A PISI binary package is produced from a PISI source package. It has a
name that is constant throughout the history of the source package,
and it inherits the source version and release number from the source
package. However, a binary package has in addition a binary build
number. Shortly, build number or just build. For each of the
architecture targets, e.g. particular binaries, it also has an
architecture tag.
A binary package is uniquely identified by its name, build number, and
architecture regardless of the source version.
\section{Build Number}
Similarly to source release number, binary build number is the number
of changes that are made to a binary package. By change, we mean any
bit change. The existence of a change is tested by comparing the
cryptographic checksums in files.xml with those of the previous build, and the
build number is automatically determined by the P\.IS\.I build system.
The build number starts from $1$ as in release number, and increments
by one with each binary change.
The user never interferes with the build number himself. However, if
the user fails to provide the previous build, then a package without
a build number is built. A package without a build number is evaluated
on the basis of release number, which is guaranteed to exist.
The scope of a build number is a given distribution build environment
for a particular architecture, which may vary from repository to
repository. Therefore, it is not used in dependency
specifications. However, the system does assume that a build of a
given package and architecture is unique in a given repository.
\section{Package File Names}
A P\.IS\.I binary package file name contains all the components relevant
to its identification, separated by dashes:
\begin{verbatim}
<binary name>-<source version>-<source release>-<binary build>.pisi
\end{verbatim}
\section{Future Work}
In the future, it may be necessary to extend the notion of release
number and build number to support branches and forks of a
distribution. A proposal was to have CVS-like branching, but it
was dismissed as unnecessary.
\end{document}
-532
View File
@@ -1,532 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<XMI xmlns:UML="http://schema.omg.org/spec/UML/1.3" verified="false" timestamp="2006-09-29T21:06:38" xmi.version="1.2" >
<XMI.header>
<XMI.documentation>
<XMI.exporter>umbrello uml modeller http://uml.sf.net</XMI.exporter>
<XMI.exporterVersion>1.5.4</XMI.exporterVersion>
<XMI.exporterEncoding>UnicodeUTF8</XMI.exporterEncoding>
</XMI.documentation>
<XMI.metamodel xmi.name="UML" href="UML.xml" xmi.version="1.3" />
</XMI.header>
<XMI.content>
<UML:Model isSpecification="false" isLeaf="false" isRoot="false" xmi.id="m1" isAbstract="false" name="UML Model" >
<UML:Namespace.ownedElement>
<UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="3" isRoot="false" isAbstract="false" name="datatype" />
<UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="108" isRoot="false" isAbstract="false" name="enum" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="2" isRoot="false" isAbstract="false" name="int" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="4" isRoot="false" isAbstract="false" name="char" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="5" isRoot="false" isAbstract="false" name="bool" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="6" isRoot="false" isAbstract="false" name="float" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="7" isRoot="false" isAbstract="false" name="double" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="8" isRoot="false" isAbstract="false" name="short" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="9" isRoot="false" isAbstract="false" name="long" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="10" isRoot="false" isAbstract="false" name="unsigned int" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="11" isRoot="false" isAbstract="false" name="unsigned short" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="12" isRoot="false" isAbstract="false" name="unsigned long" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="13" isRoot="false" isAbstract="false" name="string" />
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="89" isRoot="false" isAbstract="false" name="data" />
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="24" isRoot="false" isAbstract="false" name="InstallDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="25" type="76" name="d" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="77" type="76" name="db" />
</UML:Classifier.feature>
<UML:Namespace.ownedElement>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="24" xmi.id="14" isRoot="false" isAbstract="false" name="InstallInfo" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="79" type="13" name="state" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="80" type="13" name="version" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="81" type="13" name="release" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="82" type="13" name="build" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="83" type="13" name="distribution" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="84" type="13" name="time" />
</UML:Classifier.feature>
</UML:Class>
</UML:Namespace.ownedElement>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="76" isRoot="false" isAbstract="false" name="LockedDBShelf" />
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="86" isRoot="false" isAbstract="false" name="RepoDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="87" type="76" name="d" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="98" isRoot="false" isAbstract="false" name="Repo" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="99" type="13" name="indexuri" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="111" isRoot="false" isAbstract="false" name="FilesDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="112" type="76" name="d" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="114" isRoot="false" isAbstract="false" name="FileInfo" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="115" type="13" name="path" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="116" type="13" name="type" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="117" type="9" name="size" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="118" type="13" name="hash" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="119" type="5" name="permanent" />
</UML:Classifier.feature>
</UML:Class>
<UML:Enumeration stereotype="108" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="141" isRoot="false" isAbstract="false" name="repo" >
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="142" isRoot="false" isAbstract="false" name="installed" />
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="143" isRoot="false" isAbstract="false" name="thirdparty" />
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="144" isRoot="false" isAbstract="false" name="repos" />
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="145" isRoot="false" isAbstract="false" name="all" />
</UML:Enumeration>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="146" isRoot="false" isAbstract="false" name="ItemByRepo" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="147" type="76" name="d" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="155" isRoot="false" isAbstract="false" name="ComponentDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="156" type="146" name="d" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="163" isRoot="false" isAbstract="false" name="Component" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="164" type="13" name="name" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="165" type="13" name="localname" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="166" type="13" name="summary" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="167" type="13" name="description" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="168" type="177" name="dependencies" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="169" type="177" name="packages" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="170" type="177" name="sources" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="177" isRoot="false" isAbstract="false" name="list" />
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="186" isRoot="false" isAbstract="false" name="PackageDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="187" type="146" name="d" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="188" type="146" name="dr" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="190" isRoot="false" isAbstract="false" name="Package" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="191" type="13" name="name" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="192" type="13" name="summary" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="193" type="13" name="description" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="194" type="13" name="partof" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="195" type="13" name="license" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="196" type="177" name="packageDependencies" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="197" type="177" name="componentDependencies" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="198" type="177" name="files" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="199" type="177" name="conflicts" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="200" type="177" name="providesComar" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="201" type="177" name="additionalFiles" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="202" type="177" name="history" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="211" isRoot="false" isAbstract="false" name="SourceDB" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="212" type="146" name="d" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="213" type="146" name="dpkgtosrc" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="215" isRoot="false" isAbstract="false" name="SpecFile" >
<UML:Classifier.feature>
<UML:Attribute isSpecification="false" visibility="private" xmi.id="216" type="217" name="source" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="218" type="177" name="packages" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="219" type="177" name="history" />
<UML:Attribute isSpecification="false" visibility="private" xmi.id="221" type="177" name="components" />
</UML:Classifier.feature>
</UML:Class>
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="217" isRoot="false" isAbstract="false" name="Source" />
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="220" isRoot="false" isAbstract="false" name="History" />
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="26" name="" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="27" aggregation="none" type="24" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="28" aggregation="none" type="14" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="35" name="" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="36" aggregation="none" type="24" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="37" aggregation="none" type="14" name="" multiplicity="n" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="40" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="41" aggregation="none" type="24" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="42" aggregation="none" type="14" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="64" name="" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="65" aggregation="none" type="24" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="66" aggregation="none" type="14" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="73" name="" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="74" aggregation="none" type="24" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="75" aggregation="none" type="14" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="90" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="91" aggregation="composite" type="24" name="name" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="92" aggregation="none" type="14" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="100" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="101" aggregation="composite" type="86" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="102" aggregation="none" type="98" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="120" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="121" aggregation="composite" type="111" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="122" aggregation="none" type="114" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Dependency isSpecification="false" visibility="public" namespace="m1" xmi.id="149" client="146" name="" supplier="141" />
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="171" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="172" aggregation="composite" type="155" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="173" aggregation="none" type="163" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="203" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="204" aggregation="composite" type="186" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="205" aggregation="none" type="190" name="" />
</UML:Association.connection>
</UML:Association>
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="222" name="1..*" >
<UML:Association.connection>
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="223" aggregation="composite" type="211" name="" />
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="224" aggregation="none" type="215" name="" />
</UML:Association.connection>
</UML:Association>
</UML:Namespace.ownedElement>
</UML:Model>
</XMI.content>
<XMI.extensions xmi.extender="umbrello" >
<docsettings viewid="209" documentation="" uniqueid="234" />
<diagrams>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="1" documentation="" type="402" showops="1" showpackage="0" name="InstallDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<classwidget usesdiagramfillcolour="0" width="142" showattsigs="601" usesdiagramusefillcolour="0" x="543" y="273" showopsigs="601" linewidth="none" fillcolour="#ffffc0" height="135" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="14" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="#ff0000" />
<classwidget usesdiagramfillcolour="0" width="204" showattsigs="601" usesdiagramusefillcolour="0" x="35" y="355" showopsigs="601" linewidth="none" fillcolour="#ffffc0" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="24" showoperations="1" showpackage="1" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="#ff0000" />
<notewidget usesdiagramfillcolour="1" width="413" usesdiagramusefillcolour="1" x="27" y="149" linewidth="none" fillcolour="none" height="185" usefillcolor="1" isinstance="0" xmi.id="46" showstereotype="1" text="@d: is a bsddb dict which holds package related data.
key:&quot;package name&quot; value:InstallInfo
&quot;/var/db/pisi/install.bdb&quot;
@dp: is a bsddb dict which holds pending packages data.
key:&quot;package name&quot; value:bool
&quot;/var/db/pisi/configpending.bdb&quot;
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="411" usesdiagramusefillcolour="1" x="15" y="14" linewidth="none" fillcolour="none" height="113" usefillcolor="1" isinstance="0" xmi.id="47" text="installdb.py
The main purpose of the installdb is to hold the packages' version and status infos. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="313" usesdiagramusefillcolour="1" x="678" y="50" linewidth="none" fillcolour="none" height="245" usefillcolor="1" isinstance="0" xmi.id="49" showstereotype="1" text="@state: holds the status of the package
'i' : 'installed'
'ip' : 'installed-pending'
'r' : 'removed'
'p' : 'purged'
@version: version of the package
@release: release number of the package
@build: build number of the package
@distribution: distribution of the package. (exp: &quot;Pardus&quot;)
@time: the time when the package is installed
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="14" widgetaid="24" xmi.id="90" linecolor="none" >
<linepath>
<startpoint startx="239" starty="386" />
<endpoint endx="543" endy="340" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="391" y="363" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="227" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<floatingtext usesdiagramfillcolour="1" width="56" usesdiagramusefillcolour="1" x="241" y="362" linewidth="none" posttext="" role="709" fillcolour="none" height="22" usefillcolor="1" pretext="+" isinstance="0" xmi.id="228" text="name" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="52" documentation="" type="402" showops="1" showpackage="0" name="RepoDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="13" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="54" text="repodb.py
The main purpose of the repodb is to hold the repositories' infos (currently we only have the repository's url).
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="35" y="299" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="86" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="403" usesdiagramusefillcolour="1" x="22" y="165" linewidth="none" fillcolour="none" height="101" usefillcolor="1" isinstance="0" xmi.id="88" showstereotype="1" text="@d: is a bsddb dict which holds repositories' infos
key:&quot;repository name&quot; value:Repo
&quot;/var/db/pisi/repodb.bdb&quot;
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="121" showattsigs="601" usesdiagramusefillcolour="1" x="538" y="259" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="98" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="243" usesdiagramusefillcolour="1" x="615" y="211" linewidth="none" fillcolour="none" height="58" usefillcolor="1" isinstance="0" xmi.id="106" showstereotype="1" text="@indexuri: url of the repository. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="98" widgetaid="86" xmi.id="100" linecolor="none" >
<linepath>
<startpoint startx="174" starty="321" />
<endpoint endx="538" endy="281" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="356" y="301" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="229" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="109" documentation="" type="402" showops="1" showpackage="0" name="FilesDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<notewidget usesdiagramfillcolour="1" width="404" usesdiagramusefillcolour="1" x="14" y="14" linewidth="none" fillcolour="none" height="113" usefillcolor="1" isinstance="0" xmi.id="110" text="filesdb.py
The main purpose of the filesdb is to hold all the packages' files' information.
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="43" y="338" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="111" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="420" usesdiagramusefillcolour="1" x="15" y="188" linewidth="none" fillcolour="none" height="115" usefillcolor="1" isinstance="0" xmi.id="113" showstereotype="1" text="@d: is a bsddb dict which holds file infos that all the installed packages have.
key:&quot;file path&quot; value: (packagename, FileInfo) tuple
&quot;/var/db/pisi/filesdb.bdb&quot;
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="140" showattsigs="601" usesdiagramusefillcolour="1" x="538" y="244" showopsigs="601" linewidth="none" fillcolour="none" height="122" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="114" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="335" usesdiagramusefillcolour="1" x="640" y="81" linewidth="none" fillcolour="none" height="223" usefillcolor="1" isinstance="0" xmi.id="133" showstereotype="1" text="@path: path of the file
@type: type of the file. (&quot;doc&quot;, &quot;man&quot;, &quot;info&quot;, &quot;config&quot;, &quot;header&quot;, &quot;library&quot;, &quot;executable&quot;, &quot;data&quot;, &quot;localedata&quot;)
@size: size of the file
@hash: sha1sum of the file
@permanent: If true, file is not removed when the package is removed. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="114" widgetaid="111" xmi.id="120" linecolor="none" >
<linepath>
<startpoint startx="182" starty="360" />
<endpoint endx="538" endy="305" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="360" y="332" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="230" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="136" documentation="" type="402" showops="1" showpackage="0" name="ItemByRepo" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<enumwidget usesdiagramfillcolour="0" width="85" usesdiagramusefillcolour="0" x="507" y="249" linewidth="none" fillcolour="#ffffc0" height="126" usefillcolor="1" isinstance="0" xmi.id="141" showpackage="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="52" y="262" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="146" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="394" usesdiagramusefillcolour="1" x="38" y="178" linewidth="none" fillcolour="none" height="69" usefillcolor="1" isinstance="0" xmi.id="148" showstereotype="1" text="@d: is a bsddb dict which holds another key,value dict
key: objkey value:dict(&quot;repo name&quot;, objvalue)
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="152" text="itembydb.py
The main purpose of the itembyrepo class is make it easier for holding datas by repository." font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="141" widgetaid="146" xmi.id="149" linecolor="none" >
<linepath>
<startpoint startx="191" starty="284" />
<endpoint endx="507" endy="312" />
</linepath>
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="153" documentation="" type="402" showops="1" showpackage="0" name="ComponentDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="154" text="component.py
The main purpose of the componentdb is to hold the repositories component infos. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="130" showattsigs="601" usesdiagramusefillcolour="1" x="31" y="332" showopsigs="601" linewidth="none" fillcolour="none" height="47" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="155" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="401" usesdiagramusefillcolour="1" x="30" y="203" linewidth="none" fillcolour="none" height="112" usefillcolor="1" isinstance="0" xmi.id="157" showstereotype="1" text="@d: is a ItemByRepo object which contains component infos by repos.
key: &quot;component name&quot; value: Component
&quot;/var/db/pisi/component.bdb&quot;
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="160" showattsigs="601" usesdiagramusefillcolour="1" x="549" y="317" showopsigs="601" linewidth="none" fillcolour="none" height="153" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="163" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="335" usesdiagramusefillcolour="1" x="673" y="106" linewidth="none" fillcolour="none" height="223" usefillcolor="1" isinstance="0" xmi.id="176" showstereotype="1" text="@name: component name
@localname: components local name (system.base) -> (sistem.taban)
@summary: component information
@component: component description
@dependencies: string list of other components this component depends on.
@packages: string list of packages under this component
@sources: string list of source packages under this component" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="163" widgetaid="155" xmi.id="171" linecolor="none" >
<linepath>
<startpoint startx="161" starty="355" />
<endpoint endx="549" endy="393" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="355" y="374" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="231" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1021" snapy="10" showatts="1" xmi.id="184" documentation="" type="402" showops="1" showpackage="0" name="PackageDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="579" >
<widgets>
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="185" text="packagedb.py
The main purpose of the packagedb is to hold package infos of the repositories and reverse dependency informations." font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="125" showattsigs="601" usesdiagramusefillcolour="1" x="36" y="428" showopsigs="601" linewidth="none" fillcolour="none" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="186" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="418" usesdiagramusefillcolour="1" x="32" y="177" linewidth="none" fillcolour="none" height="235" usefillcolor="1" isinstance="0" xmi.id="189" showstereotype="1" text="@d: is a ItemByRepo object which holds the package informations of repos.
key:&quot;package name&quot; value:Package
&quot;/var/db/pisi/package.bdb&quot;
@d: is a ItemByRepo object which holds reverse dependency information of packages.
key:&quot;dependency name&quot; value: [dependent package names list]
&quot;/var/db/pisi/revdep.bdb&quot; " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="217" showattsigs="601" usesdiagramusefillcolour="1" x="557" y="296" showopsigs="601" linewidth="none" fillcolour="none" height="243" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="190" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="317" usesdiagramusefillcolour="1" x="700" y="187" linewidth="none" fillcolour="none" height="120" usefillcolor="1" isinstance="0" xmi.id="206" showstereotype="1" text="@name: package name
@summary: package's summary
@description: description of the package
@partof: package's component
@license: license of the package
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="190" widgetaid="186" xmi.id="203" linecolor="none" >
<linepath>
<startpoint startx="161" starty="459" />
<endpoint endx="557" endy="417" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="359" y="438" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="232" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="209" documentation="" type="402" showops="1" showpackage="0" name="SourceDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
<widgets>
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="27" y="22" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="210" text="sourcedb.py
The main purpose of the sourcedb is for emerge operation. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="175" showattsigs="601" usesdiagramusefillcolour="1" x="33" y="433" showopsigs="601" linewidth="none" fillcolour="none" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="211" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
<notewidget usesdiagramfillcolour="1" width="414" usesdiagramusefillcolour="1" x="31" y="174" linewidth="none" fillcolour="none" height="242" usefillcolor="1" isinstance="0" xmi.id="214" showstereotype="1" text="@d: is a ItemByRepo object which contains source packages building information of the package for emerge.
key: &quot;source package name&quot; value: SpecFile
&quot;/var/db/pisi/source.bdb&quot;
@d: is a ItemByRepo object which contains package name information and that package's source package name information
key: &quot;package name&quot; value:&quot;source package name&quot;
&quot;/var/db/pisi/pkgtosrc.bdb&quot;
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
<classwidget usesdiagramfillcolour="1" width="133" showattsigs="601" usesdiagramusefillcolour="1" x="666" y="365" showopsigs="601" linewidth="none" fillcolour="none" height="99" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="215" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
</widgets>
<messages/>
<associations>
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="215" widgetaid="211" xmi.id="222" linecolor="none" >
<linepath>
<startpoint startx="208" starty="464" />
<endpoint endx="666" endy="414" />
</linepath>
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="437" y="439" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="233" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
</assocwidget>
</associations>
</diagram>
</diagrams>
<listview>
<listitem open="1" type="800" label="Görünümler" >
<listitem open="1" type="801" label="Mantıksal Görünüm" >
<listitem open="1" type="813" id="163" >
<listitem open="0" type="814" id="164" />
<listitem open="0" type="814" id="165" />
<listitem open="0" type="814" id="166" />
<listitem open="0" type="814" id="167" />
<listitem open="0" type="814" id="168" />
<listitem open="0" type="814" id="169" />
<listitem open="0" type="814" id="170" />
</listitem>
<listitem open="1" type="813" id="155" >
<listitem open="0" type="814" id="156" />
</listitem>
<listitem open="1" type="813" id="114" >
<listitem open="0" type="814" id="115" />
<listitem open="0" type="814" id="116" />
<listitem open="0" type="814" id="117" />
<listitem open="0" type="814" id="118" />
<listitem open="0" type="814" id="119" />
</listitem>
<listitem open="1" type="813" id="111" >
<listitem open="0" type="814" id="112" />
</listitem>
<listitem open="1" type="813" id="220" />
<listitem open="1" type="813" id="24" >
<listitem open="1" type="813" id="14" >
<listitem open="0" type="814" id="79" />
<listitem open="0" type="814" id="80" />
<listitem open="0" type="814" id="81" />
<listitem open="0" type="814" id="82" />
<listitem open="0" type="814" id="83" />
<listitem open="0" type="814" id="84" />
</listitem>
<listitem open="0" type="814" id="25" />
<listitem open="0" type="814" id="77" />
</listitem>
<listitem open="1" type="813" id="146" >
<listitem open="0" type="814" id="147" />
</listitem>
<listitem open="1" type="813" id="76" />
<listitem open="1" type="813" id="190" >
<listitem open="0" type="814" id="191" />
<listitem open="0" type="814" id="192" />
<listitem open="0" type="814" id="193" />
<listitem open="0" type="814" id="194" />
<listitem open="0" type="814" id="195" />
<listitem open="0" type="814" id="196" />
<listitem open="0" type="814" id="197" />
<listitem open="0" type="814" id="198" />
<listitem open="0" type="814" id="199" />
<listitem open="0" type="814" id="200" />
<listitem open="0" type="814" id="201" />
<listitem open="0" type="814" id="202" />
</listitem>
<listitem open="1" type="813" id="186" >
<listitem open="0" type="814" id="187" />
<listitem open="0" type="814" id="188" />
</listitem>
<listitem open="1" type="813" id="98" >
<listitem open="0" type="814" id="99" />
</listitem>
<listitem open="1" type="813" id="86" >
<listitem open="0" type="814" id="87" />
</listitem>
<listitem open="1" type="813" id="217" />
<listitem open="1" type="813" id="211" >
<listitem open="0" type="814" id="212" />
<listitem open="0" type="814" id="213" />
</listitem>
<listitem open="1" type="813" id="215" >
<listitem open="0" type="814" id="216" />
<listitem open="0" type="814" id="218" />
<listitem open="0" type="814" id="219" />
<listitem open="0" type="814" id="221" />
</listitem>
<listitem open="1" type="813" id="177" />
<listitem open="1" type="830" label="Veri türleri" >
<listitem open="1" type="829" id="5" />
<listitem open="1" type="829" id="4" />
<listitem open="1" type="829" id="89" />
<listitem open="1" type="829" id="7" />
<listitem open="1" type="829" id="6" />
<listitem open="1" type="829" id="2" />
<listitem open="1" type="829" id="9" />
<listitem open="1" type="829" id="8" />
<listitem open="1" type="829" id="13" />
<listitem open="1" type="829" id="10" />
<listitem open="1" type="829" id="12" />
<listitem open="1" type="829" id="11" />
</listitem>
<listitem open="1" type="831" id="141" >
<listitem open="0" type="839" id="142" />
<listitem open="0" type="839" id="143" />
<listitem open="0" type="839" id="144" />
<listitem open="0" type="839" id="145" />
</listitem>
</listitem>
<listitem open="1" type="802" label="Use Case Görünümü" />
<listitem open="1" type="821" label="Bileşen Görünümü" />
<listitem open="1" type="827" label="Deployment View" />
<listitem open="1" type="836" label="Entity Relationship Model" />
</listitem>
</listview>
<codegeneration>
<codegenerator language="C++" />
</codegeneration>
</XMI.extensions>
</XMI>
-37
View File
@@ -1,37 +0,0 @@
%%
%% This is file `prettyref.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% prettyref.dtx (with options: `style')
%%
%% Copyright (c) 1995 Kevin Ruland
%%
%%
%% prettyref v3.0
%%
%% Copyright 1995,1998. by Kevin Ruland kevin@rodin.wustl.edu
%%
\ProvidesPackage{prettyref}[1998/07/09 v3.0]
\def\newrefformat#1#2{%
\@namedef{pr@#1}##1{#2}}
\newrefformat{eq}{\textup{(\ref{#1})}}
\newrefformat{lem}{Lemma \ref{#1}}
\newrefformat{thm}{Theorem \ref{#1}}
\newrefformat{cha}{Chapter \ref{#1}}
\newrefformat{sec}{Section \ref{#1}}
\newrefformat{tab}{Table \ref{#1} on page \pageref{#1}}
\newrefformat{fig}{Figure \ref{#1} on page \pageref{#1}}
\def\prettyref#1{\@prettyref#1:}
\def\@prettyref#1:#2:{%
\expandafter\ifx\csname pr@#1\endcsname\relax%
\PackageWarning{prettyref}{Reference format #1\space undefined}%
\ref{#1:#2}%
\else%
\csname pr@#1\endcsname{#1:#2}%
\fi%
}
\endinput
%%
%% End of file `prettyref.sty'.
-86
View File
@@ -1,86 +0,0 @@
#!/usr/bin/python
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import locale
import traceback
import exceptions
import signal
import bsddb3.db as db
import pisi.ui
import pisi.context as ctx
import pisi.cli.pisicli as pisicli
import gettext
gettext.bindtextdomain('pisi', "/usr/share/locale")
gettext.textdomain('pisi')
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
def sig_handler(sig, frame):
if sig == signal.SIGTERM:
exit()
def exit():
pisi.api.finalize()
sys.exit(1)
def handle_exception(exception, value, tb):
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
ui = pisi.cli.CLI() # make a temporary UI
show_traceback = False
if exception == exceptions.KeyboardInterrupt:
ui.error(_("Keyboard Interrupt: Exiting..."))
exit()
elif isinstance(value, pisi.Error):
ui.error(_("Program Terminated."))
show_traceback = ctx.get_option('debug')
elif isinstance(value, db.DBRunRecoveryError):
ui.error(_("""A database operation has been aborted. You should run pisi
again for normal DB recovery procedure. Make sure you have free disk space.
You have to run rebuild-db only when there is file corruption and database upgrades."""))
elif isinstance(value, pisi.Exception):
show_traceback = True
ui.error(_("""Unhandled internal exception.
Please file a bug report. (http://bugs.uludag.org.tr)"""))
else:
# For any other exception (possibly Python exceptions) show
# the traceback!
show_traceback = ctx.get_option('debug')
ui.error(_("System Error. Program Terminated."))
if ctx.get_option('debug'):
ui.error(u"%s: %s" % (exception, value))
else:
ui.error(unicode(value))
ui.info(_("Please use 'pisi help' for general help."))
if show_traceback:
ui.info(_("Traceback:"))
traceback.print_tb(tb)
else:
if not exception is pisicli.Error:
ui.info(_("Use --debug to see a traceback."))
exit()
if __name__ == "__main__":
sys.excepthook = handle_exception
signal.signal(signal.SIGTERM, sig_handler)
locale.setlocale(locale.LC_ALL, '')
cli = pisicli.PisiCLI()
cli.run_command()
-78
View File
@@ -1,78 +0,0 @@
#!/usr/bin/python2.5
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import locale
import traceback
import exceptions
import signal
import bsddb3.db as db
import pisi.ui
import pisi.context as ctx
import pisi.cli.pisicli as pisicli
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
def exit():
pisi.api.finalize()
sys.exit(1)
def handle_exception(exception, value, tb):
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
ui = pisi.cli.CLI() # make a temporary UI
show_traceback = False
if exception == exceptions.KeyboardInterrupt:
ui.error(_("Keyboard Interrupt: Exiting..."))
exit()
elif isinstance(value, pisi.Error):
ui.error(_("Program Terminated."))
show_traceback = ctx.get_option('debug')
elif isinstance(value, db.DBRunRecoveryError):
ui.error(_("""A database operation has been aborted. You should run pisi
again for normal DB recovery procedure. Make sure you have free disk space.
You have to run rebuild-db only when there is file corruption and database upgrades."""))
elif isinstance(value, pisi.Exception):
show_traceback = True
ui.error(_("""Unhandled internal exception.
Please file a bug report. (http://bugs.uludag.org.tr)"""))
else:
# For any other exception (possibly Python exceptions) show
# the traceback!
show_traceback = ctx.get_option('debug')
ui.error(_("System Error. Program Terminated."))
if ctx.get_option('debug'):
ui.error(u"%s: %s" % (exception, value))
else:
ui.error(unicode(value))
ui.info(_("Please use 'pisi help' for general help."))
if show_traceback:
ui.info(_("Traceback:"))
traceback.print_tb(tb)
else:
if not exception is pisicli.Error:
ui.info(_("Use --debug to see a traceback."))
exit()
if __name__ == "__main__":
sys.excepthook = handle_exception
locale.setlocale(locale.LC_ALL, '')
cli = pisicli.PisiCLI()
cli.run_command()
-123
View File
@@ -1,123 +0,0 @@
<!-- Document Type Declaration file for PISI pspec.xml files -->
<!-- http://www.pardus.org.tr/projeler/pisi/ -->
<!ELEMENT PISI (Source,Package+,History)>
<!-- Source Section -->
<!ELEMENT Source (
#PCDATA |
Name |
Homepage |
Icon |
Packager |
License |
IsA |
PartOf |
Summary |
Description |
Archive |
Patches |
BuildDependencies
)*>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Homepage (#PCDATA)>
<!ELEMENT Icon (#PCDATA)>
<!ELEMENT Packager (Name, Email)>
<!ELEMENT Email (#PCDATA)>
<!ELEMENT License (#PCDATA)>
<!ELEMENT IsA (#PCDATA)>
<!ELEMENT PartOf (#PCDATA)>
<!ELEMENT Summary (#PCDATA)>
<!ATTLIST Summary xml:lang CDATA #IMPLIED>
<!ELEMENT Description (#PCDATA)>
<!ATTLIST Description xml:lang CDATA #IMPLIED>
<!ELEMENT Archive (#PCDATA)>
<!ATTLIST Archive sha1sum CDATA #REQUIRED>
<!-- NOTE: we can limit possible types here to the ones that pisi know -->
<!ATTLIST Archive type CDATA #REQUIRED>
<!ELEMENT BuildDependencies (Dependency*)>
<!ELEMENT Dependency (#PCDATA)>
<!ATTLIST Dependency versionFrom CDATA #IMPLIED>
<!ATTLIST Dependency versionTo CDATA #IMPLIED>
<!ATTLIST Dependency version CDATA #IMPLIED>
<!ATTLIST Dependency releaseFrom CDATA #IMPLIED>
<!ATTLIST Dependency releaseTo CDATA #IMPLIED>
<!ATTLIST Dependency release CDATA #IMPLIED>
<!ELEMENT Patches (Patch*)>
<!ELEMENT Patch (#PCDATA)>
<!ATTLIST Patch compressionType CDATA #IMPLIED>
<!ATTLIST Patch level CDATA #IMPLIED>
<!ATTLIST Patch target CDATA #IMPLIED>
<!-- Package Section -->
<!ELEMENT Package (
#PCDATA |
Name |
License |
IsA |
PartOf |
Summary |
Description |
RuntimeDependencies |
Files |
Conflicts |
AdditionalFiles |
Provides
)*>
<!ELEMENT Provides (COMAR)+>
<!ELEMENT COMAR (#PCDATA)>
<!ATTLIST COMAR script CDATA #IMPLIED>
<!ELEMENT Conflicts (Package)+>
<!-- we have a problem here, this Package tag only contains a package name
while dtd treats this as another Package section, who said dtd doesnt
suck anyway -->
<!ELEMENT AdditionalFiles (AdditionalFile+)>
<!ELEMENT AdditionalFile (#PCDATA)>
<!ATTLIST AdditionalFile target CDATA #REQUIRED>
<!ATTLIST AdditionalFile permission CDATA #IMPLIED>
<!ATTLIST AdditionalFile owner CDATA #IMPLIED>
<!ELEMENT RuntimeDependencies (Dependency*)>
<!ELEMENT Files (Path)+>
<!ELEMENT Path (#PCDATA)>
<!-- NOTE: is this list correct? -->
<!ATTLIST Path fileType
(executable|library|data|config|doc|man|info|localedata|header)
#REQUIRED>
<!ATTLIST Path permanent (false|true) #IMPLIED>
<!-- History Section -->
<!ELEMENT History (Update+)>
<!ELEMENT Update (#PCDATA | Date | Version | Comment | Name | Email)*>
<!ATTLIST Update release CDATA #REQUIRED>
<!-- FIXME: valid types must be used instead of CDATA -->
<!ATTLIST Update type CDATA #IMPLIED>
<!ELEMENT Date (#PCDATA)>
<!ELEMENT Version (#PCDATA)>
<!ELEMENT Comment (#PCDATA)>
-40
View File
@@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# PiSi version
__version__ = "1.1_rc4"
__dbversion__ = "1.1_beta8"
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
# FIXME: Exception shadows builtin Exception. This is no good.
class Exception(Exception):
"""Class of exceptions that must be caught and handled within PiSi"""
def __str__(self):
s = u''
for x in self.args:
if s != '':
s += '\n'
s += unicode(x)
return s
class Error(Exception):
"""Class of exceptions that lead to program termination"""
pass
import pisi.api
# FIXME: can't do this due to name clashes in config and other singletons booo
#pisi.api import *
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
import pisi.context as ctx
def error(msg):
if ctx.config.get_option('ignore_action_errors'):
ctx.ui.error(msg)
else:
raise Error(msg)
@@ -1,163 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
from pisi.actionsapi.shelltools import unlink
from pisi.actionsapi.libtools import gnuconfig_update
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
if can_access_file('config.log'):
ctx.ui.error(_('Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
'''configure source with given parameters = "--with-nls --with-libusb --with-something-usefull"'''
if can_access_file('configure'):
gnuconfig_update()
args = './configure \
--prefix=/%s \
--host=%s \
--mandir=/%s \
--infodir=/%s \
--datadir=/%s \
--sysconfdir=/%s \
--localstatedir=/%s \
%s' % (get.defaultprefixDIR(), \
get.HOST(), get.manDIR(), \
get.infoDIR(), get.dataDIR(), \
get.confDIR(), get.localstateDIR(), parameters)
if system(args):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def rawConfigure(parameters = ''):
'''configure source with given parameters = "--prefix=/usr --libdir=/usr/lib --with-nls"'''
if can_access_file('configure'):
gnuconfig_update()
if system('./configure %s' % parameters):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def compile(parameters = ''):
#FIXME: Only one package uses this until now, hmmm
system('%s %s %s' % (get.CC(), get.CFLAGS(), parameters))
def make(parameters = ''):
'''make source with given parameters = "all" || "doc" etc.'''
if system('make %s %s' % (get.makeJOBS(), parameters)):
raise MakeError(_('Make failed.'))
def fixInfoDir():
infoDir = "%s/usr/share/info/dir" % get.installDIR()
if can_access_file(infoDir):
unlink(infoDir)
def install(parameters = '', argument = 'install'):
'''install source into install directory with given parameters'''
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
args = 'make prefix=%(prefix)s/%(defaultprefix)s \
datadir=%(prefix)s/%(data)s \
infodir=%(prefix)s/%(info)s \
localstatedir=%(prefix)s/%(localstate)s \
mandir=%(prefix)s/%(man)s \
sysconfdir=%(prefix)s/%(conf)s \
%(parameters)s \
%(argument)s' % {'prefix': get.installDIR(),
'defaultprefix': get.defaultprefixDIR(),
'man': get.manDIR(),
'info': get.infoDIR(),
'localstate': get.localstateDIR(),
'conf': get.confDIR(),
'data': get.dataDIR(),
'parameters': parameters,
'argument':argument}
if system(args):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
fixInfoDir()
def rawInstall(parameters = '', argument = 'install'):
'''install source into install directory with given parameters = PREFIX=%s % get.installDIR()'''
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
if system('make %s %s' % (parameters, argument)):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
fixInfoDir()
def aclocal(parameters = ''):
'''generates an aclocal.m4 based on the contents of configure.in.'''
if system('aclocal %s' % parameters):
raise RunTimeError(_('Running aclocal failed.'))
def autoconf(parameters = ''):
'''generates a configure script'''
if system('autoconf %s' % parameters):
raise RunTimeError(_('Running autoconf failed.'))
def autoreconf(parameters = ''):
'''re-generates a configure script'''
if system('autoreconf %s' % parameters):
raise RunTimeError(_('Running autoconf failed.'))
def automake(parameters = ''):
'''generates a makefile'''
if system('automake %s' % parameters):
raise RunTimeError(_('Running automake failed.'))
def autoheader(parameters = ''):
'''generates templates for configure'''
if system('autoheader %s' % parameters):
raise RunTimeError(_('Running autoheader failed.'))
@@ -1,80 +0,0 @@
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import re
import sys
from itertools import izip
from itertools import imap
from itertools import count
from itertools import ifilter
from itertools import ifilterfalse
# ActionsAPI
import pisi.actionsapi
def cat(filename):
return file(filename).xreadlines()
class grep:
'''keep only lines that match the regexp'''
def __init__(self, pat, flags = 0):
self.fun = re.compile(pat, flags).match
def __ror__(self, input):
return ifilter(self.fun, input)
class tr:
'''apply arbitrary transform to each sequence element'''
def __init__(self, transform):
self.tr = transform
def __ror__(self, input):
return imap(self.tr, input)
class printto:
'''print sequence elements one per line'''
def __init__(self, out = sys.stdout):
self.out = out
def __ror__(self,input):
for line in input:
print >> self.out, line
printlines = printto(sys.stdout)
class terminator:
def __init__(self,method):
self.process = method
def __ror__(self,input):
return self.process(input)
aslist = terminator(list)
asdict = terminator(dict)
astuple = terminator(tuple)
join = terminator(''.join)
enum = terminator(enumerate)
class sort:
def __ror__(self,input):
ll = list(input)
ll.sort()
return ll
sort = sort()
class uniq:
def __ror__(self,input):
for i in input:
try:
if i == prev:
continue
except NameError:
pass
prev = i
yield i
uniq = uniq()
-196
View File
@@ -1,196 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standart Python Modules
import os
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi Modules
import pisi.actionsapi
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi.variables
class BinutilsError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
# Globals
env = pisi.actionsapi.variables.glb.env
dirs = pisi.actionsapi.variables.glb.dirs
def curDIR():
'''returns current work directory's path'''
return os.getcwd()
def curKERNEL():
'''returns currently running kernel's version'''
return os.uname()[2]
def curPYTHON():
''' returns currently used python's version'''
(a, b, c, x, y) = sys.version_info
return 'python%s.%s' % (a, b)
def curPERL():
''' returns currently used perl's version'''
return os.path.realpath("/usr/bin/perl").split("perl")[1]
def ENV(environ):
'''returns any given environ variable'''
try:
return os.environ[environ];
except KeyError:
return None
# PİSİ Related Functions
def pkgDIR():
'''returns the path of binary packages'''
'''Default: /var/cache/pisi/packages'''
return env.pkg_dir
def workDIR():
return env.work_dir
def installDIR():
'''returns the path of binary packages'''
return env.install_dir
# PSPEC Related Functions
def srcNAME():
return env.src_name
def srcVERSION():
return env.src_version
def srcRELEASE():
return env.src_release
def srcTAG():
return '%s-%s-%s' % (env.src_name, env.src_version, env.src_release)
def srcDIR():
return '%s-%s' % (env.src_name, env.src_version)
# Build Related Functions
def HOST():
return env.host
def CHOST():
# FIXME: Currently it behave same as HOST,
# but will be used for cross-compiling when PİSİ ready...
return env.host
def CFLAGS():
return env.cflags
def CXXFLAGS():
return env.cxxflags
def LDFLAGS():
return env.ldflags
def makeJOBS():
return env.jobs
# Directory Related Functions
def docDIR():
return dirs.doc
def sbinDIR():
return dirs.sbin
def infoDIR():
return dirs.info
def manDIR():
return dirs.man
def dataDIR():
return dirs.data
def confDIR():
return dirs.conf
def localstateDIR():
return dirs.localstate
def defaultprefixDIR():
return dirs.defaultprefix
def kdeDIR():
return dirs.kde
def qtDIR():
return dirs.qt
def qtLIBDIR():
return '%s/lib/' % qtDIR()
# Binutils Variables
def existBinary(bin):
# determine if path has binary
path = os.environ['PATH'].split(':')
for directory in path:
if os.path.exists(os.path.join(directory, bin)):
return True
return False
def getBinutilsInfo(util):
cross_build_name = '%s-%s' % (HOST(), util)
if not existBinary(cross_build_name):
if not existBinary(util):
raise BinutilsError(_('Util %s cannot be found') % util)
else:
ctx.ui.debug(_('Warning: %s does not exist, using plain name %s') \
% (cross_build_name, util))
return util
else:
return cross_build_name
def AR():
return getBinutilsInfo('ar')
def AS():
return getBinutilsInfo('as')
def CC():
return getBinutilsInfo('gcc')
def CXX():
return getBinutilsInfo('g++')
def LD():
return getBinutilsInfo('ld')
def NM():
return getBinutilsInfo('nm')
def RANLIB():
return getBinutilsInfo('ranlib')
def F77():
return getBinutilsInfo('f77')
def GCJ():
return getBinutilsInfo('gcj')
-81
View File
@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
if can_access_file('config.log'):
ctx.ui.error(_('\n!!! Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
''' parameters = '--with-nls --with-libusb --with-something-usefull '''
if can_access_file('configure'):
args = './configure \
--prefix=%s \
--host=%s \
--with-x \
--enable-mitshm \
--with-xinerama \
--with-qt-dir=%s \
--enable-mt \
--with-qt-libraries=%s \
--disable-dependency-tracking \
--disable-debug \
%s' % (get.kdeDIR(), get.HOST(), get.qtDIR(), get.qtLIBDIR(), parameters)
if system(args):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def make(parameters = ''):
'''make source with given parameters = "all" || "doc" etc.'''
if system('make %s %s' % (get.makeJOBS(), parameters)):
raise MakeError(_('Make failed.'))
def install(parameters = 'install'):
if can_access_file('Makefile'):
args = 'make DESTDIR=%s destdir=%s %s' % (get.installDIR(), get.installDIR(), parameters)
if system(args):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
@@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi-Core Modules
import pisi.context as ctx
from pisi.util import join_path
# ActionsAPI Modules
import pisi.actionsapi
from pisi.actionsapi.shelltools import *
import pisi.actionsapi.get as get
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def preplib(sourceDirectory = '/usr/lib'):
sourceDirectory = join_path(get.installDIR(), sourceDirectory)
if can_access_directory(sourceDirectory):
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
raise RunTimeError(_('Running ldconfig failed.'))
def gnuconfig_update():
''' copy newest config.* onto source\'s '''
for root, dirs, files in os.walk(os.getcwd()):
for fileName in files:
if fileName in ['config.sub', 'config.guess']:
targetFile = os.path.join(root, fileName)
if os.path.islink(targetFile):
unlink(targetFile)
copy('/usr/share/gnuconfig/%s' % fileName, join_path(root, fileName))
ctx.ui.info(_('GNU Config Update Finished.'))
def libtoolize(parameters = ''):
if system('/usr/bin/libtoolize %s' % parameters):
raise RunTimeError(_('Running libtoolize failed.'))
def gen_usr_ldscript(dynamicLib):
makedirs('%s/usr/lib' % get.installDIR())
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
content = '''
/* GNU ld script
Since Pardus has critical dynamic libraries
in /lib, and the static versions in /usr/lib,
we need to have a "fake" dynamic lib in /usr/lib,
otherwise we run into linking problems.
*/
GROUP ( /lib/%s )
''' % dynamicLib
destinationFile.write(content)
destinationFile.close()
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
@@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
from pisi.actionsapi.shelltools import export
from pisi.actionsapi.shelltools import unlink
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
'''configure source with given parameters.'''
export('PERL_MM_USE_DEFAULT', '1')
if can_access_file('Build.PL'):
if system('perl Build.PL installdirs=vendor destdir=%s' % get.installDIR()):
raise CompileError, _('Configure failed.')
else:
if system('perl Makefile.PL %s PREFIX=/usr INSTALLDIRS=vendor DESTDIR=%s' % (parameters, get.installDIR())):
raise CompileError, _('Configure failed.')
def make(parameters = ''):
'''make source with given parameters.'''
if can_access_file('Makefile'):
if system('make %s' % parameters):
raise MakeError, _('Make failed.')
else:
if system('perl Build build'):
raise MakeError, _('perl build failed.')
def install(parameters = 'install'):
'''install source with given parameters.'''
if can_access_file('Makefile'):
if system('make %s' % parameters):
raise InstallError, _('Make failed.')
else:
if system('perl Build install'):
raise MakeError, _('perl install failed.')
fixLocalPod()
def fixLocalPod():
podFiles = [ "%s/usr/lib/perl5/vendor_perl/%s/i686-linux/perllocal.pod" % (get.installDIR(), get.curPERL()),
"%s/usr/lib/perl5/%s/i686-linux/perllocal.pod" % (get.installDIR(), get.curPERL()),
"%s/usr/lib/perl5/site_perl/%s/perllocal.pod" % (get.installDIR(), get.curPERL())]
for podFile in podFiles:
if can_access_file(podFile):
unlink(podFile)
@@ -1,234 +0,0 @@
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
'''supports globs in sourceFile arguments'''
# Standart Python Modules
import os
import glob
import sys
import fileinput
import re
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
from pisi.util import join_path
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.pisitoolsfunctions import *
from pisi.actionsapi.shelltools import *
from pisi.actionsapi import error
def dobin(sourceFile, destinationDirectory = '/usr/bin'):
'''insert a executable file into /bin or /usr/bin'''
''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") '''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dodir(destinationDirectory):
'''creates a directory tree'''
makedirs(join_path(get.installDIR(), destinationDirectory))
def dodoc(*sourceFiles):
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
readable_insinto(join_path(get.installDIR(), join_path('/usr/share/doc', get.srcTAG())), *sourceFiles)
def doexe(sourceFile, destinationDirectory):
'''insert a executable file into destination directory'''
''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")'''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dohtml(*sourceFiles):
'''inserts the files in the list of files into /usr/share/doc/PACKAGE/html'''
''' example call: pisitools.dohtml("doc/doxygen/html/*")'''
destionationDirectory = join_path(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html')
if not can_access_directory(destionationDirectory):
makedirs(destionationDirectory)
allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js']
disallowed_directories = ['CVS']
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions:
system('install -m0644 "%s" %s' % (source, destionationDirectory))
if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories:
for root, dirs, files in os.walk(source):
for source in files:
if os.path.splitext(source)[1] in allowed_extensions:
makedirs(destionationDirectory)
system('install -m0644 %s %s' % (join_path(root, source), destionationDirectory))
def doinfo(*sourceFiles):
'''inserts the into files in the list of files into /usr/share/info'''
readable_insinto(join_path(get.installDIR(), get.infoDIR()), *sourceFiles)
def dolib(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the library into /usr/lib'''
'''example call: pisitools.dolib_a("libz.a")'''
'''example call: pisitools.dolib_a("libz.so")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 755)
def dolib_a(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the static library into /usr/lib with permission 0644'''
'''example call: pisitools.dolib_a("staticlib/libvga.a")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 644)
def dolib_so(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the static library into /usr/lib with permission 0755'''
'''example call: pisitools.dolib_so("pppd/plugins/minconn.so")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 755)
def doman(*sourceFiles):
'''inserts the man pages in the list of files into /usr/share/man/'''
'''example call: pisitools.doman("man.1", "pardus.*")'''
manDIR = join_path(get.installDIR(), get.manDIR())
if not can_access_directory(manDIR):
makedirs(manDIR)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
try:
pageName, pageDirectory = source[:source.rindex('.')], \
source[source.rindex('.')+1:]
except ValueError:
error(_('ActionsAPI [doman]: Wrong man page file: %s') % (source))
makedirs(join_path(manDIR, '/man%s' % pageDirectory))
system('install -m0644 %s %s' % (source, join_path(manDIR, '/man%s' % pageDirectory)))
def domo(sourceFile, locale, destinationFile ):
'''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES'''
'''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")'''
system('msgfmt %s' % sourceFile)
makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale))
move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile))
def domove(sourceFile, destination, destinationFile = ''):
'''moves sourceFile/Directory into destinationFile/Directory'''
''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")'''
''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")'''
makedirs(join_path(get.installDIR(), destination))
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
if not destinationFile:
move(filePath, join_path(get.installDIR(), join_path(destination, os.path.basename(filePath))))
else:
move(filePath, join_path(get.installDIR(), join_path(destination, destinationFile)))
def rename(sourceFile, destinationFile):
''' renames sourceFile as destinationFile'''
''' example call: pisitools.rename("/usr/bin/bash", "bash.old") '''
''' the result of the previous example would be "/usr/bin/bash.old" '''
baseDir = os.path.dirname(sourceFile)
try:
os.rename(join_path(get.installDIR(), sourceFile), join_path(get.installDIR(), baseDir, destinationFile))
except OSError:
error(_('ActionsAPI [rename]: No such file or directory: %s') % (sourceFile))
def dosed(sourceFiles, findPattern, replacePattern = ''):
'''replaces patterns in sourceFiles'''
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
''' example call: pisitools.dosed("/etc/passwd", "caglar")'''
''' example call: pisitools.dosed("/etc/pass*", "caglar")'''
''' example call: pisitools.dosed("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
for sourceFile in glob.glob(sourceFiles):
if can_access_file(sourceFile):
for line in fileinput.input(sourceFile, inplace = 1):
#FIXME: In-place filtering is disabled when standard input is read
line = re.sub(findPattern, replacePattern, line)
sys.stdout.write(line)
else:
raise FileError(_('File does not exist or permission denied: %s') % sourceFile)
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
'''insert a executable file into /sbin or /usr/sbin'''
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dosym(sourceFile, destinationFile):
'''creates soft link between sourceFile and destinationFile'''
''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")'''
makedirs(join_path(get.installDIR(), os.path.dirname(destinationFile)))
try:
os.symlink(sourceFile, join_path(get.installDIR() ,destinationFile))
except OSError:
error(_('ActionsAPI [dosym]: File exists: %s') % (sourceFile))
def insinto(destinationDirectory, sourceFile, destinationFile = '', sym = True):
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
makedirs(join_path(get.installDIR(), destinationDirectory))
if not destinationFile:
for filePath in glob.glob(sourceFile):
if can_access_file(filePath):
copy(filePath, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(filePath))), sym)
else:
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)), sym)
def newdoc(sourceFile, destinationFile):
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
destinationDirectory = '' #490
destinationDirectory = os.path.dirname(destinationFile)
destinationFile = os.path.basename(destinationFile)
# Use copy instead of move or let build-install scream like file not found!
copy(sourceFile, destinationFile)
readable_insinto(join_path(get.installDIR(), 'usr/share/doc', get.srcTAG(), destinationDirectory), destinationFile)
def newman(sourceFile, destinationFile):
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
# Use copy instead of move or let build-install scream like file not found!
copy(sourceFile, destinationFile)
doman(destinationFile)
def remove(sourceFile):
'''removes sourceFile'''
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
unlink(filePath)
def removeDir(destinationDirectory):
'''removes destinationDirectory and its subtrees'''
for directory in glob.glob(join_path(get.installDIR(), destinationDirectory)):
unlinkDir(directory)
@@ -1,80 +0,0 @@
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Generic functions for common usage of pisitools #
# Standart Python Modules
import os
import glob
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
from pisi.actionsapi.shelltools import *
class FileError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class ArgumentError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def executable_insinto(destinationDirectory, *sourceFiles):
'''insert a executable file into destinationDirectory'''
if not sourceFiles or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
# FIXME: use an internal install routine for these
system('install -m0755 -o root -g root %s %s' % (source, destinationDirectory))
def readable_insinto(destinationDirectory, *sourceFiles):
'''inserts file list into destinationDirectory'''
if not sourceFiles or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
system('install -m0644 "%s" %s' % (source, destinationDirectory))
def lib_insinto(sourceFile, destinationDirectory, permission = 0644):
'''inserts a library fileinto destinationDirectory with given permission'''
if not sourceFile or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
if os.path.islink(sourceFile):
os.symlink(os.path.realpath(sourceFile), os.path.join(destinationDirectory, sourceFile))
else:
system('install -m%s %s %s' % (permission, sourceFile, destinationDirectory))
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system, can_access_file
from pisi.actionsapi.pisitools import dodoc
class CompileError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def compile(parameters = ''):
'''compile source with given parameters.'''
if system('python setup.py build %s' % (parameters)):
raise CompileError, _('Make failed.')
def install(parameters = ''):
'''does python setup.py install'''
if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)):
raise InstallError, _('Install failed.')
DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \
CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*'
for doc in DDOCS:
if can_access_file(doc):
pisitools.dodoc(doc)
def run(parameters = ''):
'''executes parameters with python'''
if system('python %s' % (parameters)):
raise RunTimeError, _('Running %s failed.') % parameters
@@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Pisi Modules
import pisi.context as ctx
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def make(parameters = ''):
if system('scons %s' % parameters):
raise MakeError(_('Make failed.'))
def install(parameters = 'install', prefix = get.installDIR(), argument='prefix'):
if system('scons %s=%s %s' % (argument, prefix, parameters)):
raise InstallError(_('Install failed.'))
@@ -1,224 +0,0 @@
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standart Python Modules
import os
import glob
import shutil
import string
import pwd
import grp
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get
from pisi.actionsapi import error
from pisi.util import run_logged
from pisi.util import join_path
def can_access_file(filePath):
'''test the existence of file'''
return os.access(filePath, os.F_OK)
def can_access_directory(destinationDirectory):
'''test readability, writability and executablility of directory'''
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
def makedirs(destinationDirectory):
'''recursive directory creation function'''
try:
if not os.access(destinationDirectory, os.F_OK):
os.makedirs(destinationDirectory)
except OSError:
error(_('Cannot create directory %s') % destinationDirectory)
def echo(destionationFile, content):
try:
f = open(destionationFile, 'a')
f.write('%s\n' % content)
f.close()
except IOError:
error(_('ActionsAPI [echo]: Can\'t append to file %s.') % (destionationFile))
def chmod(filePath, mode = 0755):
'''change the mode of filePath to the mode'''
for fileName in glob.glob(filePath):
if can_access_file(fileName):
try:
os.chmod(fileName, mode)
except OSError:
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
% (fileName, mode))
else:
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (fileName))
def chown(filePath, uid = "root", gid = "root"):
'''change the owner and group id of filePath to uid and gid'''
if can_access_file(filePath):
try:
os.chown(filePath, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
except OSError:
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
% (filePath, uid, gid))
else:
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % filePath)
def sym(source, destination):
'''creates symbolic link'''
try:
os.symlink(source, destination)
except OSError:
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (source, destination))
def unlink(filePath):
'''remove the file path'''
if isFile(filePath) or isLink(filePath):
try:
os.unlink(filePath)
except OSError:
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (filePath))
elif isDirectory(filePath):
pass
else:
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (filePath))
def unlinkDir(sourceDirectory):
'''delete an entire directory tree'''
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
try:
shutil.rmtree(sourceDirectory)
except OSError:
error(_('ActionsAPI [unlinkDir]: Operation not permitted: %s') % (sourceDirectory))
elif isFile(sourceDirectory):
pass
else:
error(_('ActionsAPI [unlinkDir]: Directory %s doesn\'t exists.') % (sourceDirectory))
def move(source, destination):
'''recursively move a "source" file or directory to "destination"'''
for filePath in glob.glob(source):
if isFile(filePath) or isLink(filePath) or isDirectory(filePath):
try:
shutil.move(filePath, destination)
except OSError:
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (filePath, destination))
else:
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (filePath))
# FIXME: instead of passing a sym parameter, split copy and copytree into 4 different function
def copy(source, destination, sym = True):
'''recursively copy a "source" file or directory to "destination"'''
for filePath in glob.glob(source):
if isFile(filePath) and not isLink(filePath):
try:
shutil.copy(filePath, destination)
except IOError:
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (filePath, destination))
elif isLink(filePath) and sym:
if isDirectory(destination):
os.symlink(os.readlink(filePath), join_path(destination, os.path.basename(filePath)))
else:
if isFile(destination):
os.remove(destination)
os.symlink(os.readlink(filePath), destination)
elif isLink(filePath) and not sym:
if isDirectory(filePath):
copytree(filePath, destination)
else:
shutil.copy(filePath, destination)
elif isDirectory(filePath):
copytree(filePath, destination, sym)
else:
error(_('ActionsAPI [copy]: File %s does not exist.') % filePath)
def copytree(source, destination, sym = True):
'''recursively copy an entire directory tree rooted at source'''
if isDirectory(source):
if os.path.exists(destination):
if isDirectory(destination):
copytree(source, join_path(destination, os.path.basename(source.strip('/'))))
return
else:
copytree(source, join_path(destination, os.path.basename(source)))
return
try:
shutil.copytree(source, destination, sym)
except OSError, e:
error(_('ActionsAPI [copytree] %s to %s: %s') % (source, destination, e))
else:
error(_('ActionsAPI [copytree]: Directory %s doesn\'t exists.') % (source))
def touch(filePath):
'''changes the access time of the 'filePath', or creates it if it is not exist'''
if glob.glob(filePath):
for f in glob.glob(filePath):
os.utime(f, None)
else:
try:
f = open(filePath, 'w')
f.close()
except IOError:
error(_('ActionsAPI [touch]: Permission denied: %s') % (filePath))
def cd(directoryName = ''):
'''change directory'''
current = os.getcwd()
if directoryName:
os.chdir(directoryName)
else:
os.chdir(os.path.dirname(current))
def ls(source):
'''listdir'''
if os.path.isdir(source):
return os.listdir(source)
else:
return glob.glob(source)
def export(key, value):
'''export environ variable'''
os.environ[key] = value
def isLink(filePath):
'''return True if filePath refers to a symbolic link'''
return os.path.islink(filePath)
def isFile(filePath):
'''return True if filePath is an existing regular file'''
return os.path.isfile(filePath)
def isDirectory(filePath):
'''Return True if filePath is an existing directory'''
return os.path.isdir(filePath)
def realPath(filePath):
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
return os.path.realpath(filePath)
def baseName(filePath):
'''return the base name of pathname filePath'''
return os.path.basename(filePath)
def dirName(filePath):
'''return the directory name of pathname path'''
return os.path.dirname(filePath)
def system(command):
command = string.join(string.split(command))
return run_logged(command)
@@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import os
from copy import deepcopy
# Pisi-Core Modules
import pisi.context as ctx
# Set individual information, that are generally needed for ActionsAPI
def exportFlags():
'''General flags used in actions API.'''
# first reset environ
os.environ = {}
os.environ = deepcopy(ctx.config.environ)
# Build systems depend on these environment variables. That is why
# we export them instead of using as (instance) variables.
values = ctx.config.values
os.environ['HOST'] = values.build.host
os.environ['CFLAGS'] = values.build.cflags
os.environ['CXXFLAGS'] = values.build.cxxflags
os.environ['LDFLAGS'] = values.build.ldflags
os.environ['USER_LDFLAGS'] = values.build.ldflags
os.environ['JOBS'] = values.build.jobs
class Env(object):
'''General environment variables used in actions API'''
def __init__(self):
exportFlags()
self.__vars = {
'pkg_dir': 'PKG_DIR',
'work_dir': 'WORK_DIR',
'install_dir': 'INSTALL_DIR',
'src_name': 'SRC_NAME',
'src_version': 'SRC_VERSION',
'src_release': 'SRC_RELEASE',
'host': 'HOST',
'cflags': 'CFLAGS',
'cxxflags': 'CXXFLAGS',
'ldflags': 'LDFLAGS',
'jobs': 'JOBS'
}
def __getattr__(self, attr):
# Using environment variables is somewhat tricky. Each time
# you need them you need to check for their value.
if self.__vars.has_key(attr):
return os.getenv(self.__vars[attr])
else:
return None
class Dirs:
'''General directories used in actions API.'''
# TODO: Eventually we should consider getting these from a/the
# configuration file
doc = 'usr/share/doc'
sbin = 'usr/sbin'
man = 'usr/share/man'
info = 'usr/share/info'
data = 'usr/share'
conf = 'etc'
localstate = 'var/lib'
defaultprefix = 'usr'
# These should be owned by object not the class. Or else Python
# will bug us with NoneType errors because of uninitialized
# context (ctx) because of the import in build.py.
def __init__(self):
self.values = ctx.config.values
self.kde = self.values.dirs.kde_dir
self.qt = self.values.dirs.qt_dir
# As we import this module from build.py, we can't init glb as a
# singleton here. Or else Python will bug us with NoneType errors
# because of uninitialized context (ctx) because of exportFlags().
#
# We import this modue from build.py becase we need to reset/init glb
# for each build. # See bug #2575
glb = None
def initVariables():
global glb
ctx.env = Env()
ctx.dirs = Dirs()
glb = ctx
-515
View File
@@ -1,515 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
"""Top level PiSi interfaces. a facade to the entire PiSi system"""
import os
import sys
import logging
import logging.handlers
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.uri import URI
import pisi.util as util
import pisi.dependency as dependency
import pisi.pgraph as pgraph
import pisi.operations as operations
import pisi.packagedb as packagedb
import pisi.repodb
import pisi.installdb
import pisi.sourcedb
import pisi.component as component
from pisi.index import Index
import pisi.cli
from pisi.operations import install, remove, upgrade, emerge
from pisi.operations import plan_install_pkg_names as plan_install
from pisi.operations import plan_remove, plan_upgrade, upgrade_base
from pisi.build import build_until
from pisi.atomicoperations import resurrect_package, build
from pisi.metadata import MetaData
from pisi.files import Files
from pisi.file import File
import pisi.search
import pisi.lockeddbshelve as shelve
from pisi.version import Version
class Error(pisi.Error):
pass
def init(database = True, write = True,
options = None, ui = None, comar = True,
stdout = None, stderr = None,
comar_sockname = None):
"""Initialize PiSi subsystem"""
# UI comes first
if ui is None:
from pisi.cli import CLI
if options:
ctx.ui = CLI(options.debug, options.verbose)
else:
ctx.ui = CLI()
else:
ctx.ui = ui
if os.access('/var/log', os.W_OK):
handler = logging.handlers.RotatingFileHandler('/var/log/pisi.log')
#handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
ctx.log = logging.getLogger('pisi')
ctx.log.addHandler(handler)
ctx.loghandler = handler
ctx.log.setLevel(logging.DEBUG)
else:
ctx.log = None
# If given define stdout and stderr. Needed by buildfarm currently
# but others can benefit from this too.
if stdout:
ctx.stdout = stdout
if stderr:
ctx.stderr = stderr
import pisi.config
ctx.config = pisi.config.Config(options)
# TODO: this is definitely not dynamic beyond this point!
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
# This is for YALI, used in comariface.py:make_com()
ctx.comar_sockname = comar_sockname
# initialize repository databases
ctx.database = database
if database:
shelve.init_dbenv(write=write)
ctx.repodb = pisi.repodb.init()
ctx.installdb = pisi.installdb.init()
ctx.filesdb = pisi.files.FilesDB()
ctx.componentdb = pisi.component.ComponentDB()
ctx.packagedb = packagedb.init_db()
ctx.sourcedb = pisi.sourcedb.init()
pisi.search.init(['summary', 'description'], ['en', 'tr'])
else:
ctx.repodb = None
ctx.installdb = None
ctx.filesdb = None
ctx.componentdb = None
ctx.packagedb = None
ctx.sourcedb = None
ctx.ui.debug('PiSi API initialized')
ctx.initialized = True
def finalize():
if ctx.initialized:
if ctx.log:
ctx.loghandler.flush()
ctx.log.removeHandler(ctx.loghandler)
pisi.repodb.finalize()
pisi.installdb.finalize()
if ctx.filesdb != None:
ctx.filesdb.close()
ctx.filesdb = None
if ctx.componentdb != None:
ctx.componentdb.close()
ctx.componentdb = None
if ctx.packagedb:
packagedb.finalize_db()
ctx.packagedb = None
if ctx.sourcedb:
pisi.sourcedb.finalize()
ctx.sourcedb = None
pisi.search.finalize()
if ctx.dbenv:
ctx.dbenv.close()
ctx.dbenv_lock.close()
if ctx.build_leftover and os.path.exists(ctx.build_leftover):
os.unlink(ctx.build_leftover)
ctx.ui.debug('PiSi API finalized')
ctx.ui.close()
ctx.initialized = False
def list_installed():
'''returns a set of installed package names'''
return set(ctx.installdb.list_installed())
def list_available(repo = None):
'''returns a set of available package names'''
return set(ctx.packagedb.list_packages(repo = repo))
def list_upgradable():
ignore_build = ctx.get_option('ignore_build_no')
return filter(pisi.operations.is_upgradable, ctx.installdb.list_installed())
def package_graph(A, repo = pisi.itembyrepodb.installed, ignore_installed = False):
"""Construct a package relations graph, containing
all dependencies of packages A, if ignore_installed
option is True, then only uninstalled deps will
be added."""
ctx.ui.debug('A = %s' % str(A))
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb, repo) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
#state = {}
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x, repo)
#print pkg
for dep in pkg.runtimeDependencies():
if ignore_installed:
if dependency.installed_satisfies_dep(dep):
continue
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
return G_f
def generate_install_order(A):
# returns the install order of the given install package list with any extra
# dependency that is also going to be installed
G_f, order = plan_install(A, ignore_conflicts = True)
return order
def generate_remove_order(A):
# returns the remove order of the given removal package list with any extra
# reverse dependency that is also going to be removed
G_f, order = plan_remove(A)
return order
def generate_upgrade_order(A):
# returns the upgrade order of the given upgrade package list with any needed extra
# dependency
G_f, order = plan_upgrade(A, ignore_conflicts = True)
return order
def generate_base_upgrade(A):
# all the packages of the system.base must be installed on the system.
# method returns the currently needed system.base component install and
# upgrade needs
base = upgrade_base(A)
return list(base)
def configure_pending():
# start with pending packages
# configure them in reverse topological order of dependency
A = ctx.installdb.list_pending()
G_f = pgraph.PGraph(ctx.packagedb, pisi.itembyrepodb.installed) # construct G_f
for x in A.keys():
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B.keys():
pkg = ctx.packagedb.get_package(x, pisi.itembyrepodb.installed)
for dep in pkg.runtimeDependencies():
if dep.package in G_f.vertices():
G_f.add_dep(x, dep)
B = Bp
if ctx.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
try:
import pisi.comariface as comariface
for x in order:
if ctx.installdb.is_installed(x):
pkginfo = A[x]
pkgname = util.package_name(x, pkginfo.version,
pkginfo.release,
False,
False)
pkg_path = util.join_path(ctx.config.lib_dir(),
'package', pkgname)
m = MetaData()
metadata_path = util.join_path(pkg_path, ctx.const.metadata_xml)
m.read(metadata_path)
# FIXME: we need a full package info here!
pkginfo.name = x
ctx.ui.notify(pisi.ui.configuring, name = pkginfo.name)
pisi.comariface.post_install(
pkginfo.name,
m.package.providesComar,
util.join_path(pkg_path, ctx.const.comar_dir),
util.join_path(pkg_path, ctx.const.metadata_xml),
util.join_path(pkg_path, ctx.const.files_xml),
)
ctx.ui.notify(pisi.ui.configured, package = pkginfo, files = None)
ctx.installdb.clear_pending(x)
except ImportError:
raise Error(_("comar package is not fully installed"))
def info(package, installed = False):
if package.endswith(ctx.const.package_suffix):
return info_file(package)
else:
return info_name(package, installed)
def info_file(package_fn):
from package import Package
if not os.path.exists(package_fn):
raise Error (_('File %s not found') % package_fn)
package = Package(package_fn)
package.read()
return package.metadata, package.files
def info_name(package_name, installed=False):
"""fetch package information for a package"""
if installed:
package = ctx.packagedb.get_package(package_name, pisi.itembyrepodb.installed)
else:
package, repo = ctx.packagedb.get_package_repo(package_name, pisi.itembyrepodb.repos)
repostr = repo
from pisi.metadata import MetaData
metadata = MetaData()
metadata.package = package
#FIXME: get it from sourcedb if available
metadata.source = None
#TODO: fetch the files from server if possible (wow, you maniac -- future exa)
if installed and ctx.installdb.is_installed(package.name):
try:
files = ctx.installdb.files(package.name)
except pisi.Error, e:
ctx.ui.warning(e)
files = None
else:
files = None
return metadata, files
def search_package_names(query):
r = set()
packages = ctx.packagedb.list_packages()
for pkgname in packages:
if query in pkgname:
r.add(pkgname)
return r
def search_package_terms(terms, lang = None, search_names = True, repo = pisi.itembyrepodb.all):
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
r1 = pisi.search.query_terms('summary', lang, terms, repo = repo)
r2 = pisi.search.query_terms('description', lang, terms, repo = repo)
r = r1.union(r2)
if search_names:
for term in terms:
r |= search_package_names(term)
return r
def search_package(query, lang = None, search_names = True, repo = pisi.itembyrepodb.all):
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
r1 = pisi.search.query('summary', lang, query, repo = repo)
r2 = pisi.search.query('description', lang, query, repo = repo)
r = r1.union(r2)
if search_names:
r |= search_package_names(query)
return r
def check(package):
md, files = info(package, True)
corrupt = []
for file in files.list:
if file.hash and file.type != "config" \
and not os.path.islink('/' + file.path):
ctx.ui.info(_("Checking /%s ") % file.path, noln=True, verbose=True)
if file.hash != util.sha1_file('/' + file.path):
corrupt.append(file)
ctx.ui.info(_("\nCorrupt file: %s") % file, noln=True)
else:
ctx.ui.info(_("OK"), verbose=True)
return corrupt
def index(dirs=None, output='pisi-index.xml', skip_sources=False, skip_signing=False):
"""accumulate PiSi XML files in a directory"""
index = Index()
index.distribution = None
if not dirs:
dirs = ['.']
for repo_dir in dirs:
repo_dir = str(repo_dir)
ctx.ui.info(_('* Building index of PiSi files under %s') % repo_dir)
index.index(repo_dir, skip_sources)
if skip_signing:
index.write(output, sha1sum=True, compress=File.bz2, sign=None)
else:
index.write(output, sha1sum=True, compress=File.bz2, sign=File.detached)
ctx.ui.info(_('* Index file written'))
def add_repo(name, indexuri, at = None):
if ctx.repodb.has_repo(name):
raise Error(_('Repo %s already present.') % name)
else:
repo = pisi.repodb.Repo(URI(indexuri))
ctx.repodb.add_repo(name, repo, at = at)
ctx.ui.info(_('Repo %s added to system.') % name)
def remove_repo(name):
if ctx.repodb.has_repo(name):
ctx.repodb.remove_repo(name)
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
ctx.ui.info(_('Repo %s removed from system.') % name)
else:
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
% name)
def list_repos():
return ctx.repodb.list()
def update_repo(repo, force=False):
ctx.ui.info(_('* Updating repository: %s') % repo)
ctx.ui.notify(pisi.ui.updatingrepo, name = repo)
index = Index()
if ctx.repodb.has_repo(repo):
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
try:
index.read_uri_of_repo(repouri, repo)
except pisi.file.AlreadyHaveException, e:
ctx.ui.info(_('No updates available for repository %s.') % repo)
if force:
ctx.ui.info(_('Updating database at any rate as requested'))
index.read_uri_of_repo(repouri, repo, force = force)
else:
return
try:
index.check_signature(repouri, repo)
except pisi.file.NoSignatureFound, e:
ctx.ui.warning(e)
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
ctx.ui.info(_('* Package database updated.'))
else:
raise Error(_('No repository named %s found.') % repo)
def delete_cache():
util.clean_dir(ctx.config.packages_dir())
util.clean_dir(ctx.config.archives_dir())
util.clean_dir(ctx.config.tmp_dir())
def rebuild_repo(repo):
ctx.ui.info(_('* Rebuilding \'%s\' named repo... ') % repo)
if ctx.repodb.has_repo(repo):
repouri = URI(ctx.repodb.get_repo(repo).indexuri.get_uri())
indexname = repouri.filename()
index = Index()
indexpath = pisi.util.join_path(ctx.config.index_dir(), repo, indexname)
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
pisi.util.clean_dir(tmpdir)
pisi.util.check_dir(tmpdir)
try:
index.read_uri(indexpath, tmpdir, force=True) # don't look for sha1sum there
except IOError, e:
ctx.ui.warning(_("Input/Output error while reading %s: %s") % (indexpath, unicode(e)))
return
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
else:
raise Error(_('No repository named %s found.') % repo)
def rebuild_db(files=False):
assert ctx.database == False
# Bug 2596
# finds and cleans duplicate package directories under '/var/lib/pisi/package'
# deletes the _older_ versioned package directories.
def clean_duplicates():
i_version = {} # installed versions
replica = []
for pkg in os.listdir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package')):
(name, ver) = util.parse_package_name(pkg)
if i_version.has_key(name):
if Version(ver) > Version(i_version[name]):
# found a greater version, older one is a replica
replica.append(name + '-' + i_version[name])
i_version[name] = ver
else:
# found an older version which is a replica
replica.append(name + '-' + ver)
else:
i_version[name] = ver
for pkg in replica:
pisi.util.clean_dir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package', pkg))
def destroy(files):
#TODO: either don't delete version files here, or remove force flag...
import bsddb3.db
for db in os.listdir(ctx.config.db_dir()):
if db.endswith('.bdb'):# or db.startswith('log'): # delete only db files
if db.startswith('files') or db.startswith('filesdbversion'):
clean = files
else:
clean = True
if clean:
fn = pisi.util.join_path(ctx.config.db_dir(), db)
#NB: there is a parameter bug with python-bsddb3, fixed in pardus
ctx.dbenv.dbremove(file=fn, flags=bsddb3.db.DB_AUTO_COMMIT)
def reload_packages(files, txn):
for package_fn in os.listdir( pisi.util.join_path( ctx.config.lib_dir(),
'package' ) ):
if not package_fn == "scripts":
ctx.ui.debug('Resurrecting %s' % package_fn)
pisi.api.resurrect_package(package_fn, files, txn)
def reload_indices(txn):
index_dir = ctx.config.index_dir()
if os.path.exists(index_dir): # it may have been erased, or we may be upgrading from a previous version -- exa
for repo in os.listdir(index_dir):
indexuri = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, 'uri')
indexuri = open(indexuri, 'r').readline()
pisi.api.add_repo(repo, indexuri)
pisi.api.rebuild_repo(repo)
# check db schema versions
try:
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=False)
except:
files = True # exception means the files db version was wrong
pisi.lockeddbshelve.init_dbenv(write=True, writeversion=True)
destroy(files) # bye bye
# save parameters and shutdown pisi
options = ctx.config.options
ui = ctx.ui
comar = ctx.comar
finalize()
# construct new database
init(database=True, options=options, ui=ui, comar=comar)
clean_duplicates()
reload_packages(files, None)
reload_indices(None)
-411
View File
@@ -1,411 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Archive module provides access to regular archive file types."""
# standard library modules
import os
import stat
import shutil
import tarfile
import zipfile
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi modules
import pisi
import pisi.util as util
import pisi.context as ctx
class ArchiveError(pisi.Error):
pass
class LZMAError(pisi.Error):
def __init__(self, err):
pisi.Error.__init__(self, _("An error has occured while running LZMA:\n%s") % err)
class ArchiveBase(object):
"""Base class for Archive classes."""
def __init__(self, file_path, atype):
self.file_path = file_path
self.type = atype
def unpack(self, target_dir, clean_dir = False):
self.target_dir = target_dir
# first we check if we need to clean-up our working env.
if os.path.exists(self.target_dir) and clean_dir:
util.clean_dir(self.target_dir)
os.makedirs(self.target_dir)
class ArchiveBinary(ArchiveBase):
"""ArchiveBinary handles binary archive files (usually distrubuted as
.bin files)"""
def __init__(self, file_path, arch_type = "binary"):
super(ArchiveBinary, self).__init__(file_path, arch_type)
def unpack(self, target_dir, clean_dir = False):
super(ArchiveBinary, self).unpack(target_dir, clean_dir)
# we can't unpack .bin files. we'll just move them to target
# directory and leave the dirty job to actions.py ;)
import shutil
target_file = os.path.join(target_dir, os.path.basename(self.file_path))
shutil.copyfile(self.file_path, target_file)
class ArchiveTar(ArchiveBase):
"""ArchiveTar handles tar archives depending on the compression
type. Provides access to tar, tar.gz and tar.bz2 files.
This class provides the unpack magic for tar archives."""
def __init__(self, file_path, arch_type = "tar", no_same_permissions = True):
super(ArchiveTar, self).__init__(file_path, arch_type)
self.tar = None
self.no_same_permissions = no_same_permissions
def unpack(self, target_dir, clean_dir = False):
"""Unpack tar archive to a given target directory(target_dir)."""
super(ArchiveTar, self).unpack(target_dir, clean_dir)
self.unpack_dir(target_dir)
def unpack_dir(self, target_dir):
rmode = ""
if self.type == 'tar':
rmode = 'r:'
elif self.type == 'targz':
rmode = 'r:gz'
elif self.type == 'tarbz2':
rmode = 'r:bz2'
elif self.type == 'tarlzma':
rmode = 'r:'
self.file_path = self.file_path.rstrip(ctx.const.lzma_suffix)
ret, out, err = util.run_batch("lzma d %s %s" % (self.file_path + ctx.const.lzma_suffix,
self.file_path))
if ret != 0:
raise LZMAError(err)
else:
raise ArchiveError(_("Archive type not recognized"))
self.tar = tarfile.open(self.file_path, rmode)
oldwd = os.getcwd()
os.chdir(target_dir)
install_tar_path = util.join_path(ctx.config.tmp_dir(), ctx.const.install_tar)
for tarinfo in self.tar:
# Installing packages (especially shared libraries) is a
# bit tricky. You should also change the inode if you
# change the file, cause the file is opened allready and
# accessed. Removing and creating the file will also
# change the inode and will do the trick (in fact, old
# file will be deleted only when its closed).
#
# Also, tar.extract() doesn't write on symlinks... Not any
# more :).
if self.file_path == install_tar_path:
if os.path.isfile(tarinfo.name) or os.path.islink(tarinfo.name):
try:
os.unlink(tarinfo.name)
except OSError, e:
ctx.ui.warning(e)
self.tar.extract(tarinfo)
# tarfile.extract does not honor umask. It must be honored explicitly.
# see --no-same-permissions option of tar(1), which is the deafult
# behaviour.
#
# Note: This is no good while installing a pisi package. Thats why
# this is optional.
if self.no_same_permissions and not os.path.islink(tarinfo.name):
os.chmod(tarinfo.name, tarinfo.mode & ~ctx.const.umask)
os.chdir(oldwd)
self.close()
def add_to_archive(self, file_name, arc_name=None):
"""Add file or directory path to the tar archive"""
if not self.tar:
if self.type == 'tar':
wmode = 'w:'
elif self.type == 'targz':
wmode = 'w:gz'
elif self.type == 'tarbz2':
wmode = 'w:bz2'
elif self.type == 'tarlzma':
wmode = 'w:'
self.file_path = self.file_path.rstrip(ctx.const.lzma_suffix)
else:
raise ArchiveError(_("Archive type not recognized"))
self.tar = tarfile.open(self.file_path, wmode)
self.tar.add(file_name, arc_name)
def close(self):
self.tar.close()
if self.tar.mode == 'wb' and self.type == 'tarlzma':
batch = None
if ctx.config.values.build.compressionlevel:
batch = "lzmash -%s %s" % (ctx.config.values.build.compressionlevel, self.file_path)
else:
batch = "lzmash %s" % self.file_path
ret, out, err = util.run_batch(batch)
if ret != 0:
raise LZMAError(err)
class MyZipFile(zipfile.ZipFile):
def decompressToFile(self, name, outname):
import zlib
import binascii
block_size = 1024 * 1024 * 2
if self.mode not in ("r", "a"):
raise RuntimeError, 'read() requires mode "r" or "a"'
if not self.fp:
raise RuntimeError, \
"Attempt to read ZIP archive that was already closed"
zinfo = self.getinfo(name)
filepos = self.fp.tell()
self.fp.seek(zinfo.file_offset, 0)
destfile = file(outname, 'wb')
if zinfo.compress_type == zipfile.ZIP_STORED:
total_read = 0
crc = None
while total_read < zinfo.compress_size:
if zinfo.compress_size - total_read < block_size:
block_size = zinfo.compress_size - total_read
buff = self.fp.read(block_size)
destfile.write(buff)
total_read += (block_size)
if crc:
crc = binascii.crc32(buff, crc)
else:
crc = binascii.crc32(buff)
destfile.close()
self.fp.seek(filepos, 0)
if crc and crc != zinfo.CRC:
raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name
return
elif zinfo.compress_type != zipfile.ZIP_DEFLATED:
raise BadZipfile, \
"Unsupported compression method %d for file %s" % \
(zinfo.compress_type, name)
if not zlib:
raise RuntimeError, \
"De-compression requires the (missing) zlib module"
# zlib compress/decompress code by Jeremy Hylton of CNRI
dc = zlib.decompressobj(-15)
total_read = 0
crc = None
while total_read < zinfo.compress_size:
if zinfo.compress_size - total_read < block_size:
block_size = zinfo.compress_size - total_read
buff = self.fp.read(block_size)
dcbuff = dc.decompress(dc.unconsumed_tail + buff)
destfile.write(dcbuff)
total_read += block_size
if crc:
crc = binascii.crc32(dcbuff, crc)
else:
crc = binascii.crc32(dcbuff)
# need to feed in unused pad byte so that zlib won't choke
ex = dc.decompress(dc.unconsumed_tail + 'Z') + dc.flush()
if ex:
if crc:
crc = binascii.crc32(ex, crc)
else:
crc = binascii.crc32(ex)
destfile.write(ex)
if crc and crc != zinfo.CRC:
raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name
destfile.close()
self.fp.seek(filepos, 0)
class ArchiveZip(ArchiveBase):
"""ArchiveZip handles zip archives.
Being a zip archive PiSi packages also use this class
extensively. This class provides unpacking and packing magic for
zip archives."""
symmagic = 2716663808 #long of hex val '0xA1ED0000L'
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
super(ArchiveZip, self).__init__(file_path, arch_type)
self.zip_obj = MyZipFile(self.file_path, mode)
def close(self):
"""Close the zip archive."""
self.zip_obj.close()
def add_to_archive(self, file_name, arc_name=None):
"""Add file or directory path to the zip file"""
# It's a pity that zipfile can't handle unicode strings. Grrr!
file_name = str(file_name)
if os.path.isdir(file_name) and not os.path.islink(file_name):
self.zip_obj.writestr(file_name + '/', '')
attr_obj = self.zip_obj.getinfo(file_name + '/')
attr_obj.external_attr = stat.S_IMODE(os.stat(file_name)[0]) << 16L
for f in os.listdir(file_name):
self.add_to_archive(os.path.join(file_name, f))
else:
if os.path.islink(file_name):
dest = os.readlink(file_name)
attr = zipfile.ZipInfo()
attr.filename = file_name
attr.create_system = 3
attr.external_attr = self.symmagic
self.zip_obj.writestr(attr, dest)
else:
self.zip_obj.write(file_name, arc_name, zipfile.ZIP_DEFLATED)
if not arc_name:
zinfo = self.zip_obj.getinfo(file_name)
else:
zinfo = self.zip_obj.getinfo(arc_name)
zinfo.create_system = 3
def add_basename_to_archive(self, file_name):
"""Add only the basepath to the zip file. For example; if the given
file_name parameter is /usr/local/bin/somedir, this function
will create only the base directory/file somedir in the
archive."""
cwd = os.getcwd()
path_name = os.path.dirname(file_name)
file_name = os.path.basename(file_name)
if path_name:
os.chdir(path_name)
self.add_to_archive(file_name)
os.chdir(cwd)
def has_file(self, file_path):
""" Returns true if file_path is member of the zip archive"""
return file_path in self.zip_obj.namelist()
def read_file(self, file_path):
return self.zip_obj.read(file_path)
def unpack_file_cond(self, pred, target_dir, archive_root = ''):
"""Unpack/Extract files according to predicate function
pred: filename -> bool
unpacks stuff into target_dir and only extracts files
from archive_root, treating it as the archive root"""
zip_obj = self.zip_obj
for info in zip_obj.infolist():
if pred(info.filename): # check if condition holds
# below code removes that, so we find it here
is_dir = info.filename.endswith('/')
# calculate output file name
if archive_root == '':
outpath = info.filename
else:
# change archive_root
if util.subpath(archive_root, info.filename):
outpath = util.removepathprefix(archive_root,
info.filename)
else:
continue # don't extract if not under
ofile = os.path.join(target_dir, outpath)
if is_dir: # this is a directory
d = os.path.join(target_dir, outpath)
if not os.path.isdir(d):
os.makedirs(d)
perm = info.external_attr
perm &= 0xFFFF0000
perm >>= 16
perm |= 0x00000100
os.chmod(d, perm)
continue
# check that output dir is present
util.check_dir(os.path.dirname(ofile))
# remove output file we might be overwriting.
# (also check for islink? for broken symlinks...)
if os.path.isfile(ofile) or os.path.islink(ofile):
os.remove(ofile)
if info.external_attr == self.symmagic:
if os.path.isdir(ofile):
shutil.rmtree(ofile) # a rare case, the file used to be a dir, now it is a symlink!
target = zip_obj.read(info.filename)
os.symlink(target, ofile)
else:
perm = info.external_attr
perm &= 0x08FF0000
perm >>= 16
perm |= 0x00000100
zip_obj.decompressToFile(info.filename, ofile)
os.chmod(ofile, perm)
def unpack_files(self, paths, target_dir):
self.unpack_file_cond(lambda f:f in paths, target_dir)
def unpack_dir(self, path, target_dir):
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir)
def unpack_dir_flat(self, path, target_dir):
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir, path)
def unpack(self, target_dir, clean_dir=False):
super(ArchiveZip, self).unpack(target_dir, clean_dir)
self.unpack_file_cond(lambda f: True, target_dir)
self.close()
return
class Archive:
"""Archive is the main factory for ArchiveClasses, regarding the
Abstract Factory Pattern :)."""
def __init__(self, file_path, arch_type):
"""accepted archive types:
targz, tarbz2, zip, tar"""
handlers = {
'targz': ArchiveTar,
'tarbz2': ArchiveTar,
'tarlzma': ArchiveTar,
'tar': ArchiveTar,
'zip': ArchiveZip,
'binary': ArchiveBinary
}
self.archive = handlers.get(arch_type)(file_path, arch_type)
def unpack(self, target_dir, clean_dir = False):
self.archive.unpack(target_dir, clean_dir)
def unpack_files(self, files, target_dir):
self.archive.unpack_files(files, target_dir)
-571
View File
@@ -1,571 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
"""Atomic package operations such as install/remove/upgrade"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import sys
import os
import bsddb3.db as db
import pisi
import pisi.context as ctx
import pisi.packagedb as packagedb
import pisi.dependency as dependency
import pisi.util as util
from pisi.specfile import *
from pisi.metadata import MetaData
from pisi.files import Files
from pisi.uri import URI
import pisi.ui
from pisi.version import Version
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
pass
# single package operations
class AtomicOperation(object):
def __init__(self, ignore_dep = None):
#self.package = package
if ignore_dep==None:
self.ignore_dep = ctx.config.get_option('ignore_dependency')
else:
self.ignore_dep = ignore_dep
def run(self, package):
"perform an atomic package operation"
pass
class Install(AtomicOperation):
"Install class, provides install routines for pisi packages"
@staticmethod
def from_name(name, ignore_dep = None):
# download package and return an installer object
# find package in repository
repo = ctx.packagedb.which_repo(name)
if repo:
ctx.ui.info(_("Package %s found in repository %s") % (name, repo))
repo = ctx.repodb.get_repo(repo)
pkg = ctx.packagedb.get_package(name)
# FIXME: let pkg.packageURI be stored as URI type rather than string
pkg_uri = URI(pkg.packageURI)
if pkg_uri.is_absolute_path():
pkg_path = str(pkg.packageURI)
else:
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(pkg_uri.path()))
ctx.ui.info(_("Package URI: %s") % pkg_path, verbose=True)
return Install(pkg_path, ignore_dep)
else:
raise Error(_("Package %s not found in any active repository.") % name)
def __init__(self, package_fname, ignore_dep = None, ignore_file_conflicts = None):
"initialize from a file name"
super(Install, self).__init__(ignore_dep)
if not ignore_file_conflicts:
ignore_file_conflicts = ctx.get_option('ignore_file_conflicts')
self.ignore_file_conflicts = ignore_file_conflicts
self.package_fname = package_fname
self.package = pisi.package.Package(package_fname)
self.package.read()
self.metadata = self.package.metadata
self.files = self.package.files
self.pkginfo = self.metadata.package
ctx.ui.notify(pisi.ui.installing, name=self.pkginfo.name)
def install(self, ask_reinstall = True):
if ctx.get_option('fetch_only'):
return
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
(self.pkginfo.name, self.pkginfo.version,
self.pkginfo.release, self.pkginfo.build))
self.ask_reinstall = ask_reinstall
self.check_requirements()
self.check_relations()
self.check_reinstall()
self.extract_install()
self.store_pisi_files()
self.postinstall()
txn = ctx.dbenv.txn_begin()
try:
self.update_databases(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
ctx.ui.status()
if self.upgrade:
event = pisi.ui.upgraded
else:
event = pisi.ui.installed
ctx.ui.notify(event, package = self.pkginfo, files = self.files)
def check_requirements(self):
"""check system requirements"""
#TODO: IS THERE ENOUGH SPACE?
# what to do if / is split into /usr, /var, etc.
# check comar
if self.metadata.package.providesComar and ctx.comar:
import pisi.comariface as comariface
com = comariface.make_com()
def check_relations(self):
# check conflicts
for pkg in self.metadata.package.conflicts:
if ctx.installdb.is_installed(self.pkginfo):
raise Error(_("Package conflicts %s") % pkg)
# check dependencies
if not ctx.config.get_option('ignore_dependency'):
if not self.pkginfo.installable():
ctx.ui.error(_('Dependencies for %s not satisfied') %
self.pkginfo.name)
raise Error(_("Package not installable"))
# check if package is in database
# If it is not, put it into 3rd party packagedb
if not ctx.packagedb.has_package(self.pkginfo.name):
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.thirdparty)
# check file conflicts
file_conflicts = []
for file in self.files.list:
if ctx.filesdb.has_file(file.path):
pkg, existing_file = ctx.filesdb.get_file(file.path)
dst = pisi.util.join_path(ctx.config.dest_dir(), file.path)
if pkg != self.pkginfo.name and not os.path.isdir(dst):
file_conflicts.append( (pkg, existing_file) )
if file_conflicts:
file_conflicts_str = ""
for (pkg, existing_file) in file_conflicts:
file_conflicts_str += _("/%s from %s package\n") % (existing_file.path, pkg)
msg = _('File conflicts:\n%s') % file_conflicts_str
if self.ignore_file_conflicts:
ctx.ui.warning(msg)
else:
raise Error(msg)
def check_reinstall(self):
"check reinstall, confirm action, and schedule reinstall"
pkg = self.pkginfo
self.reinstall = False
self.upgrade = False
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
#FIXME: consider REPOSITORY instead of DISTRIBUTION -- exa
#ipackage = ctx.packagedb.get_package(pkg.name, pisi.itembyrepodb.installed)
ipkg = ctx.installdb.get_info(pkg.name)
repomismatch = ipkg.distribution != pkg.distribution
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
# determine if same version
self.same_ver = False
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
if repomismatch or (not ibuild) or (not pkg.build) or ignore_build:
# we don't look at builds to compare two package versions
if Version(pkg.release) == Version(irelease):
self.same_ver = True
else:
if pkg.build == ibuild:
self.same_ver = True
if self.same_ver:
if self.ask_reinstall:
if not ctx.ui.confirm(_('Re-install same version package?')):
raise Error(_('Package re-install declined'))
else:
upgrade = False
# is this an upgrade?
# determine and report the kind of upgrade: version, release, build
if Version(pkg.version) > Version(iversion):
ctx.ui.info(_('Upgrading to new upstream version'))
upgrade = True
elif Version(pkg.release) > Version(irelease):
ctx.ui.info(_('Upgrading to new distribution release'))
upgrade = True
elif ((not ignore_build) and ibuild and pkg.build
and pkg.build > ibuild):
ctx.ui.info(_('Upgrading to new distribution build'))
upgrade = True
self.upgrade = upgrade
# is this a downgrade? confirm this action.
if self.ask_reinstall and (not upgrade):
if Version(pkg.version) < Version(iversion):
#x = _('Downgrade to old upstream version?')
x = None
elif Version(pkg.release) < Version(irelease):
x = _('Downgrade to old distribution release?')
else:
x = _('Downgrade to old distribution build?')
if x and not ctx.ui.confirm(x):
raise Error(_('Package downgrade declined'))
# schedule for reinstall
self.old_files = ctx.installdb.files(pkg.name)
self.old_path = ctx.installdb.pkg_dir(pkg.name, iversion, irelease)
self.reinstall = True
self.remove_old = Remove(pkg.name)
self.remove_old.run_preremove()
def postinstall(self):
self.config_later = False
if ctx.comar:
import pisi.comariface
ctx.ui.notify(pisi.ui.configuring, name = self.pkginfo.name)
pisi.comariface.post_install(
self.pkginfo.name,
self.metadata.package.providesComar,
self.package.comar_dir(),
os.path.join(self.package.pkg_dir(), ctx.const.metadata_xml),
os.path.join(self.package.pkg_dir(), ctx.const.files_xml),
)
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
else:
self.config_later = True
def extract_install(self):
"unzip package in place"
ctx.ui.notify(pisi.ui.extracting, name = self.pkginfo.name)
config_changed = []
def check_config_changed(config):
changed = False
fpath = pisi.util.join_path(ctx.config.dest_dir(), config.path)
if os.path.exists(fpath) and not os.path.isdir(fpath):
if os.path.islink(fpath):
file = os.readlink(fpath)
if os.path.exists(file) and pisi.util.sha1_data(file) != config.hash:
changed = True
else:
if pisi.util.sha1_file(fpath) != config.hash:
changed = True
if changed:
config_changed.append(fpath)
if os.path.exists(fpath + '.old'):
os.unlink(fpath + '.old')
os.rename(fpath, fpath + '.old')
# old config files are kept as they are. New config files from the installed
# packages are saved with ".newconfig" string appended to their names.
def rename_configs():
for path in config_changed:
newconfig = path + '.newconfig'
oldconfig = path + '.old'
if os.path.exists(newconfig):
os.unlink(newconfig)
os.rename(path, newconfig)
os.rename(oldconfig, path)
# remove left over files from the old package.
def clean_leftovers():
new = set(map(lambda x: str(x.path), self.files.list))
old = set(map(lambda x: str(x.path), self.old_files.list))
leftover = old - new
old_fileinfo = {}
for fileinfo in self.old_files.list:
old_fileinfo[str(fileinfo.path)] = fileinfo
for path in leftover:
Remove.remove_file( old_fileinfo[path] )
if self.reinstall:
# get 'config' typed file objects
new = filter(lambda x: x.type == 'config', self.files.list)
old = filter(lambda x: x.type == 'config', self.old_files.list)
# get config path lists
newconfig = set(map(lambda x: str(x.path), new))
oldconfig = set(map(lambda x: str(x.path), old))
config_overlaps = newconfig & oldconfig
if config_overlaps:
files = filter(lambda x: x.path in config_overlaps, old)
for file in files:
check_config_changed(file)
else:
for file in self.files.list:
if file.type == 'config':
# there may be left over config files
check_config_changed(file)
self.package.extract_install(ctx.config.dest_dir())
if config_changed:
rename_configs()
if self.reinstall:
clean_leftovers()
def store_pisi_files(self):
"""put files.xml, metadata.xml, actions.py and COMAR scripts
somewhere in the file system. We'll need these in future..."""
if self.reinstall:
util.clean_dir(self.old_path)
ctx.ui.info(_('Storing %s, ') % ctx.const.files_xml, verbose=True)
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
ctx.ui.info(_('Storing %s.') % ctx.const.metadata_xml, verbose=True)
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
for pcomar in self.metadata.package.providesComar:
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
# comar prefix is added to the pkg_dir while extracting comar
# script file. so we'll use pkg_dir as destination.
ctx.ui.info(_('Storing %s') % fpath, verbose=True)
self.package.extract_file(fpath, self.package.pkg_dir())
def update_databases(self, txn):
"update databases"
if self.reinstall:
self.remove_old.remove_db(txn)
# installdb
ctx.installdb.install(self.metadata.package.name,
self.metadata.package.version,
self.metadata.package.release,
self.metadata.package.build,
self.metadata.package.distribution,
config_later = self.config_later,
txn = txn)
# filesdb
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn=txn)
# installed packages
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.installed, txn=txn)
def install_single(pkg, upgrade = False):
"""install a single package from URI or ID"""
url = URI(pkg)
# Check if we are dealing with a remote file or a real path of
# package filename. Otherwise we'll try installing a package from
# the package repository.
if url.is_remote_file() or os.path.exists(url.uri):
install_single_file(pkg, upgrade)
else:
install_single_name(pkg, upgrade)
# FIXME: Here and elsewhere pkg_location must be a URI
def install_single_file(pkg_location, upgrade = False):
"""install a package file"""
Install(pkg_location).install(not upgrade)
def install_single_name(name, upgrade = False):
"""install a single package from ID"""
install = Install.from_name(name)
install.install(not upgrade)
class Remove(AtomicOperation):
def __init__(self, package_name, ignore_dep = None):
super(Remove, self).__init__(ignore_dep)
self.package_name = package_name
self.package = ctx.packagedb.get_package(self.package_name, pisi.itembyrepodb.installed)
try:
self.files = ctx.installdb.files(self.package_name)
except pisi.Error, e:
# for some reason file was deleted, we still allow removes!
ctx.ui.error(unicode(e))
ctx.ui.warning(_('File list could not be read for package %s, continuing removal.') % package_name)
self.files = Files()
ctx.ui.notify(pisi.ui.removing, name=self.package_name)
def run(self):
"""Remove a single package"""
ctx.ui.status(_('Removing package %s') % self.package_name)
if not ctx.installdb.is_installed(self.package_name):
raise Exception(_('Trying to remove nonexistent package ')
+ self.package_name)
self.check_dependencies()
self.run_preremove()
progress = ctx.ui.Progress(len(self.files.list))
removed = 0
for fileinfo in self.files.list:
self.remove_file(fileinfo)
removed += 1
ctx.ui.display_progress(operation = "removing",
percent = progress.update(removed),
info = _("Removing package %s") % self.package_name)
txn = ctx.dbenv.txn_begin()
try:
self.remove_db(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
self.remove_pisi_files()
ctx.ui.status()
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
def check_dependencies(self):
#FIXME: why is this not implemented? -- exa
#we only have to check the dependencies to ensure the
#system will be consistent after this removal
pass
# is there any package who depends on this package?
@staticmethod
def remove_file(fileinfo):
fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path)
# we should check if the file belongs to another
# package (this can legitimately occur while upgrading
# two packages such that a file has moved from one package to
# another as in #2911)
if ctx.filesdb.has_file(fpath):
pkg, existing_file = ctx.filesdb.get_file(fpath)
if pkg != self.package_name:
ctx.ui.warning(_('Not removing conflicted file : %s') % fpath)
return
if fileinfo.permanent:
# do not remove precious files :)
pass
elif fileinfo.type == ctx.const.conf:
# config files are precious, leave them as they are
# unless they are the same as provided by package.
try:
if pisi.util.sha1_file(fpath) == fileinfo.hash:
os.unlink(fpath)
except pisi.util.FileError, e:
pass
else:
if os.path.isfile(fpath) or os.path.islink(fpath):
os.unlink(fpath)
elif os.path.isdir(fpath) and not os.listdir(fpath):
os.rmdir(fpath)
else:
ctx.ui.warning(_('Installed file %s is not exists on system [Probably you manually deleted]') % fpath)
return
# remove emptied directories
dpath = os.path.dirname(fpath)
while dpath != '/' and not os.listdir(dpath):
os.rmdir(dpath)
dpath = os.path.dirname(dpath)
def run_preremove(self):
if ctx.comar:
import pisi.comariface
pisi.comariface.pre_remove(
self.package_name,
os.path.join(self.package.pkg_dir(), ctx.const.metadata_xml),
os.path.join(self.package.pkg_dir(), ctx.const.files_xml),
)
def remove_pisi_files(self):
util.clean_dir(self.package.pkg_dir())
def remove_db(self, txn):
ctx.installdb.remove(self.package_name, txn)
ctx.filesdb.remove_files(self.files, txn)
pisi.packagedb.remove_tracking_package(self.package_name, txn)
def remove_single(package_name):
Remove(package_name).run()
def build(package):
# wrapper for build op
import pisi.build
return pisi.build.build(package)
def virtual_install(metadata, files, txn):
"""Recreate the package info for rebuilddb command"""
# installdb
ctx.installdb.install(metadata.package.name,
metadata.package.version,
metadata.package.release,
metadata.package.build,
metadata.package.distribution,
rebuild=True,
txn=txn)
# filesdb
if files:
ctx.filesdb.add_files(metadata.package.name, files, txn=txn)
# installed packages
ctx.packagedb.add_package(metadata.package, pisi.itembyrepodb.installed, txn=txn)
def resurrect_package(package_fn, write_files, txn = None):
"""Resurrect the package from xml files"""
from os.path import exists
metadata_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.metadata_xml)
if not exists(metadata_xml):
raise Error, _("Metadata XML '%s' cannot be found") % metadata_xml
metadata = MetaData()
metadata.read(metadata_xml)
errs = metadata.errors()
if errs:
util.Checks.print_errors(errs)
raise Error, _("MetaData format wrong (%s)") % package_fn
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
if write_files:
files_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.files_xml)
if not exists(files_xml):
raise Error, _("Files XML '%s' cannot be found") % files_xml
files = Files()
files.read(files_xml)
if files.errors():
raise Error, _("Invalid %s") % ctx.const.files_xml
else:
files = None
#import pisi.atomicoperations
def f(t):
pisi.atomicoperations.virtual_install(metadata, files, t)
ctx.txn_proc(f, txn)
ctx.ui.info(_('OK.'))
-975
View File
@@ -1,975 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
"""package building code"""
# python standard library
import os
import sys
import glob
from copy import deepcopy
from os.path import basename, dirname
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from pisi.specfile import SpecFile
import pisi.util as util
from pisi.util import join_path as join, parenturi
from pisi.file import File
import pisi.context as ctx
import pisi.dependency as dependency
import pisi.operations as operations
from pisi.sourcearchive import SourceArchive
from pisi.files import Files, FileInfo
from pisi.fetcher import fetch_url
from pisi.uri import URI
from pisi.metadata import MetaData
from pisi.package import Package
import pisi.component as component
import pisi.archive as archive
import pisi.actionsapi.variables
class Error(pisi.Error):
pass
# Helper Functions
def get_file_type(path, pinfo_list, install_dir):
"""Return the file type of a path according to the given PathInfo
list"""
Match = lambda x: [match for match in glob.glob(install_dir + x) if join(install_dir, path).find(match) > -1]
def Sort(x):
x.sort(reverse=True)
return x
best_matched_path = Sort([pinfo.path for pinfo in pinfo_list if Match(pinfo.path)])[0]
info = [pinfo for pinfo in pinfo_list if best_matched_path == pinfo.path][0]
return info.fileType, info.permanent
def check_path_collision(package, pkgList):
"""This function will check for collision of paths in a package with
the paths of packages in pkgList. The return value will be the
list containing the paths that collide."""
collisions = []
for pinfo in package.files:
for pkg in pkgList:
if pkg is package:
continue
for path in pkg.files:
# if pinfo.path is a subpath of path.path like
# the example below. path.path is marked as a
# collide. Exp:
# pinfo.path: /usr/share
# path.path: /usr/share/doc
if (path.path.endswith(ctx.const.ar_file_suffix) and ctx.get_option('create_static')) or \
(path.path.endswith(ctx.const.debug_file_suffix) and ctx.config.values.build.generatedebug):
# don't throw collision error for these files.
# we'll handle this in gen_files_xml..
continue
if util.subpath(pinfo.path, path.path):
collisions.append(path.path)
ctx.ui.debug(_('Path %s belongs in multiple packages') %
path.path)
return collisions
class Builder:
"""Provides the package build and creation routines"""
#FIXME: this class and every other class must use URLs as paths!
@staticmethod
def from_name(name):
# download package and return an installer object
# find package in repository
sf, reponame = ctx.sourcedb.get_spec_repo(name)
src = sf.source
if src:
src_uri = URI(src.sourceURI)
if src_uri.is_absolute_path():
src_path = str(src_uri)
else:
repo = ctx.repodb.get_repo(reponame)
#FIXME: don't use dirname to work on URLs
src_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(src_uri.path()))
ctx.ui.debug(_("Source URI: %s") % src_path)
return Builder(src_path)
else:
raise Error(_("Source %s not found in any active repository.") % name)
def __init__(self, specuri):
# process args
if not isinstance(specuri, URI):
specuri = URI(specuri)
# read spec file, we'll need it :)
self.set_spec_file(specuri)
if specuri.is_remote_file():
self.specdir = self.fetch_files()
else:
self.specdir = dirname(self.specuri.get_uri())
self.sourceArchive = SourceArchive(self.spec, self.pkg_work_dir())
self.set_environment_vars()
self.actionLocals = None
self.actionGlobals = None
self.srcDir = None
def set_spec_file(self, specuri):
if not specuri.is_remote_file():
specuri = URI(os.path.realpath(specuri.get_uri())) # FIXME: doesn't work for file://
self.specuri = specuri
spec = SpecFile()
spec.read(specuri, ctx.config.tmp_dir())
self.spec = spec
# directory accessor functions
# pkg_x_dir: per package directory for storing info type x
def pkg_dir(self):
"package build directory"
packageDir = self.spec.source.name + '-' + \
self.spec.getSourceVersion() + '-' + self.spec.getSourceRelease()
return util.join_path(ctx.config.dest_dir(), ctx.config.values.dirs.tmp_dir,
packageDir)
def pkg_work_dir(self):
return self.pkg_dir() + ctx.const.work_dir_suffix
def pkg_debug_dir(self):
return self.pkg_dir() + ctx.const.debug_dir_suffix
def pkg_install_dir(self):
return self.pkg_dir() + ctx.const.install_dir_suffix
def set_state(self, state):
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
open(stateFile, "w").write(state)
def get_state(self):
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
if not os.path.exists(stateFile): # no state
return None
return open(stateFile, "r").read()
def build(self):
"""Build the package in one shot."""
ctx.ui.status(_("Building PiSi source package: %s") % self.spec.source.name)
self.compile_action_script()
self.compile_comar_script()
# check if all patch files exists, if there are missing no need to unpack!
self.patch_exists()
self.check_build_dependencies()
self.fetch_component()
self.fetch_source_archive()
self.unpack_source_archive()
# apply the patches and prepare a source directory for build.
self.apply_patches()
self.run_setup_action()
self.run_build_action()
self.run_install_action()
# after all, we are ready to build/prepare the packages
return self.build_packages()
def set_environment_vars(self):
"""Sets the environment variables for actions API to use"""
# Each time a builder is created we must reset
# environment. See bug #2575
pisi.actionsapi.variables.initVariables()
env = {
"PKG_DIR": self.pkg_dir(),
"WORK_DIR": self.pkg_work_dir(),
"INSTALL_DIR": self.pkg_install_dir(),
"SRC_NAME": self.spec.source.name,
"SRC_VERSION": self.spec.getSourceVersion(),
"SRC_RELEASE": self.spec.getSourceRelease()
}
os.environ.update(env)
# First check icecream, if not found use ccache, no need to use both
# together (according to kde-wiki it cause performance loss)
if ctx.config.values.build.buildhelper == "icecream":
if os.path.exists("/opt/icecream/bin/gcc"):
# Add icecream directory for support distributed compiling :)
os.environ["PATH"] = "/opt/icecream/bin/:%s" % os.environ["PATH"]
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
elif ctx.config.values.build.buildhelper == "ccache":
if os.path.exists("/usr/lib/ccache/bin/gcc"):
# Add ccache directory for support Compiler Cache :)
os.environ["PATH"] = "/usr/lib/ccache/bin/:%s" % os.environ["PATH"]
ctx.ui.info(_("CCache detected..."))
def fetch_files(self):
self.specdiruri = dirname(self.specuri.get_uri())
pkgname = basename(self.specdiruri)
self.destdir = join(ctx.config.tmp_dir(), pkgname)
#self.location = dirname(self.url.uri)
self.fetch_actionsfile()
self.fetch_patches()
self.fetch_comarfiles()
self.fetch_additionalFiles()
return self.destdir
def fetch_actionsfile(self):
actionsuri = join(self.specdiruri, ctx.const.actions_file)
self.download(actionsuri, self.destdir)
def fetch_patches(self):
spec = self.spec
for patch in spec.source.patches:
file_name = basename(patch.filename)
dir_name = dirname(patch.filename)
patchuri = join(self.specdiruri,
ctx.const.files_dir, dir_name, file_name)
self.download(patchuri, join(self.destdir, ctx.const.files_dir, dir_name))
def fetch_comarfiles(self):
spec = self.spec
for package in spec.packages:
for pcomar in package.providesComar:
comaruri = join(self.specdiruri,
ctx.const.comar_dir, pcomar.script)
self.download(comaruri, join(self.destdir, ctx.const.comar_dir))
def fetch_additionalFiles(self):
spec = self.spec
for pkg in spec.packages:
for afile in pkg.additionalFiles:
file_name = basename(afile.filename)
dir_name = dirname(afile.filename)
afileuri = join(self.specdiruri,
ctx.const.files_dir, dir_name, file_name)
self.download(afileuri, join(self.destdir, ctx.const.files_dir, dir_name))
def download(self, uri, transferdir):
# fix auth info and download
uri = File.make_uri(uri)
File.download(uri, transferdir)
def fetch_component(self):
if not self.spec.source.partOf:
ctx.ui.warning(_('PartOf tag not defined, looking for component'))
diruri = parenturi(self.specuri.get_uri())
parentdir = parenturi(diruri)
url = util.join_path(parentdir, 'component.xml')
progress = ctx.ui.Progress
if URI(url).is_remote_file():
fetch_url(url, self.pkg_work_dir(), progress)
path = util.join_path(self.pkg_work_dir(), 'component.xml')
else:
if not os.path.exists(url):
raise Exception(_('Cannot find component.xml in upper directory'))
path = url
comp = component.Component()
comp.read(path)
ctx.ui.info(_('Source is part of %s component') % comp.name)
self.spec.source.partOf = comp.name
def fetch_source_archive(self):
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archive.uri)
self.sourceArchive.fetch()
ctx.ui.info(_("Source archive is stored: %s/%s")
%(ctx.config.archives_dir(), self.spec.source.archive.name))
def unpack_source_archive(self):
ctx.ui.info(_("Unpacking archive..."), noln = True)
self.sourceArchive.unpack()
ctx.ui.info(_(" unpacked (%s)") % self.pkg_work_dir())
self.set_state("unpack")
def run_setup_action(self):
# Run configure, build and install phase
ctx.ui.action(_("Setting up source..."))
self.run_action_function(ctx.const.setup_func)
self.set_state("setupaction")
def run_build_action(self):
ctx.ui.action(_("Building source..."))
self.run_action_function(ctx.const.build_func)
self.set_state("buildaction")
def run_install_action(self):
ctx.ui.action(_("Installing..."))
# Before install make sure install_dir is clean
if os.path.exists(self.pkg_install_dir()):
util.clean_dir(self.pkg_install_dir())
# install function is mandatory!
self.run_action_function(ctx.const.install_func, True)
self.set_state("installaction")
def get_abandoned_files(self):
# return the files those are not collected from the install dir
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
abandoned_files = []
all_paths_in_packages = []
for package in self.spec.packages:
for path in package.files:
map(lambda p: all_paths_in_packages.append(p), [p for p in glob.glob(install_dir + path.path)])
for root, dirs, files in os.walk(install_dir):
for file_ in files:
already_in_package = False
fpath = util.join_path(root, file_)
for path in all_paths_in_packages:
if not fpath.find(path):
already_in_package = True
if not already_in_package:
abandoned_files.append(fpath)
return abandoned_files
def compile_action_script(self):
"""Compiles given actions.py to check syntax error in it and sets the actionLocals and actionGlobals"""
fname = util.join_path(self.specdir, ctx.const.actions_file)
try:
localSymbols = globalSymbols = {}
buf = open(fname).read()
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
except IOError, e:
raise Error(_("Unable to read Actions Script (%s): %s") %(fname,e))
except SyntaxError, e:
raise Error(_("SyntaxError in Actions Script (%s): %s") %(fname,e))
self.actionLocals = localSymbols
self.actionGlobals = globalSymbols
self.srcDir = self.pkg_src_dir()
def compile_comar_script(self):
"""Compiles comar scripts to check syntax errors"""
for package in self.spec.packages:
for pcomar in package.providesComar:
fname = util.join_path(self.specdir, ctx.const.comar_dir,
pcomar.script)
try:
buf = open(fname).read()
compile(buf, "error", "exec")
except IOError, e:
raise Error(_("Unable to read COMAR script (%s): %s") %(fname,e))
except SyntaxError, e:
raise Error(_("SyntaxError in COMAR file (%s): %s") %(fname,e))
def pkg_src_dir(self):
"""Returns the real path of WorkDir for an unpacked archive."""
try:
workdir = self.actionGlobals['WorkDir']
except KeyError:
workdir = self.spec.source.name + "-" + self.spec.getSourceVersion()
return util.join_path(self.pkg_work_dir(), workdir)
def run_action_function(self, func, mandatory=False):
"""Calls the corresponding function in actions.py.
If mandatory parameter is True, and function is not present in
actionLocals pisi.build.Error will be raised."""
# we'll need our working directory after actionscript
# finished its work in the archive source directory.
curDir = os.getcwd()
os.chdir(self.srcDir)
if func in self.actionLocals:
self.actionLocals[func]()
else:
if mandatory:
Error, _("unable to call function from actions: %s") %func
os.chdir(curDir)
def check_build_dependencies(self):
"""check and try to install build dependencies, otherwise fail."""
build_deps = self.spec.source.buildDependencies
if not ctx.get_option('ignore_safety'):
if ctx.componentdb.has_component('system.devel'):
build_deps_names = set([x.package for x in build_deps])
devel_deps_names = set(ctx.componentdb.get_component('system.devel').packages)
extra_names = devel_deps_names - build_deps_names
extra_names = filter(lambda x: not ctx.installdb.is_installed(x), extra_names)
if extra_names:
ctx.ui.warning(_('Safety switch: following extra packages in system.devel will be installed: ') +
util.strlist(extra_names))
extra_deps = [dependency.Dependency(package = x) for x in extra_names]
build_deps.extend(extra_deps)
else:
ctx.ui.warning(_('Safety switch: system.devel is already installed'))
else:
ctx.ui.warning(_('Safety switch: the component system.devel cannot be found'))
# find out the build dependencies that are not satisfied...
dep_unsatis = []
for dep in build_deps:
if not dependency.installed_satisfies_dep(dep):
dep_unsatis.append(dep)
if dep_unsatis:
ctx.ui.info(_("Unsatisfied Build Dependencies:") + ' '
+ util.strlist([str(x) for x in dep_unsatis]) )
def fail():
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
if ctx.config.get_option('no_install'):
fail()
if not ctx.config.get_option('ignore_dependency'):
for dep in dep_unsatis:
if not dependency.repo_satisfies_dep(dep):
raise Error(_('Build dependency %s cannot be satisfied') % str(dep))
if ctx.ui.confirm(
_('Do you want to install the unsatisfied build dependencies')):
ctx.ui.info(_('Installing build dependencies.'))
operations.install([dep.package for dep in dep_unsatis])
else:
fail()
else:
ctx.ui.warning(_('Ignoring build dependencies.'))
def patch_exists(self):
"""check existence of patch files declared in PSPEC"""
files_dir = os.path.abspath(util.join_path(self.specdir,
ctx.const.files_dir))
for patch in self.spec.source.patches:
patchFile = util.join_path(files_dir, patch.filename)
if not os.access(patchFile, os.F_OK):
raise Error(_("Patch file is missing: %s\n") % patch.filename)
def apply_patches(self):
files_dir = os.path.abspath(util.join_path(self.specdir,
ctx.const.files_dir))
for patch in self.spec.source.patches:
patchFile = util.join_path(files_dir, patch.filename)
if patch.compressionType:
patchFile = util.uncompress(patchFile,
compressType=patch.compressionType,
targetDir=ctx.config.tmp_dir())
ctx.ui.action(_("* Applying patch: %s") % patch.filename)
util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target)
def generate_static_package_object(self):
ar_files = []
for root, dirs, files in os.walk(self.pkg_install_dir()):
for f in files:
if f.endswith(ctx.const.ar_file_suffix) and util.is_ar_file(util.join_path(root, f)):
ar_files.append(util.join_path(root, f))
if not len(ar_files):
return None
static_package_obj = pisi.specfile.Package()
static_package_obj.name = self.spec.source.name + ctx.const.static_name_suffix
# FIXME: find a better way to deal with the summary and description constants.
static_package_obj.summary['en'] = u'Ar files for %s' % (self.spec.source.name)
static_package_obj.description['en'] = u'Ar files for %s' % (self.spec.source.name)
static_package_obj.partOf = self.spec.source.partOf
for f in ar_files:
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_install_dir()):], fileType = "library"))
# append all generated packages to dependencies
for p in self.spec.packages:
static_package_obj.packageDependencies.append(
pisi.dependency.Dependency(package = p.name))
return static_package_obj
def generate_debug_package_object(self):
debug_files = []
for root, dirs, files in os.walk(self.pkg_debug_dir()):
for f in files:
if f.endswith(ctx.const.debug_file_suffix):
debug_files.append(util.join_path(root, f))
if not len(debug_files):
return None
debug_package_obj = pisi.specfile.Package()
debug_package_obj.debug_package = True
debug_package_obj.name = self.spec.source.name + ctx.const.debug_name_suffix
# FIXME: find a better way to deal with the summary and description constants.
debug_package_obj.summary['en'] = u'Debug files for %s' % (self.spec.source.name)
debug_package_obj.description['en'] = u'Debug files for %s' % (self.spec.source.name)
debug_package_obj.partOf = self.spec.source.partOf + '-debug'
for f in debug_files:
debug_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_debug_dir()):], fileType = "debug"))
# append all generated packages to dependencies
for p in self.spec.packages:
debug_package_obj.packageDependencies.append(
pisi.dependency.Dependency(package = p.name))
return debug_package_obj
def strip_install_dir(self):
"""strip install directory"""
ctx.ui.action(_("Stripping files.."))
install_dir = self.pkg_install_dir()
try:
nostrip = self.actionGlobals['NoStrip']
util.strip_directory(install_dir, nostrip)
except KeyError:
util.strip_directory(install_dir)
def gen_metadata_xml(self, package, build_no=None):
"""Generate the metadata.xml file for build source.
metadata.xml is composed of the information from specfile plus
some additional information."""
metadata = MetaData()
metadata.from_spec(self.spec.source, package, self.spec.history)
metadata.package.distribution = ctx.config.values.general.distribution
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
metadata.package.architecture = "Any"
metadata.package.packageFormat = ctx.get_option('package_format')
size = 0
if package.debug_package:
d = self.pkg_debug_dir()
else:
d = self.pkg_install_dir()
for path in package.files:
for p in glob.glob(util.join_path(d, path.path)):
size += util.dir_size(p)
metadata.package.installedSize = size
self.metadata = metadata
def gen_files_xml(self, package):
"""Generates files.xml using the path definitions in specfile and
the files produced by the build system."""
files = Files()
if package.debug_package:
install_dir = self.pkg_debug_dir()
else:
install_dir = self.pkg_install_dir()
# FIXME: We need to expand globs before trying to calculate hashes
# Not on the fly like now.
# we'll exclude collisions in get_file_hashes. Having a
# collisions list is not wrong, we must just handle it :).
collisions = check_path_collision(package, self.spec.packages)
# FIXME: material collisions after expanding globs could be
# reported as errors
d = {}
def add_path(path):
# add the files under material path
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
if ctx.get_option('create_static') \
and fpath.endswith(ctx.const.ar_file_suffix) \
and not package.name.endswith(ctx.const.static_name_suffix) \
and util.is_ar_file(fpath):
# if this is an ar file, and this package is not a static package,
# don't include this file into the package.
continue
frpath = util.removepathprefix(install_dir, fpath) # relative path
ftype, permanent = get_file_type(frpath, package.files, install_dir)
fsize = util.dir_size(fpath)
d[frpath] = FileInfo(path=frpath, type=ftype, permanent=permanent,
size=fsize, hash=fhash)
for pinfo in package.files:
wildcard_path = util.join_path(install_dir, pinfo.path)
for path in glob.glob(wildcard_path):
add_path(path)
for (p, fileinfo) in d.iteritems():
files.append(fileinfo)
files_xml_path = util.join_path(self.pkg_dir(), ctx.const.files_xml)
files.write(files_xml_path)
self.files = files
def calc_build_no(self, package_name):
"""Calculate build number"""
def metadata_changed(old_metadata, new_metadata):
for key in old_metadata.package.__dict__.keys():
if old_metadata.package.__dict__[key] != new_metadata.package.__dict__[key]:
if key != "build":
return True
return False
# find previous build in packages dir
found = []
def locate_old_package(old_package_fn):
if util.is_package_name(os.path.basename(old_package_fn), package_name):
try:
old_pkg = Package(old_package_fn, 'r')
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
ctx.ui.info(_('(found old version %s)') % old_package_fn)
if str(old_pkg.metadata.package.name) != package_name:
ctx.ui.warning(_('Skipping %s with wrong pkg name ') %
old_package_fn)
return
old_build = old_pkg.metadata.package.build
found.append( (old_package_fn, old_build) )
except Error:
ctx.ui.warning('Package file %s may be corrupt. Skipping.' % old_package_fn)
for root, dirs, files in os.walk(ctx.config.packages_dir()):
for file in files:
locate_old_package(join(root,file))
outdir=ctx.get_option('output_dir')
if not outdir:
outdir = '.'
for file in [join(outdir,entry) for entry in os.listdir(outdir)]:
if os.path.isfile(file):
locate_old_package(file)
if not found:
return (1, None)
ctx.ui.warning(_('(no previous build found, setting build no to 1.)'))
else:
a = filter(lambda (x,y): y != None, found)
ctx.ui.debug(str(a))
if a:
# sort in order of increasing build number
a.sort(lambda x,y : cmp(x[1],y[1]))
old_package_fn = a[-1][0] # get the last one
old_build = a[-1][1]
# compare old files.xml with the new one..
old_pkg = Package(old_package_fn, 'r')
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
changed = False
fnew = self.files.list
fold = old_pkg.files.list
fold.sort(lambda x,y : cmp(x.path,y.path))
fnew.sort(lambda x,y : cmp(x.path,y.path))
if len(fnew) != len(fold):
changed = True
else:
for i in range(len(fold)):
fo = fold.pop(0)
fn = fnew.pop(0)
if fo.path != fn.path:
changed = True
break
else:
if fo.hash != fn.hash:
changed = True
break
if metadata_changed(old_pkg.metadata, self.metadata):
changed = True
self.old_packages.append(os.path.basename(old_package_fn))
else: # no old build had a build number
old_build = None
ctx.ui.debug('old build number: %s' % old_build)
# set build number
if old_build is None:
ctx.ui.warning(_('(old package lacks a build no, setting build no to 1.)'))
return (1, None)
elif changed:
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
return (old_build + 1, old_build)
else:
ctx.ui.info(_('There is no change from previous build %d') % old_build)
return (old_build, old_build)
def build_packages(self):
"""Build each package defined in PSPEC file. After this process there
will be .pisi files hanging around, AS INTENDED ;)"""
self.fetch_component() # bug 856
# Strip install directory before building .pisi packages.
self.strip_install_dir()
if ctx.get_option('create_static'):
obj = self.generate_static_package_object()
if obj:
self.spec.packages.append(obj)
if ctx.config.values.build.generatedebug:
obj = self.generate_debug_package_object()
if obj:
self.spec.packages.append(obj)
self.new_packages = []
self.old_packages = []
for package in self.spec.packages:
# removing "farce" in specfile.py:SpecFile.override_tags
# this block of code came here... SpecFile should never
# ever ruin the generated PSPEC file. If build process
# needs this, we should do it in here... (bug: #3773)
if not package.summary:
package.summary = self.spec.source.summary
if not package.description:
# TODO: remove this if statement with the part in
# specfile.py:SpecFile
if not self.spec.source.description:
self.spec.dirtyWorkAround()
package.description = self.spec.source.description
if not package.partOf:
package.partOf = self.spec.source.partOf
if not package.license:
package.license = self.spec.source.license
if not package.icon:
package.icon = self.spec.source.icon
old_package_name = None
# store additional files
c = os.getcwd()
os.chdir(self.specdir)
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
for afile in package.additionalFiles:
src = os.path.join(ctx.const.files_dir, afile.filename)
dest = os.path.join(install_dir + os.path.dirname(afile.target), os.path.basename(afile.target))
util.copy_file(src, dest)
if afile.permission:
# mode is octal!
os.chmod(dest, int(afile.permission, 8))
os.chdir(c)
ctx.ui.action(_("** Building package %s") % package.name);
ctx.ui.info(_("Generating %s,") % ctx.const.files_xml)
self.gen_files_xml(package)
ctx.ui.info(_("Generating %s,") % ctx.const.metadata_xml)
self.gen_metadata_xml(package)
# build number
if ctx.config.options.ignore_build_no or not ctx.config.values.build.buildno:
build_no = old_build_no = None
ctx.ui.warning(_('Build number is not available. For repo builds you must enable buildno in pisi.conf.'))
else:
build_no, old_build_no = self.calc_build_no(package.name)
self.metadata.package.build = build_no
self.metadata.write(util.join_path(self.pkg_dir(), ctx.const.metadata_xml))
# Calculate new and oldpackage names for buildfarm
name = util.package_name(package.name,
self.spec.getSourceVersion(),
self.spec.getSourceRelease(),
self.metadata.package.build)
outdir = ctx.get_option('output_dir')
if outdir:
name = pisi.util.join_path(outdir, name)
self.new_packages.append(name)
ctx.ui.info(_("Creating PiSi package %s.") % name)
pkg = Package(name, 'w')
# add comar files to package
os.chdir(self.specdir)
for pcomar in package.providesComar:
fname = util.join_path(ctx.const.comar_dir,
pcomar.script)
pkg.add_to_package(fname)
# add xmls and files
os.chdir(self.pkg_dir())
pkg.add_to_package(ctx.const.metadata_xml)
pkg.add_to_package(ctx.const.files_xml)
# Now it is time to add files to the packages using newly
# created files.xml
files = Files()
files.read(ctx.const.files_xml)
if ctx.get_option('package_format') == "1.0":
for finfo in files.list:
orgname = arcname = join("install", finfo.path)
if package.debug_package:
orgname = join("debug", finfo.path)
pkg.add_to_package(orgname, arcname)
pkg.close()
else: # default package format is 1.1, so make it fallback.
ctx.build_leftover = join(self.pkg_dir(), ctx.const.install_tar_lzma)
tar = archive.ArchiveTar(ctx.const.install_tar_lzma, "tarlzma")
for finfo in files.list:
orgname = arcname = join("install", finfo.path)
if package.debug_package:
orgname = join("debug", finfo.path)
tar.add_to_archive(orgname, arcname.lstrip("install"))
tar.close()
pkg.add_to_package(ctx.const.install_tar_lzma)
pkg.close()
os.unlink(ctx.const.install_tar_lzma)
ctx.build_leftover = None
os.chdir(c)
self.set_state("buildpackages")
ctx.ui.info(_("Done."))
#show the files those are not collected from the install dir
if ctx.get_option('debug'):
abandoned_files = self.get_abandoned_files()
if abandoned_files:
ctx.ui.warning(_('Abandoned files under the install dir (%s):') % (install_dir))
for f in abandoned_files:
ctx.ui.info(' - %s' % (f))
else:
ctx.ui.warning(_('All of the files under the install dir (%s) has been collected by package(s)')
% (install_dir))
if ctx.config.values.general.autoclean is True:
ctx.ui.info(_("Cleaning Build Directory..."))
util.clean_dir(self.pkg_dir())
else:
ctx.ui.info(_("Keeping Build Directory"))
# reset environment variables after build. this one is for
# buildfarm actually. buildfarm re-inits pisi for each build
# and left environment variables go directly into initial dict
# making actionsapi.variables.exportFlags() useless...
os.environ = {}
os.environ = deepcopy(ctx.config.environ)
return self.new_packages, self.old_packages
# build functions...
def build(pspec):
if pspec.endswith('.xml'):
pb = Builder(pspec)
else:
pb = Builder.from_name(pspec)
return pb.build()
order = {"none": 0,
"fetch": 1,
"unpack": 2,
"setupaction": 3,
"buildaction": 4,
"installaction": 5,
"buildpackages": 6}
def __buildState_fetch(pb):
# fetch is the first state to run.
pb.patch_exists()
pb.fetch_source_archive()
def __buildState_unpack(pb, last):
if order[last] < order["fetch"]:
__buildState_fetch(pb)
pb.unpack_source_archive()
pb.apply_patches()
def __buildState_setupaction(pb, last):
if order[last] < order["unpack"]:
__buildState_unpack(pb, last)
pb.run_setup_action()
def __buildState_buildaction(pb, last):
if order[last] < order["setupaction"]:
__buildState_setupaction(pb, last)
pb.run_build_action()
def __buildState_installaction(pb, last):
if order[last] < order["buildaction"]:
__buildState_buildaction(pb, last)
pb.run_install_action()
def __buildState_buildpackages(pb, last):
if order[last] < order["installaction"]:
__buildState_installaction(pb, last)
pb.build_packages()
def build_until(pspec, state):
if pspec.endswith('.xml'):
pb = Builder(pspec)
else:
pb = Builder.from_name(pspec)
pb.compile_action_script()
pb.compile_comar_script()
last = pb.get_state()
ctx.ui.info("Last state was %s"%last)
if not last: last = "none"
if state == "fetch":
__buildState_fetch(pb)
return
if state == "unpack":
__buildState_unpack(pb, last)
return
if state == "setup":
__buildState_setupaction(pb, last)
return
if state == "build":
__buildState_buildaction(pb, last)
return
if state == "install":
__buildState_installaction(pb, last)
return
__buildState_buildpackages(pb, last)
-51
View File
@@ -1,51 +0,0 @@
Index: build.py
===================================================================
--- build.py (revision 8935)
+++ build.py (working copy)
@@ -197,13 +197,30 @@
# apply the patches and prepare a source directory for build.
self.apply_patches()
+ if ctx.get_option('use_catbox'):
+ import catbox
+ ret = catbox.run(
+ self.sandboxed,
+ ["/var/tmp/pisi", "/tmp", "/dev/tty", "/dev/null"],
+ logger = self.sandbox_logger
+ )
+ if ret != 0:
+ raise Error, "actions.py borked"
+ else:
+ self.sandboxed()
+
+ # after all, we are ready to build/prepare the packages
+ return self.build_packages()
+
+ def sandbox_logger(self, event, data):
+ if event == "denied":
+ ctx.ui.error("Access denied to '%s'" % data)
+
+ def sandboxed(self):
self.run_setup_action()
self.run_build_action()
self.run_install_action()
- # after all, we are ready to build/prepare the packages
- return self.build_packages()
-
def set_environment_vars(self):
"""Sets the environment variables for actions API to use"""
Index: cli/commands.py
===================================================================
--- cli/commands.py (revision 8935)
+++ cli/commands.py (working copy)
@@ -396,6 +396,8 @@
default=False, help=_("don't install build dependencies, fail if a build dependency is present"))
self.parser.add_option("-F", "--package-format", action="store", default='1.1',
help=_("pisi package format"))
+ self.parser.add_option("", "--use-catbox", action="store_true",
+ default=False, help=_("use catbox"))
def run(self):
if not self.args:
-23
View File
@@ -1,23 +0,0 @@
Writing commands:
-----------------
TODO: this is a *bit* out of date now.
Subclass from Command.
Be careful not to import PiSi modules before Command is run, e.g.
do it in run() method. This is necessary to prevent an obscure
initialization error.
We used to import pisi.operations in pisi.cli
This prevented pisi.operations from having the current global
pisi.ui.ui because it's loaded before the UI is set! So, despite
what you might understand from the python FAQ 1.2.3
http://www.python.org/doc/faq/programming.html#how-do-i-share-global-variables-across-modules
you have to be careful about the initialization order when using
singleton modules.
Another way to solve this is to ensure that the global module is
always imported again in every function it is called (see
ui.CLI.confirm() ) but this is more expensive.
-165
View File
@@ -1,165 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import logging
import locale
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.ui import UI
import pisi.ui as ui
import pisi.util as util
from pisi.cli.colors import colorize
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
def printu(obj, err = False):
if not isinstance(obj, unicode):
obj = unicode(obj)
if err:
out = sys.stderr
else:
out = sys.stdout
out.write(obj.encode('utf-8'))
out.flush()
class CLI(UI):
"Command Line Interface"
def __init__(self, show_debug = False, show_verbose = False):
super(CLI, self).__init__(show_debug, show_verbose)
def close(self):
util.xterm_title_reset()
def output(self, msg, err = False, verbose = False):
if (verbose and self.show_verbose) or (not verbose):
if type(msg)==type(unicode()):
msg = msg.encode('utf-8')
if err:
out = sys.stderr
else:
out = sys.stdout
out.write(msg)
out.flush()
def info(self, msg, verbose = False, noln = False):
# TODO: need to look at more kinds of info messages
# let's cheat from KDE :)
if not noln:
msg = '%s\n' % msg
self.output(unicode(msg), verbose=verbose)
def warning(self, msg, verbose = False):
msg = unicode(msg)
if ctx.log:
ctx.log.warning(msg)
if ctx.get_option('no_color'):
self.output(_('Warning: ') + msg + '\n', err=True, verbose=verbose)
else:
self.output(colorize(msg + '\n', 'purple'), err=True, verbose=verbose)
def error(self, msg):
msg = unicode(msg)
if ctx.log:
ctx.log.error(msg)
if ctx.get_option('no_color'):
self.output(_('Error: ') + msg + '\n', err=True)
else:
self.output(colorize(msg + '\n', 'red'), err=True)
def action(self, msg, verbose = False):
#TODO: this seems quite redundant?
msg = unicode(msg)
if ctx.log:
ctx.log.info(msg)
self.output(colorize(msg + '\n', 'green'))
def choose(self, msg, opts):
print msg
for i in range(0,len(opts)):
print i + 1, opts(i)
while True:
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
try:
opt = int(s)
if 1 <= opt and opt <= len(opts):
return opts(opt-1)
except (Exception,e):
pass
def confirm(self, msg):
msg = unicode(msg)
if ctx.config.options and ctx.config.options.yes_all:
return True
while True:
import re
yesexpr = re.compile(locale.nl_langinfo(locale.YESEXPR))
prompt = msg + colorize(_(' (yes/no)'), 'red')
s = raw_input(prompt.encode('utf-8'))
if yesexpr.search(s):
return True
return False
def display_progress(self, operation, percent, info="", **ka):
""" display progress of any operation """
if operation == "removing":
return
elif operation == "fetching":
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
(ka['filename'], totalsize, percent,
ka['rate'], ka['symbol'], ka['eta'])
self.output(out)
else:
self.output("\r%s (%d%%)" % (info, percent))
if percent == 100:
self.output(colorize(_(' [complete]\n'), 'gray'))
def status(self, msg = None):
if msg:
msg = unicode(msg)
self.output(colorize(msg + '\n', 'purple'))
util.xterm_title(msg)
def notify(self, event, **keywords):
if event == ui.installed:
msg = _('Installed %s') % keywords['package'].name
elif event == ui.removed:
msg = _('Removed %s') % keywords['package'].name
elif event == ui.upgraded:
msg = _('Upgraded %s') % keywords['package'].name
elif event == ui.configured:
msg = _('Configured %s') % keywords['package'].name
elif event == ui.extracting:
msg = _('Extracting the files of %s') % keywords['name']
else:
msg = None
if msg:
self.output(colorize(msg + '\n', 'cyan'))
if ctx.log:
ctx.log.info(msg)
-64
View File
@@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Colors module provides some color codes for console output."""
import pisi.context as ctx
colors = {'black' : "\033[30m",
'red' : "\033[31m",
'green' : "\033[32m",
'yellow' : "\033[33m",
'blue' : "\033[34m",
'purple' : "\033[35m",
'cyan' : "\033[36m",
'white' : "\033[37m",
'brightblack' : "\033[01;30m",
'brightred' : "\033[01;31m",
'brightgreen' : "\033[01;32m",
'brightyellow' : "\033[01;33m",
'brightblue' : "\033[01;34m",
'brightmagenta' : "\033[01;35m",
'brightcyan' : "\033[01;36m",
'brightwhite' : "\033[01;37m",
'underlineblack' : "\033[04;30m",
'underlinered' : "\033[04;31m",
'underlinegreen' : "\033[04;32m",
'underlineyellow' : "\033[04;33m",
'underlineblue' : "\033[04;34m",
'underlinemagenta' : "\033[04;35m",
'underlinecyan' : "\033[04;36m",
'underlinewhite' : "\033[04;37m",
'blinkingblack' : "\033[05;30m",
'blinkingred' : "\033[05;31m",
'blinkinggreen' : "\033[05;32m",
'blinkingyellow' : "\033[05;33m",
'blinkingblue' : "\033[05;34m",
'blinkingmagenta' : "\033[05;35m",
'blinkingcyan' : "\033[05;36m",
'blinkingwhite' : "\033[05;37m",
'backgroundblack' : "\033[07;30m",
'backgroundred' : "\033[07;31m",
'backgroundgreen' : "\033[07;32m",
'backgroundyellow' : "\033[07;33m",
'backgroundblue' : "\033[07;34m",
'backgroundmagenta' : "\033[07;35m",
'backgroundcyan' : "\033[07;36m",
'backgroundwhite' : "\033[07;37m",
'default' : "\033[0m" }
def colorize(msg, color):
"""Colorize the given message for console output"""
if colors.has_key(color) and not ctx.get_option('no_color'):
return colors[color] + msg + colors['default']
else:
return msg
File diff suppressed because it is too large Load Diff
-111
View File
@@ -1,111 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
from optparse import OptionParser
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.cli
from pisi.cli import printu
from pisi.uri import URI
from pisi.cli.commands import *
class ParserError(pisi.Exception):
pass
class Error(pisi.Error):
pass
class PreParser(OptionParser):
"""consumes any options, and finds arguments from command line"""
def __init__(self, version):
OptionParser.__init__(self, usage=usage_text, version=version)
def error(self, msg):
raise ParserError, msg
def parse_args(self, args=None):
self.opts = []
self.rargs = self._get_args(args)
self._process_args()
return (self.opts, self.args)
def _process_args(self):
args = []
rargs = self.rargs
if not self.allow_interspersed_args:
first_arg = False
while rargs:
arg = rargs[0]
def option():
if not self.allow_interspersed_args and first_arg:
self.error(_('Options must precede non-option arguments'))
arg = rargs[0]
if arg.startswith('--'):
self.opts.append(arg[2:])
else:
self.opts.append(arg[1:])
del rargs[0]
return
# We handle bare "--" explicitly, and bare "-" is handled by the
# standard arg handler since the short arg case ensures that the
# len of the opt string is greater than 1.
if arg == "--":
del rargs[0]
break
elif arg[0:2] == "--":
# process a single long option (possibly with value(s))
option()
elif arg[:1] == "-" and len(arg) > 1:
# process a cluster of short options (possibly with
# value(s) for the last one only)
option()
else: # then it must be an argument
args.append(arg)
del rargs[0]
self.args = args
class PisiCLI(object):
def __init__(self, orig_args=None):
# first construct a parser for common options
# this is really dummy
self.parser = PreParser(version="%prog " + pisi.__version__)
try:
opts, args = self.parser.parse_args(args=orig_args)
if len(args)==0: # more explicit than using IndexError
if 'version' in opts:
self.parser.print_version()
sys.exit(0)
elif 'help' in opts or 'h' in opts:
self.die()
raise Error(_('No command given'))
cmd_name = args[0]
except ParserError:
raise Error(_('Command line parsing error'))
self.command = Command.get_command(cmd_name, args=orig_args)
if not self.command:
raise Error(_("Unrecognized command: %s") % cmd_name)
def die(self):
printu('\n' + self.parser.format_help())
sys.exit(1)
def run_command(self):
self.command.run()
-127
View File
@@ -1,127 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
def make_com():
try:
import comar
if not wait_comar():
raise comar.Error
if ctx.comar_sockname:
com = comar.Link(sockname=ctx.comar_sockname)
else:
com = comar.Link()
return com
except ImportError:
raise Error(_("comar package is not fully installed"))
except comar.Error:
raise Error(_("cannot connect to comar"))
def wait_comar():
import socket, time
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
timeout = 5
while timeout > 0:
try:
if ctx.comar_sockname:
sock.connect(ctx.comar_sockname)
else:
sock.connect("/var/run/comar.socket")
return True
except:
timeout -= 0.2
time.sleep(0.2)
return False
def wait_for_result(com, package_name=None):
multiple = False
while 1:
try:
reply = com.read_cmd()
except:
# Comar postInstall does a "service comar restart" which cuts
# our precious communication link, so we waitsss
if package_name == "comar":
if not wait_comar():
raise Error, _("Could not restart comar")
return
else:
raise Error, _("connection with comar unexpectedly closed")
cmd = reply[0]
if cmd == com.RESULT and not multiple:
return
elif cmd == com.NONE and not multiple:
# no post/pre function, that is ok
return
elif cmd == com.RESULT_START:
multiple = True
elif cmd == com.RESULT_END:
return
elif cmd == com.FAIL:
e = _("Configuration error: %s") % reply[2]
raise Error, e
elif cmd == com.ERROR:
e = _("Script error: %s") % reply[2]
raise Error, e
elif cmd == com.DENIED:
raise Error, _("comar denied our access")
def post_install(package_name, provided_scripts, scriptpath, metapath, filepath):
ctx.ui.info(_("Configuring package"))
self_post = False
com = make_com()
for script in provided_scripts:
ctx.ui.debug(_("Registering %s comar script") % script.om)
if script.om == "System.Package":
self_post = True
com.register(script.om, package_name, os.path.join(scriptpath, script.script))
wait_for_result(com)
ctx.ui.debug(_("Calling post install handlers"))
com.call("System.PackageHandler.setupPackage", [ "metapath", metapath, "filepath", filepath ])
wait_for_result(com)
if self_post:
ctx.ui.debug(_("Running package's post install script"))
com.call_package("System.Package.postInstall", package_name)
wait_for_result(com, package_name)
def pre_remove(package_name, metapath, filepath):
ctx.ui.info(_("Configuring package for removal"))
com = make_com()
ctx.ui.debug(_("Running package's pre remove script"))
com.call_package("System.Package.preRemove", package_name)
wait_for_result(com)
ctx.ui.debug(_("Calling pre remove handlers"))
com.call("System.PackageHandler.cleanupPackage", [ "metapath", metapath, "filepath", filepath ])
wait_for_result(com)
ctx.ui.debug(_("Unregistering comar scripts"))
com.remove(package_name)
wait_for_result(com)
-225
View File
@@ -1,225 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.lockeddbshelve as shelve
from pisi.itembyrepodb import ItemByRepoDB
import pisi.itembyrepodb as itembyrepodb
class Error(pisi.Error):
pass
__metaclass__ = autoxml.autoxml
class Distribution(xmlfile.XmlFile):
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_SourceName = [autoxml.Text, autoxml.mandatory] # name of distribution (source)
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_Version = [autoxml.Text, autoxml.optional]
t_Type = [autoxml.Text, autoxml.mandatory]
t_Dependencies = [ [autoxml.Text], autoxml.optional, "Dependencies/Distribution"]
t_BinaryName = [autoxml.Text, autoxml.optional] # name of repository (binary distro)
t_Architecture = [autoxml.Text, autoxml.optional] # architecture identifier
class Component(xmlfile.XmlFile):
"representation for component declarations"
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Name = [autoxml.String, autoxml.mandatory] # fully qualified name
# component name in other languages, for instance in Turkish
# LocalName for system.base could be sistem.taban or "Taban Sistem",
# this could be useful for GUIs
t_LocalName = [autoxml.LocalText, autoxml.mandatory]
# Information about the component
t_Summary = [autoxml.LocalText, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
#t_Icon = [autoxml.Binary, autoxml.mandatory]
# Dependencies to other components
t_Dependencies = [ [autoxml.String], autoxml.optional, "Dependencies/Component"]
# the parts of this component.
# to be filled by the component database, thus it is optional.
t_Packages = [ [autoxml.String], autoxml.optional, "Parts/Package"]
t_Sources = [ [autoxml.String], autoxml.optional, "Parts/Source"]
class ComponentDB(object):
"""a database of components"""
def __init__(self):
self.d = ItemByRepoDB('component')
def close(self):
self.d.close()
def destroy(self):
self.d.destroy()
def has_component(self, name, repo = pisi.itembyrepodb.repos, txn = None):
name = str(name)
return self.d.has_key(name, repo, txn)
def get_component(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def get_component_repo(self, name, repo=None, txn = None):
try:
return self.d.get_item_repo(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def get_union_comp(self, name, txn = None, repo = pisi.itembyrepodb.repos ):
"""get a union of all repository components packages, not just the first repo in order.
get only basic repo info from the first repo"""
def proc(txn):
s = self.d.d.get(name, txn=txn)
pkgs = set()
srcs = set()
for repostr in self.d.order(repo = repo):
if s.has_key(repostr):
pkgs |= set(self.get_packages(name, True, repo, txn))
srcs |= set(s[repostr].sources)
comp = self.get_component(name)
comp.packages = list(pkgs)
comp.sources = list(srcs)
return comp
return self.d.txn_proc(proc, txn)
def list_components(self, repo=None):
return self.d.list(repo)
# Returns the package list of the component
# walk: walks through the underlying components' packages
def get_packages(self, component_name, walk=False, repo=None, txn = None):
"""returns the given component's and underlying recursive components' packages"""
component = self.get_component(component_name, repo, txn)
if not walk:
return component.packages
packages = []
packages.extend(component.packages)
for dep in component.dependencies:
packages.extend(self.get_packages(dep))
return packages
def add_child(self, component, repo, txn = None):
"""update component tree"""
parent_name = ".".join(component.name.split(".")[:-1])
if not parent_name: # root component
return
if self.has_component(parent_name, repo, txn):
parent = self.get_component(parent_name, repo, txn)
else:
parent = Component(name = parent_name)
if component.name not in parent.dependencies:
parent.dependencies.append(component.name)
self.d.add_item(parent_name, parent, repo, txn)
def update_component(self, component, repo, txn = None):
def proc(txn):
if self.has_component(component.name, repo, txn):
# preserve list of packages and dependencies
current = self.d.get_item(component.name, repo, txn)
component.packages = current.packages
component.dependencies = current.dependencies
self.d.add_item(component.name, component, repo, txn)
self.add_child(component, repo, txn)
self.d.txn_proc(proc, txn)
def add_package(self, component_name, package, repo, txn = None):
def proc(txn):
assert component_name
if self.has_component(component_name, repo, txn):
component = self.get_component(component_name, repo, txn)
else:
component = Component( name = component_name )
if not package in component.packages:
component.packages.append(package)
self.d.add_item(component_name, component, repo, txn) # update
self.add_child(component, repo, txn)
self.d.txn_proc(proc, txn)
def remove_package(self, component_name, package, repo = None, txn = None):
def proc(txn, repo):
if not self.has_component(component_name, repo, txn):
raise Error(_('Information for component %s not available') % component_name)
if not repo:
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
component = self.get_component(component_name, repo, txn)
if package in component.packages:
component.packages.remove(package)
self.d.add_item(component_name, component, repo, txn) # update
ctx.txn_proc(lambda x: proc(txn, repo), txn)
def add_spec(self, component_name, spec, repo, txn = None):
def proc(txn):
assert component_name
if self.has_component(component_name, repo, txn):
component = self.get_component(component_name, repo, txn)
else:
component = Component( name = component_name )
if not spec in component.sources:
component.sources.append(spec)
self.d.add_item(component_name, component, repo, txn) # update
self.d.txn_proc(proc, txn)
def remove_spec(self, component_name, spec, repo = None, txn = None):
def proc(txn, repo):
if not self.has_component(component_name, repo, txn):
raise Error(_('Information for component %s not available') % component_name)
if not repo:
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
component = self.get_component(component_name, repo, txn)
if spec in component.sources:
component.sources.remove(spec)
self.d.add_item(component_name, component, repo, txn) # update
ctx.txn_proc(lambda x: proc(txn, repo), txn)
def clear(self, txn = None):
self.d.clear(txn)
def remove_component(self, name, repo = None, txn = None):
name = str(name)
self.d.remove_item(name, repo, txn)
def remove_repo(self, repo, txn = None):
self.d.remove_repo(repo, txn=txn)
-113
View File
@@ -1,113 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
PiSi Configuration module is used for gathering and providing
regular PiSi configurations.
"""
import os
from copy import deepcopy
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.configfile import ConfigurationFile
import pisi.util
from pisi.util import join_path as join
class Error(pisi.Error):
pass
class Options(object):
def __getattr__(self, name):
if not self.__dict__.has_key(name):
return None
else:
return self.__dict__[name]
class Config(object):
"""Config Singleton"""
def __init__(self, options = Options()):
self.options = options
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
destdir = self.get_option('destdir')
if destdir:
if destdir.strip().startswith('/'):
self.destdir = destdir
else:
self.destdir = join(os.getcwd(), destdir)
else:
self.destdir = self.values.general.destinationdirectory
if not os.path.exists(self.destdir):
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % self.destdir)
os.makedirs(self.destdir)
# get the initial environment variables. this is needed for
# build process.
self.environ = deepcopy(os.environ)
def get_option(self, opt):
if self.options:
if hasattr(self.options, opt):
return getattr(self.options, opt)
return None
# directory accessor functions
# here is how it goes
# x_dir: system wide directory for storing info type x
# pkg_x_dir: per package directory for storing info type x
def dest_dir(self):
return self.destdir
def subdir(self, path):
dir = join(self.dest_dir(), path)
pisi.util.check_dir(dir)
return dir
def lib_dir(self):
return self.subdir(self.values.dirs.lib_dir)
def db_dir(self):
return self.subdir(self.values.dirs.db_dir)
def archives_dir(self):
return self.subdir(self.values.dirs.archives_dir)
def packages_dir(self):
return self.subdir(self.values.dirs.packages_dir)
def index_dir(self):
return self.subdir(self.values.dirs.index_dir)
def tmp_dir(self):
sysdir = self.subdir(self.values.dirs.tmp_dir)
if os.environ.has_key('USER'):
userdir = self.subdir('/tmp/pisi-' + os.environ['USER'])
else:
userdir = self.subdir('/tmp/pisi-root')
# check write access
if os.access(sysdir, os.W_OK):
return sysdir
else:
return userdir
#TODO: remove this
config = Config()
-160
View File
@@ -1,160 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# PiSi Configuration File module, obviously, is used to read from the
# configuration file. Module also defines default values for
# configuration parameters.
#
# Configuration file is located in /etc/pisi/pisi.conf by default,
# having an INI like format like below.
#
#[general]
#destinationdirectory = /
#autoclean = False
#
#[build]
#host = i686-pc-linux-gnu
#generateDebug = False
#jobs = "-j1"
#CFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
#CXXFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
#LDFLAGS=
#buildno=True # necessary for generating build nos
#buildhelper = None / ccache / icecream
#compressionlevel = 7
#fallback = "ftp://ftp.pardus.org.tr/pub/pisi/source"
#
#[directories]
#lib_dir = /var/lib/pisi
#db_dir = /var/db/pisi
#archives_dir = /var/cache/pisi/archives
#packages_dir = /var/cache/pisi/packages
#index_dir = /var/cache/pisi/index
#tmp_dir = /var/tmp/pisi
#kde_dir = /usr/kde/3.5
#qt_dir = /usr/qt/3
import os
from ConfigParser import ConfigParser, NoSectionError
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
pass
class GeneralDefaults:
"""Default values for [general] section"""
destinationdirectory = "/"
autoclean = False
distribution = "Pardus"
distribution_release = "1.1"
http_proxy = os.getenv("HTTP_PROXY") or None
https_proxy = os.getenv("HTTPS_PROXY") or None
ftp_proxy = os.getenv("FTP_PROXY") or None
class BuildDefaults:
"""Default values for [build] section"""
host = "i686-pc-linux-gnu"
jobs = "-j1"
generateDebug = False
cflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
cxxflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
ldflags = ""
buildno = False
buildhelper = None
compressionlevel = 7
fallback = "ftp://ftp.pardus.org.tr/pub/pisi/source"
class DirectoriesDefaults:
"Default values for [directories] section"
lib_dir = "/var/lib/pisi"
db_dir = "/var/db/pisi"
archives_dir = "/var/cache/pisi/archives"
packages_dir = "/var/cache/pisi/packages"
index_dir = "/var/lib/pisi/index"
tmp_dir = "/var/tmp/pisi"
kde_dir = "/usr/kde/3.5"
qt_dir = "/usr/qt/3"
class ConfigurationSection(object):
"""ConfigurationSection class defines a section in the configuration
file, using defaults (above) as a fallback."""
def __init__(self, section, items=[]):
self.items = items
if section == "general":
self.defaults = GeneralDefaults
elif section == "build":
self.defaults = BuildDefaults
elif section == "directories":
self.defaults = DirectoriesDefaults
else:
e = _("No section by name '%s'") % section
raise Error, e
self.section = section
def __getattr__(self, attr):
# first search for attribute in the items provided in the
# configuration file.
if self.items:
for item in self.items:
if item[0] == attr:
# all values are returned as string types by ConfigParser.
# evaluate "True" or "False" strings to boolean.
if item[1] in ["True", "False", "None"]:
return eval(item[1])
else:
return item[1]
# then fall back to defaults
if hasattr(self.defaults, attr):
return getattr(self.defaults, attr)
return ""
# We'll need to access configuration keys by their names as a
# string. Like; ["default"]...
def __getitem__(self, key):
return self.__getattr__(key)
class ConfigurationFile(object):
"""Parse and get configuration values from the configuration file"""
def __init__(self, filePath):
parser = ConfigParser()
self.filePath = filePath
parser.read(self.filePath)
try:
generalitems = parser.items("general")
except NoSectionError:
generalitems = []
self.general = ConfigurationSection("general", generalitems)
try:
builditems = parser.items("build")
except NoSectionError:
builditems = []
self.build = ConfigurationSection("build", builditems)
try:
dirsitems = parser.items("directories")
except NoSectionError:
dirsitems = []
self.dirs = ConfigurationSection("directories", dirsitems)
-110
View File
@@ -1,110 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""PiSi constants.
If you have a "magic" constant value this is where it should be
defined."""
# Author: Baris Metin <baris@pardus.org.tr
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class _constant:
"Constant members implementation"
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, _("Can't rebind constant: %s") % name
# Binding an attribute once to a const is available
self.__dict__[name] = value
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, _("Can't unbind constant: %s") % name
# we don't have an attribute by this name
raise NameError, name
class Constants:
"Pisi Constants Singleton"
__c = _constant()
def __init__(self):
# suffix for package names
self.__c.package_suffix = ".pisi"
# suffix for lzma
self.__c.lzma_suffix = ".lzma"
# suffix for auto generated debug packages
self.__c.debug_name_suffix = "-debug"
self.__c.debug_file_suffix = ".debug"
# suffix for auto generated ar packages
self.__c.static_name_suffix = "-static" # an admissible use of constant
self.__c.ar_file_suffix = ".a"
# directory suffixes for build
self.__c.work_dir_suffix = "/work" # these, too, because we might wanna change 'em
self.__c.install_dir_suffix = "/install"
self.__c.debug_dir_suffix = "/debug"
# file/directory names
#note: these don't seem very well, constants are used
#when it is easier/more meaningful to write the constant name, or
#when the constant is bound to change later on.
#in some places literals are just as good, for instance
#when constant is the same as string. readability is important...
self.__c.actions_file = "actions.py"
self.__c.files_dir = "files"
self.__c.metadata_dir = "metadata"
self.__c.comar_dir = "comar"
self.__c.files_xml = "files.xml"
self.__c.metadata_xml = "metadata.xml"
self.__c.install_tar = "install.tar"
self.__c.install_tar_lzma = "install.tar.lzma"
self.__c.mirrors_conf = "/etc/pisi/mirrors.conf"
#file/directory permissions
self.__c.umask = 0022
# functions in actions_file
self.__c.setup_func = "setup"
self.__c.build_func = "build"
self.__c.install_func = "install"
# file types
# FIXME: these seem redundant
self.__c.doc = "doc"
self.__c.man = "man"
self.__c.info = "info"
self.__c.conf = "config"
self.__c.header = "header"
self.__c.library = "library"
self.__c.executable = "executable"
self.__c.data = "data"
self.__c.localedata = "localedata"
def __getattr__(self, attr):
return getattr(self.__c, attr)
def __setattr__(self, attr, value):
setattr(self.__c, attr, value)
def __delattr__(self, attr):
delattr(self.__c, attr)
-78
View File
@@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# global variables here
import pisi.constants
const = pisi.constants.Constants()
config = None
log = None
use_mdom = False
def get_option(opt):
return config and config.get_option(opt)
# default UI is CLI
ui = None # not now
# stdout, stderr for PiSi API
stdout = None
stderr = None
dbenv = None
installdb = None
packagedb = None
repodb = None
invidx = None
comar = None
comar_sockname = None
initialized = False
# Bug #2879
# FIXME: Maybe we can create a simple rollback mechanism. There are other
# places which need this, too.
# this is needed in build process to clean after if something goes wrong.
build_leftover = None
#def register(_impl):
# """ Register a UI implementation"""
# ui = _impl
import bsddb3.db as db
# copy of DBShelve.txn_proc, the only difference is it doesn't need a shelf object
#FIXME: remove this redundancy, and move all this stuff to database.py
def txn_proc(proc, txn = None):
# can be used to txn protect a method automatically
if not txn:
if dbenv:
autotxn = dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise e
except Exception, e:
autotxn.abort()
raise e
autotxn.commit()
else:
retval = proc(None)
return retval
else:
return proc(txn)
-317
View File
@@ -1,317 +0,0 @@
#------------------------------------------------------------------------
# Copyright (c) 1997-2001 by Total Control Software
# All Rights Reserved
#------------------------------------------------------------------------
#
# Module Name: dbShelve.py
#
# Description: A reimplementation of the standard shelve.py that
# forces the use of cPickle, and DB.
#
# Creation Date: 11/3/97 3:39:04PM
#
# License: This is free software. You may use this software for any
# purpose including modification/redistribution, so long as
# this header remains intact and that you do not claim any
# rights of ownership or authorship of this software. This
# software has been tested, but no warranty is expressed or
# implied.
#
# 13-Dec-2000: Updated to be used with the new bsddb3 package.
# Added DBShelfCursor class.
#
# 13-Dec-2005: Minor hacking by exa to make it work better with PiSi
#------------------------------------------------------------------------
"""Manage shelves of pickled objects using bsddb database files for the
storage.
Add transaction processing by default to dictionary ops also
Also other minor improvements -- exa
Now added support for overriding the marshalling method
"""
#------------------------------------------------------------------------
import cPickle
import bsddb3.db as db
import bsddb3.dbobj as dbobj
import string
import sys
import traceback
import pisi
class CodingError(pisi.Error):
pass
class DBShelf:
"""A shelf to hold pickled objects, built upon a bsddb DB object. It
automatically pickles/unpickles data objects going to/from the DB.
"""
def __init__(self, dbenv = None):
self.dbenv = dbenv
# how lame is bsddb3?
if self.dbenv:
self.db = dbobj.DB(dbenv)
else:
self.db = db.DB(None)
# it is better to explicitly close a shelf
#def __del__(self):
# self.close()
allowed_chars = string.letters + string.digits + '-'
def check_key(key):
return pisi.util.all(lambda x: x in allowed_chars, key)
def has_key(self, key, txn = None):
if txn:
return self.db.has_key(key, txn)
else:
return self.db.has_key(key)
def txn_proc(self, proc, txn):
# can be used to txn protect a method automatically
if not txn:
if self.dbenv:
autotxn = self.dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise pisi.Error, e
autotxn.commit()
else: # execute without transactions
retval = proc(None)
return retval
else:
return proc(txn)
def decode(self, data):
try:
return cPickle.loads(data)
except cPickle.UnpicklingError:
raise CodingError()
def encode(self, obj):
return cPickle.dumps(obj, 1)
def clear(self, txn = None):
def proc(txn):
for x in self.keys(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
def delete(self, x, txn):
def proc(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
# another lame pythonic implementation method:
#def __getattr__(self, name):
# """Many methods we can just pass through to the DB object.
# (See below)
# """
# print 'aptal bsddb3', name
# return getattr(self.db, name)
#-----------------------------------
# Dictionary access methods
def __len__(self):
return len(self.db)
def __getitem__(self, key):
def proc(txn):
data = self.db.get(key)
return self.decode(data)
return self.txn_proc(proc, None)
def __setitem__(self, key, value):
# hyperdandik transactions
def proc(txn):
data = self.encode(value)
self.db.put(key,data,txn)
return self.txn_proc(proc, None)
def __delitem__(self, key):
txn = self.dbenv.txn_begin()
try:
self.db.delete(key, txn)
except db.DBError, e:
txn.abort()
raise e
txn.commit()
def keys(self, txn=None):
if txn != None:
return self.db.keys(txn)
else:
return self.db.keys()
def items(self, txn=None):
if txn != None:
items = self.db.items(txn)
else:
items = self.db.items()
newitems = []
for k, v in items:
newitems.append( (k, self.decode(v) ) )
return newitems
def values(self, txn=None):
if txn != None:
values = self.db.values(txn)
else:
values = self.db.values()
return map(lambda x : self.decode(x), values)
#-----------------------------------
# Other methods
def __append(self, value, txn=None):
data = self.encode(value)
return self.db.append(data, txn)
def append(self, value, txn=None):
if self.get_type() != db.DB_RECNO:
self.append = self.__append
return self.append(value, txn=txn)
raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO"
def associate(self, secondaryDB, callback, flags=0):
def _shelf_callback(priKey, priData, realCallback=callback):
data = self.decode(priData)
return realCallback(priKey, data)
return self.db.associate(secondaryDB, _shelf_callback, flags)
#def get(self, key, default=None, txn=None, flags=0):
def get(self, *args, **kw):
# We do it with *args and **kw so if the default value wasn't
# given nothing is passed to the extension module. That way
# an exception can be raised if set_get_returns_none is turned
# off.
data = apply(self.db.get, args, kw)
try:
return self.decode(data)
except (TypeError, CodingError):
return data # we may be getting the default value, or None,
# so it doesn't need unpickled.
def get_both(self, key, value, txn=None, flags=0):
data = self.encode(value)
data = self.db.get(key, data, txn, flags)
return self.decode(data)
def cursor(self, txn=None, flags=0):
c = DBShelfCursor(self.db.cursor(txn, flags))
c.binary = self.binary
return c
def put(self, key, value, txn=None, flags=0):
data = self.encode(value)
return self.db.put(key, data, txn, flags)
def join(self, cursorList, flags=0):
raise NotImplementedError
#----------------------------------------------
# Methods allowed to pass-through to self.db
#
# close, delete, fd, get_byteswapped, get_type, has_key,
# key_range, open, remove, rename, stat, sync,
# upgrade, verify, and all set_* methods.
#---------------------------------------------------------------------------
class DBShelfCursor:
"""
"""
def __init__(self, cursor):
self.dbc = cursor
def __del__(self):
self.close()
def __getattr__(self, name):
"""Some methods we can just pass through to the cursor object. (See below)"""
return getattr(self.dbc, name)
#----------------------------------------------
def dup(self, flags=0):
return DBShelfCursor(self.dbc.dup(flags))
def put(self, key, value, flags=0):
data = self.encode(value)
return self.dbc.put(key, data, flags)
def get(self, *args):
count = len(args) # a method overloading hack
method = getattr(self, 'get_%d' % count)
apply(method, args)
def get_1(self, flags):
rec = self.dbc.get(flags)
return self._extract(rec)
def get_2(self, key, flags):
rec = self.dbc.get(key, flags)
return self._extract(rec)
def get_3(self, key, value, flags):
data = self.encode(value)
rec = self.dbc.get(key, flags)
return self._extract(rec)
def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
def get_both(self, key, value, flags=0):
data = self.encode(value)
rec = self.dbc.get_both(key, flags)
return self._extract(rec)
def set(self, key, flags=0):
rec = self.dbc.set(key, flags)
return self._extract(rec)
def set_range(self, key, flags=0):
rec = self.dbc.set_range(key, flags)
return self._extract(rec)
def set_recno(self, recno, flags=0):
rec = self.dbc.set_recno(recno, flags)
return self._extract(rec)
set_both = get_both
def _extract(self, rec):
if rec is None:
return None
else:
key, data = rec
return key, self.decode(data)
#----------------------------------------------
# Methods allowed to pass-through to self.dbc
#
# close, count, delete, get_recno, join_item
#---------------------------------------------------------------------------
-122
View File
@@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""dependency analyzer"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
#import pisi.packagedb as packagedb
from pisi.version import Version
import pisi.pxml.autoxml as autoxml
from pisi.util import Checks
import pisi.itembyrepodb
class Dependency:
__metaclass__ = autoxml.autoxml
s_Package = [autoxml.String, autoxml.mandatory]
a_version = [autoxml.String, autoxml.optional]
a_versionFrom = [autoxml.String, autoxml.optional]
a_versionTo = [autoxml.String, autoxml.optional]
a_release = [autoxml.String, autoxml.optional]
a_releaseFrom = [autoxml.String, autoxml.optional]
a_releaseTo = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.package
if self.versionFrom:
s += _(" version >= ") + self.versionFrom
if self.versionTo:
s += _(" version <= ") + self.versionTo
if self.releaseFrom:
s += _(" release >= ") + self.releaseFrom
if self.releaseTo:
s += _(" release <= ") + self.releaseTo
return s
def satisfies(self, pkg_name, version, release):
"""determine if a package ver. satisfies given dependency spec"""
ret = True
v = Version(version)
if self.version:
ret &= v == Version(self.version)
if self.versionFrom:
ret &= v >= Version(self.versionFrom)
if self.versionTo:
ret &= v <= Version(self.versionTo)
r = Version(release)
if self.release:
ret &= r == Version(self.release)
if self.releaseFrom:
ret &= r >= Version(self.releaseFrom)
if self.releaseTo:
ret &= r <= Version(self.releaseTo)
return ret
def dict_satisfies_dep(dict, depinfo):
"""determine if a package in a dictionary satisfies given dependency spec"""
pkg_name = depinfo.package
if not dict.has_key(pkg_name):
return False
else:
pkg = dict[pkg_name]
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def installed_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
dependency spec"""
pkg_name = depinfo.package
if not ctx.installdb.is_installed(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name, pisi.itembyrepodb.installed)
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def repo_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
dependency spec"""
pkg_name = depinfo.package
if not ctx.packagedb.has_package(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name)
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
for dep in deps:
if not sat(dep):
ctx.ui.error(_('Package %s does not satisfy dependency %s') %
(pkg,dep))
return False
return True
def satisfies_runtime_deps(pkg):
deps = ctx.packagedb.get_package(pkg).runtimeDependencies()
return satisfies_dependencies(pkg, deps)
def installable(pkg):
"""calculate if pkg name is installable currently
which means it has to satisfy both install and runtime dependencies"""
if not ctx.packagedb.has_package(pkg):
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
return False
elif satisfies_runtime_deps(pkg):
return True
else:
return False
-369
View File
@@ -1,369 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Yet another Pisi module for fetching files from various sources. Of
course, this is not limited to just fetching source files. We fetch
all kinds of things: source tarballs, index files, packages, and God
knows what."""
# python standard library modules
import urllib2
import urllib
import ftplib
import os
import socket
import sys
from mimetypes import guess_type
from mimetools import Message
from base64 import encodestring
from shutil import move
from time import time
from time import gmtime
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.util as util
import pisi.context as ctx
from pisi.uri import URI
class FetchError(pisi.Error):
pass
class RangeError(pisi.Error):
pass
# helper functions
def fetch_url(url, destdir, progress=None, resume=True):
fetch = Fetcher(url, destdir)
if not resume:
fetch.resume = False
fetch.progress = progress
fetch.fetch()
class Fetcher:
"""Fetcher can fetch a file from various sources using various
protocols."""
def __init__(self, url, destdir, resume = True):
if not isinstance(url, URI):
url = URI(url)
if ctx.config.get_option("authinfo"):
url.set_auth_info(ctx.config.get_option("authinfo"))
self.resume = resume
self.scheme = url.scheme()
self.url = url
self.destdir = destdir
util.check_dir(self.destdir)
self.eta = '??:??:??'
self.percent = 0
self.rate = 0.0
self.progress = None
self.exist_size = 0
def fetch (self):
"""Return value: Fetched file's full path.."""
if not self.url.filename():
self.err(_('Filename error'))
if not os.access(self.destdir, os.W_OK):
self.err(_('Access denied to write to destination directory: "%s"') % (self.destdir))
archive_file = os.path.join(self.destdir, self.url.filename())
if os.path.exists(archive_file) and not os.access(archive_file, os.W_OK):
self.err(_('Access denied to destination file: "%s"') % (archive_file))
partial_file = archive_file + '.part'
if self.url.is_local_file():
self.fetchLocalFile(partial_file)
else:
self.fetchRemoteFile(partial_file)
if os.stat(partial_file).st_size == 0:
os.remove(partial_file)
self.err(_('A problem occured. Please check the archive address and/or permissions again.'))
move(partial_file, archive_file)
return archive_file
def _do_grab(self, fileURI, dest, total_size):
bs, tt, = 1024, int(time())
s_time = time()
Tdiff = lambda: time() - s_time
downloaded_size = exist_size = self.exist_size
symbol = 'B/s'
st = time()
chunk = fileURI.read(bs)
downloaded_size += len(chunk)
if self.progress:
p = self.progress(total_size, exist_size)
self.percent = p.update(downloaded_size)
self.complete = False
while chunk:
dest.write(chunk)
chunk = fileURI.read(bs)
downloaded_size += len(chunk)
ct = time()
if int(tt) != int(ct):
self.rate = (downloaded_size - exist_size) / (ct - st)
if self.percent:
self.eta = '%02d:%02d:%02d' %\
tuple([i for i in gmtime((Tdiff() * (100 - self.percent)) / self.percent)[3:6]])
self.rate, symbol = util.human_readable_rate(self.rate)
tt = time()
if self.progress:
if p.update(downloaded_size):
self.percent = p.percent
if not self.complete:
ctx.ui.display_progress(operation = "fetching",
percent = self.percent,
filename = self.url.filename(),
total_size = total_size,
downloaded_size = downloaded_size,
rate = self.rate,
eta = self.eta,
symbol = symbol)
if self.percent == 100: #FIXME: will be superseded by a
self.complete = True # working progress interface
dest.close()
def fetchLocalFile (self, archive_file):
url = self.url
if not os.access(url.path(), os.F_OK):
self.err(_('No such file or no permission to read'))
dest = open(archive_file, 'w')
total_size = os.path.getsize(url.path())
fileObj = open(url.path())
self._do_grab(fileObj, dest, total_size)
def fetchRemoteFile (self, archive_file):
from httplib import HTTPException
if os.path.exists(archive_file) and self.resume:
if self.scheme == 'http' or self.scheme == 'https' or self.scheme == 'ftp':
self.exist_size = os.path.getsize(archive_file)
dest = open(archive_file, 'ab')
else:
dest = open(archive_file, 'wb')
uri = self.url.get_uri()
flag = 1
try:
try:
try:
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
except RangeError:
ctx.ui.info(_('Requested range not satisfiable, starting again.'))
dest = open(archive_file, 'wb')
self.exist_size = 0
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
headers = fileObj.info()
flag = 0
except ValueError, e:
self.err(_('Cannot fetch %s; value error: %s') % (uri, e))
except urllib2.HTTPError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e))
except urllib2.URLError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e[-1][-1]))
except OSError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e))
except HTTPException, e:
self.err(_('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e))
finally:
if flag:
if os.stat(archive_file).st_size == 0:
os.remove(archive_file)
try:
total_size = int(headers['Content-Length']) + self.exist_size
except:
total_size = 0
self._do_grab(fileObj, dest, total_size)
def formatRequest(self, request):
if self.url.auth_info():
enc = encodestring('%s:%s' % self.url.auth_info())
request.add_header('Authorization', 'Basic %s' % enc)
range_handlers = {
'http' : HTTPRangeHandler,
'https': HTTPRangeHandler,
'ftp' : FTPRangeHandler
}
if self.exist_size and range_handlers.has_key(self.scheme):
opener = urllib2.build_opener(range_handlers.get(self.scheme)())
urllib2.install_opener(opener)
request.add_header('Range', 'bytes=%d-' % self.exist_size)
proxy_handler = None
if ctx.config.values.general.http_proxy and self.url.scheme() == "http":
http_proxy = ctx.config.values.general.http_proxy
proxy_handler = urllib2.ProxyHandler({URI(http_proxy).scheme(): http_proxy})
elif ctx.config.values.general.https_proxy and self.url.scheme() == "https":
https_proxy = ctx.config.values.general.https_proxy
proxy_handler = urllib2.ProxyHandler({URI(https_proxy): https_proxy})
elif ctx.config.values.general.ftp_proxy and self.url.scheme() == "ftp":
ftp_proxy = ctx.config.values.general.ftp_proxy
proxy_handler = urllib2.ProxyHandler({URI(http_proxy): ftp_proxy})
if proxy_handler:
ctx.ui.info(_("Proxy configuration has been found for '%s' protocol") % self.url.scheme())
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
return request
def err (self, error):
raise FetchError(error)
class HTTPRangeHandler(urllib2.BaseHandler):
"""
to override the urllib2 error: 'Error 206: Partial Content'
this reponse from the HTTP server is already what we expected to get.
Don't give up, resume downloading..
"""
def http_error_206(self, request, fp, errcode, msg, headers):
return urllib.addinfourl(fp, headers, request.get_full_url())
def http_error_416(self, request, fp, errcode, msg, headers):
# HTTP 1.1's 'Range Not Satisfiable' error..
raise RangeError
class FTPRangeHandler(urllib2.FTPHandler):
"""
FTP Range support..
"""
def ftp_open(self, req):
host = req.get_host()
host, port = urllib.splitport(host)
if port is None:
port = ftplib.FTP_PORT
try:
host = socket.gethostbyname(host)
except socket.error, msg:
raise FetchError(msg)
path, attrs = urllib.splitattr(req.get_selector())
dirs = path.split('/')
dirs = map(urllib.unquote, dirs)
dirs, file = dirs[:-1], dirs[-1]
if dirs and not dirs[0]:
dirs = dirs[1:]
try:
fw = self.connect_ftp('', '', host, port, dirs)
type = file and 'I' or 'D'
for attr in attrs:
attr, value = urllib.splitattr(attr)
if attr.lower() == 'type' and \
value in ('a', 'A', 'i', 'I', 'd', 'D'):
type = value.upper()
rawr = req.headers.get('Range', None)
if rawr:
rest = int(rawr.split("=")[1].rstrip("-"))
else:
rest = 0
fp, retrlen = fw.retrfile(file, type, rest)
fb, lb = rest, retrlen
if retrlen is None or retrlen == 0:
raise RangeError
retrlen = lb - fb
if retrlen < 0:
# beginning of range is larger than file
raise RangeError
headers = ''
mtype = guess_type(req.get_full_url())[0]
if mtype:
headers += 'Content-Type: %s\n' % mtype
if retrlen is not None and retrlen >= 0:
headers += 'Content-Length: %d\n' % retrlen
try:
from cStringIO import StringIO
except ImportError, msg:
from StringIO import StringIO
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
except ftplib.all_errors, msg:
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
def connect_ftp(self, user, passwd, host, port, dirs):
fw = ftpwrapper('', '', host, port, dirs)
return fw
class ftpwrapper(urllib.ftpwrapper):
def retrfile(self, file, type, rest=None):
self.endtransfer()
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
else: cmd = 'TYPE ' + type; isdir = 0
try:
self.ftp.voidcmd(cmd)
except ftplib.all_errors:
self.init()
self.ftp.voidcmd(cmd)
conn = None
if file and not isdir:
try:
self.ftp.nlst(file)
except ftplib.error_perm, reason:
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
# Restore the transfer mode!
self.ftp.voidcmd(cmd)
try:
cmd = 'RETR ' + file
conn = self.ftp.ntransfercmd(cmd, rest)
except ftplib.error_perm, reason:
if str(reason)[:3] == '501':
# workaround for REST not suported error
fp, retrlen = self.retrfile(file, type)
fp = RangeableFileObject(fp, (rest,''))
return (fp, retrlen)
elif str(reason)[:3] != '550':
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
if not conn:
self.ftp.voidcmd('TYPE A')
if file: cmd = 'LIST ' + file
else: cmd = 'LIST'
conn = self.ftp.ntransfercmd(cmd)
self.busy = 1
return (urllib.addclosehook(conn[0].makefile('rb'),
self.endtransfer), conn[1])
-265
View File
@@ -1,265 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
generic file abstraction that allows us to use URIs for everything
we support only the simple read case ATM
we are just encapsulating a common pattern in our program, nothing big.
like all pisi classes, it has been programmed in a non-restrictive way
"""
import os
import types
import bz2
import shutil
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from pisi.uri import URI
from pisi.util import join_path as join
from pisi.fetcher import fetch_url
import pisi.context as ctx
class AlreadyHaveException(pisi.Exception):
def __init__(self, url, localfile):
pisi.Exception.__init__(self, "URL %s already downloaded as %s" % (url, localfile))
self.url = url
self.localfile = localfile
class NoSignatureFound(pisi.Exception):
def __init__(self, url):
pisi.Exception.__init__(self, "No signature found for %s" % url)
self.url = url
class Error(pisi.Error):
pass
class InvalidSignature(pisi.Error):
def __init__(self, url):
pisi.Exception.__init__(self, " invalid for %s" % url)
self.url = url
class File:
(read, write) = range(2) # modes
(bz2, gzip, auto) = range(3) # compress enums
(detached, whatelse) = range(2)
@staticmethod
def make_uri(uri):
"handle URI arg"
if type(uri) == types.StringType or type(uri) == types.UnicodeType:
uri = URI(uri)
elif not isinstance(uri, URI):
raise Error(_("uri must have type either URI or string"))
return uri
@staticmethod
def choose_method(filename, compress):
# this is really simple (^_^) -- exa
if compress == File.auto:
if filename.endswith('.bz2'):
return File.bz2
elif filename.endswith('.gz'):
return File.gzip
else:
return None
else:
return compress
@staticmethod
def decompress(localfile, compress):
compress = File.choose_method(localfile, compress)
if compress == File.bz2:
open(localfile[:-4], "w").write(bz2.BZ2File(localfile).read())
localfile = localfile[:-4]
elif compress == File.gzip:
raise Error(_("zip compression not supported yet"))
return localfile
@staticmethod
def download(uri, transfer_dir = "/tmp", sha1sum = False,
compress = None, sign = None, copylocal = False):
assert isinstance(uri, URI)
if sha1sum:
sha1filename = File.download(URI(uri.get_uri() + '.sha1sum'), transfer_dir)
sha1f = file(sha1filename)
newsha1 = sha1f.readlines()[0]
if uri.is_remote_file() or copylocal:
localfile = join(transfer_dir, uri.filename())
# TODO: code to use old .sha1sum file, is this a necessary optimization?
#oldsha1fn = localfile + '.sha1sum'
#if os.exists(oldsha1fn):
#oldsha1 = file(oldsha1fn).readlines()[0]
if sha1sum and os.path.exists(localfile):
oldsha1 = pisi.util.sha1_file(localfile)
if (newsha1 == oldsha1):
# early terminate, we already got it ;)
raise AlreadyHaveException(uri, localfile)
if uri.is_remote_file():
ctx.ui.info(_("Fetching %s") % uri.get_uri(), verbose=True)
fetch_url(uri, transfer_dir, ctx.ui.Progress)
else:
# copy to transfer dir,
localfile = join(transfer_dir, uri.filename())
ctx.ui.info(_("Copying %s to transfer dir") % uri.get_uri(), verbose=True)
shutil.copy(uri.get_uri(), transfer_dir)
else:
localfile = uri.get_uri() #TODO: use a special function here?
if not os.path.exists(localfile):
raise IOError(_("File '%s' not found.") % localfile)
if not os.access(localfile, os.W_OK):
oldfn = localfile
localfile = join(transfer_dir, os.path.basename(localfile))
shutil.copy(oldfn, localfile)
if sha1sum:
if (pisi.util.sha1_file(localfile) != newsha1):
raise Error(_("File integrity of %s compromised.") % uri)
localfile = File.decompress(localfile, compress)
return localfile
def __init__(self, uri, mode, transfer_dir = "/tmp",
sha1sum = False, compress = None, sign = None):
"it is pointless to open a file without a URI and a mode"
self.transfer_dir = transfer_dir
self.sha1sum = sha1sum
self.compress = compress
self.sign = sign
uri = File.make_uri(uri)
if mode==File.read or mode==File.write:
self.mode = mode
else:
raise Error(_("File mode must be either File.read or File.write"))
if uri.is_remote_file():
if self.mode == File.read:
localfile = File.download(uri, transfer_dir, sha1sum, compress, sign)
else:
raise Error(_("Remote write not implemented"))
else:
localfile = uri.get_uri()
if self.mode == File.read:
localfile = File.decompress(localfile, self.compress)
if self.mode == File.read:
access = 'r'
else:
access = 'w'
self.__file__ = file(localfile, access)
self.localfile = localfile
def local_file(self):
"returns the underlying file object"
return self.__file__
def close(self, delete_transfer = False):
"this method must be called at the end of operation"
self.__file__.close()
if self.mode == File.write:
compressed_file = None
if self.compress == File.bz2:
compressed_file = self.localfile + ".bz2"
bz2.BZ2File(compressed_file, "w").write(open(self.localfile, "r").read())
elif self.compress == File.gzip:
raise Error(_("gzip compression not supported yet"))
if self.sha1sum:
sha1 = pisi.util.sha1_file(self.localfile)
cs = file(self.localfile + '.sha1sum', 'w')
cs.write(sha1)
cs.close()
if compressed_file:
sha1 = pisi.util.sha1_file(compressed_file)
cs = file(compressed_file + '.sha1sum', 'w')
cs.write(sha1)
cs.close()
if self.sign==File.detached:
if pisi.util.run_batch('gpg --detach-sig ' + self.localfile)[0]:
raise Error(_("ERROR: gpg --detach-sig %s failed") % self.localfile)
if compressed_file:
if pisi.util.run_batch('gpg --detach-sig ' + compressed_file)[0]:
raise Error(_("ERROR: gpg --detach-sig %s failed") % compressed_file)
@staticmethod
def check_signature(uri, transfer_dir, sign=detached):
if sign==File.detached:
try:
sigfilename = File.download(URI(uri + '.sig'), transfer_dir)
except:
raise NoSignatureFound(uri)
if os.system('gpg --verify ' + sigfilename) != 0:
raise InvalidSignature(uri)
# everything is all right here
def flush(self):
self.__file__.flush()
def fileno(self):
return self.__file__.fileno()
def isatty(self):
return self.__file__.isatty()
def next(self):
return self.__file__.next()
def read(self, size = None):
if size:
return self.__file__.read(size)
else:
return self.__file__.read()
def readline(self, size = None):
if size:
return self.__file__.readline(size)
else:
return self.__file__.readline()
def readlines(self, size = None):
if size:
return self.__file__.readlines(size)
else:
return self.__file__.readlines()
def xreadlines(self):
return self.__file__.xreadlines()
def seek(self, offset, whence=0):
self.__file__.seek(offset, whence)
def tell(self):
return self.__file__.tell()
def truncate(self):
self.__file__.truncate()
def write(self, str):
self.__file__.write(str)
def writelines(self, sequence):
self.__file__.writelines(sequence)
-100
View File
@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
'''Files module provides access to files.xml. files.xml is generated
during the build process of a package and used in installation.'''
import pisi.pxml.autoxml as autoxml
import pisi.lockeddbshelve as shelve
class FileInfo:
"""File holds the information for a File node/tag in files.xml"""
__metaclass__ = autoxml.autoxml
t_Path = [ autoxml.String, autoxml.mandatory ]
t_Type = [ autoxml.String, autoxml.mandatory ]
t_Size = [ autoxml.Long, autoxml.optional ]
t_Hash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
t_Permanent = [ autoxml.String, autoxml.optional ]
def __str__(self):
s = "/%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
self.size, self.hash)
return s
class Files(autoxml.XmlFile):
__metaclass__ = autoxml.autoxml
tag = "Files"
t_List = [ [FileInfo], autoxml.optional, "File"]
def append(self, fileinfo):
self.list.append(fileinfo)
class FilesDB(shelve.LockedDBShelf):
def __init__(self):
shelve.LockedDBShelf.__init__(self, 'files')
def add_files(self, pkg_name, files, txn = None):
def proc(txn):
for x in files.list:
path = x.path
del x.path # don't store redundant attribute in db
self.put(path, (pkg_name, x), txn)
x.path = path # store it back in
self.txn_proc(proc, txn)
def remove_files(self, files, txn = None):
def proc(txn):
for x in files.list:
if self.has_key(x.path):
self.delete(x.path, txn)
self.txn_proc(proc, txn)
def has_file(self, path, txn = None):
return self.has_key(str(path), txn)
def get_file(self, path, txn = None):
path = str(path)
def proc(txn):
if not self.has_key(path, txn):
return None
else:
(name, fileinfo) = self.get(path, txn)
fileinfo.path = path
return (name, fileinfo)
return self.txn_proc(proc, txn)
def match_files(self, glob):
# NB: avoid using, this reads the entire db
import fnmatch
glob = str(glob)
infos = []
for key in self.keys():
if fnmatch.fnmatch(key, glob):
# FIXME: Why should we assign path attribute manually
# in fileinfo? This is also done in get_file(), seems
# like a dirty workaround... - baris
name = self[key][0]
fileinfo = self[key][1]
fileinfo.path = key
infos.append((name, fileinfo))
return infos
-175
View File
@@ -1,175 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# the most simple minded digraph class ever
import sys
import string
import pisi
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# not an error!
class CycleException(pisi.Exception):
def __init__(self, cycle):
self.cycle = cycle
def __str__(self):
return _('Encountered cycle %s') % self.cycle
class Digraph(object):
def __init__(self):
self.__v = set()
self.__adj = {}
self.__vdata = {}
self.__edata = {}
def vertices(self):
"return set of vertex descriptors"
return self.__v
def edges(self):
"return a list of edge descriptors"
list = []
for u in self.__v:
for v in self.__u:
list.append( (u,v) )
return list
def from_list(self, el):
"convert a list of edges (u,v) to graph"
for (u,v) in el:
self.add_edge(u,v)
def add_vertex(self, u, data = None):
"add vertex u, optionally with data"
assert not u in self.__v
self.__v.add(u)
self.__adj[u] = set()
if data:
self.__vdata[u] = data
self.__edata[u] = {}
def add_edge(self, u, v, edata = None, udata = None, vdata = None):
"add edge u -> v"
if not u in self.__v:
self.add_vertex(u, udata)
if not v in self.__v:
self.add_vertex(v, vdata)
self.__adj[u].add(v)
if edata != None:
self.__edata[u][v] = edata
def add_biedge(self, u, v, edata = None):
self.add_edge(u, v, edata)
self.add_edge(v, u, edata)
def set_vertex_data(self, u, data):
self.__vdata[u] = data
def vertex_data(self, u):
return self.__vdata[u]
def edge_data(self, u, v):
return self.__edata[u][v]
def has_vertex(self, u):
return u in self.__v
def has_edge(self, u,v):
if u in self.__v:
return v in self.__adj[u]
else:
return False
def adj(self, u):
return self.__adj[u]
def dfs(self, finish_hook = None):
self.color = {}
self.p = {}
self.d = {}
self.f = {}
for u in self.__v:
self.color[u] = 'w' # mark white (unexplored)
self.p[u] = None
self.time = 0
for u in self.__v:
if self.color[u] == 'w':
self.dfs_visit(u, finish_hook)
def dfs_visit(self, u, finish_hook):
self.color[u] = 'g' # mark green (discovered)
self.d[u] = self.time = self.time + 1
for v in self.adj(u):
if self.color[v] == 'w': # explore unexplored vertices
self.p[v] = u
self.dfs_visit(v, finish_hook)
elif self.color[v] == 'g': # cycle detected
cycle = [u]
while self.p[u]:
u = self.p[u]
cycle.append(u)
cycle.reverse()
raise CycleException(cycle)
self.color[u] = 'b' # mark black (completed)
if finish_hook:
finish_hook(u)
self.f[u] = self.time = self.time + 1
def cycle_free(self):
try:
self.dfs()
return True
except CycleException:
return False
def topological_sort(self):
list = []
self.dfs(lambda u: list.append(u))
list.reverse()
return list
def id_str(self, u):
def repl(char):
if char in string.punctuation:
return '_'
else:
return char
return pisi.util.concat(map(repl, str(u)))
def write_graphviz(self, f):
f.write('digraph G {\n')
for u in self.vertices():
f.write(self.id_str(u))
self.write_graphviz_vlabel(f, u)
f.write(';\n')
f.write('\n')
for u in self.vertices():
for v in self.adj(u):
f.write( self.id_str(u) + ' -> ' + self.id_str(v))
self.write_graphviz_elabel(f, u, v)
f.write(';\n')
f.write('\n')
f.write('}\n')
def write_graphviz_vlabel(self, f, u):
pass
def write_graphviz_elabel(self, f, u, v):
pass
-178
View File
@@ -1,178 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""PiSi source/package index"""
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.specfile as specfile
import pisi.metadata as metadata
import pisi.packagedb as packagedb
import pisi.sourcedb as sourcedb
import pisi.util as util
from pisi.package import Package
from pisi.pxml.xmlfile import XmlFile
from pisi.file import File
import pisi.pxml.autoxml as autoxml
from pisi.uri import URI
import pisi.component as component
import pisi.specfile as specfile
class Error(pisi.Error):
pass
class Index(XmlFile):
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Distribution = [ component.Distribution, autoxml.optional ]
t_Specs = [ [specfile.SpecFile], autoxml.optional, "SpecFile"]
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
#t_Metadatas = [ [metadata.MetaData], autoxml.optional, "MetaData"]
t_Components = [ [component.Component], autoxml.optional, "Component"]
def name():
return self.distribution.name + self.distribution.repositoryname
def read_uri(self, uri, tmpdir, force = False):
self.read(uri, tmpDir=tmpdir, sha1sum=not force,
compress=File.auto, sign=File.detached, copylocal = True)
# read index for a given repo, force means download even if remote not updated
def read_uri_of_repo(self, uri, repo = None, force = False):
"""Read PSPEC file"""
if repo:
tmpdir = os.path.join(ctx.config.index_dir(), repo)
else:
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
pisi.util.clean_dir(tmpdir)
pisi.util.check_dir(tmpdir)
# write uri
urlfile = file(pisi.util.join_path(tmpdir, 'uri'), 'w')
urlfile.write(uri) # uri
urlfile.close()
self.read_uri(uri, tmpdir, force)
if not repo:
repo = self.distribution.name()
# and what do we do with it? move it to index dir properly
newtmpdir = os.path.join(ctx.config.index_dir(), repo)
pisi.util.clean_dir(newtmpdir) # replace newtmpdir
shutil.move(tmpdir, newtmpdir)
def check_signature(self, filename, repo):
tmpdir = os.path.join(ctx.config.index_dir(), repo)
File.check_signature(filename, tmpdir)
def index(self, repo_uri, skip_sources=False):
self.repo_dir = repo_uri
for root, dirs, files in os.walk(repo_uri):
for fn in files:
if fn.endswith(ctx.const.package_suffix):
ctx.ui.info(_('Adding %s to package index') % fn)
self.add_package(os.path.join(root, fn), repo_uri)
if fn == 'component.xml':
ctx.ui.info(_('Adding %s to component index') % fn)
self.add_component(os.path.join(root, fn))
if fn == 'pspec.xml' and not skip_sources:
self.add_spec(os.path.join(root, fn), repo_uri)
if fn == 'distribution.xml':
self.add_distro(os.path.join(root, fn))
def update_db(self, repo, txn = None):
# FIXME: updating db takes too much time. So a notify mechanism is used to inform the status
# of the operation.
self.progress = ctx.ui.Progress(len(self.packages)+len(self.specs))
self.processed = 0
def update_progress():
self.processed += 1
ctx.ui.display_progress(operation = "updatingrepo",
percent = self.progress.update(self.processed),
info = _("Updating package database of %s") % repo)
ctx.componentdb.remove_repo(repo, txn=txn)
for comp in self.components:
ctx.componentdb.update_component(comp, repo, txn)
ctx.packagedb.remove_repo(repo, txn=txn)
for pkg in self.packages:
ctx.packagedb.add_package(pkg, repo, txn=txn)
update_progress()
ctx.sourcedb.remove_repo(repo, txn=txn)
for sf in self.specs:
ctx.sourcedb.add_spec(sf, repo, txn=txn)
update_progress()
def add_package(self, path, repo_uri):
package = Package(path, 'r')
md = package.get_metadata()
md.package.packageSize = os.path.getsize(path)
md.package.packageHash = util.sha1_file(path)
if ctx.config.options and ctx.config.options.absolute_urls:
md.package.packageURI = os.path.realpath(path)
else: # create relative path by default
# TODO: in the future well do all of this with purl/pfile/&helpers
# really? heheh -- future exa
md.package.packageURI = util.removepathprefix(repo_uri, path)
# check package semantics
errs = md.errors()
if md.errors():
ctx.ui.error(_('Package %s: metadata corrupt, skipping...') % md.package.name)
ctx.ui.error(unicode(Error(*errs)))
else:
self.packages.append(md.package)
def add_component(self, path):
comp = component.Component()
#try:
comp.read(path)
self.components.append(comp)
#except:
# raise Error(_('Component in %s is corrupt') % path)
#ctx.ui.error(str(Error(*errs)))
def add_distro(self, path):
distro = component.Distribution()
#try:
distro.read(path)
self.distribution = distro
#except:
# raise Error(_('Distribution in %s is corrupt') % path)
#ctx.ui.error(str(Error(*errs)))
def add_spec(self, path, repo_uri):
import pisi.build
ctx.ui.info(_('Adding %s to source index') % path)
#TODO: may use try/except to handle this
builder = pisi.build.Builder(path)
#ctx.ui.error(_('SpecFile in %s is corrupt, skipping...') % path)
#ctx.ui.error(str(Error(*errs)))
builder.fetch_component()
sf = builder.spec
if ctx.config.options and ctx.config.options.absolute_urls:
sf.source.sourceURI = os.path.realpath(path)
else: # create relative path by default
sf.source.sourceURI = util.removepathprefix(repo_uri, path)
# check component
self.specs.append(sf)
-215
View File
@@ -1,215 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
#
# installation database
#
# System
import os
import fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi
import pisi
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
from pisi.files import Files
import pisi.util as util
from pisi.util import join_path as join
class InstallDBError(pisi.Error):
pass
class InstallInfo:
# some data is replicated from packagedb
# we store as an object, hey, we can waste O(1) space.
# this is also easier to modify in the future, without
# requiring database upgrades! wow!
def __init__(self, state, version, release, build, distribution, time):
self.state = state
self.version = version
self.release = release
self.build = build
self.distribution = distribution
self.time = time
def one_liner(self):
import time
time_str = time.strftime("%d %b %Y %H:%M", self.time)
s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release,
self.build, self.distribution,
time_str)
return s
state_map = { 'i': _('installed'), 'ip':_('installed-pending'),
'r':_('removed'), 'p': _('purged') }
def __str__(self):
s = _("State: %s\nVersion: %s, Release: %s, Build: %s\n") % \
(InstallInfo.state_map[self.state], self.version,
self.release, self.build)
import time
time_str = time.strftime("%d %b %Y %H:%M", self.time)
s += _('Distribution: %s, Install Time: %s\n') % (self.distribution,
time_str)
return s
class InstallDB:
def __init__(self):
self.d = shelve.LockedDBShelf('install')
self.dp = shelve.LockedDBShelf('configpending')
self.files_dir = join(ctx.config.db_dir(), 'files')
def close(self):
self.d.close()
self.dp.close()
def files_name(self, pkg, version, release):
pkg_dir = self.pkg_dir(pkg, version, release)
return join(pkg_dir, ctx.const.files_xml)
def files(self, pkg):
pkg = str(pkg)
pkginfo = self.d[pkg]
files = Files()
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
return files
def pkg_dir(self, pkg, version, release):
return join(ctx.config.lib_dir(), 'package',
pkg + '-' + version + '-' + release)
def is_recorded(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
return self.d.has_key(pkg)
return self.d.txn_proc(proc, txn)
def is_installed(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.is_recorded(pkg, txn):
info = self.d.get(pkg, txn)
return info.state=='i' or info.state=='ip'
else:
return False
return self.d.txn_proc(proc, txn)
def list_installed(self, txn = None):
def proc(txn):
list = []
for (pkg, info) in self.d.items(txn):
if info.state=='i' or info.state=='ip':
list.append(pkg)
return list
return self.d.txn_proc(proc, txn)
def list_pending(self):
# warning: reads the entire db
dict = {}
for (pkg, x) in self.dp.items():
pkginfo = self.d[pkg]
dict[pkg] = pkginfo
return dict
def get_info(self, pkg):
pkg = str(pkg)
return self.d[pkg]
def get_version(self, pkg):
pkg = str(pkg)
info = self.d[pkg]
return (info.version, info.release, info.build)
def is_removed(self, pkg):
pkg = str(pkg)
if self.is_recorded(pkg):
info = self.d[pkg]
return info.state=='r'
else:
return False
def install(self, pkg, version, release, build, distro = "",
config_later = False, rebuild=False, txn = None):
"""install package with specific version, release, build"""
pkg = str(pkg)
def proc(txn):
if self.is_installed(pkg, txn):
raise InstallDBError(_("Already installed"))
if config_later:
state = 'ip'
self.dp.put(pkg, True, txn)
else:
state = 'i'
# FIXME: it might be more appropriate to pass date
# as an argument, or installation data afterwards
# to do this -- exa
if not rebuild:
import time
ctime = time.localtime()
else:
files_xml = self.files_name(pkg, version, release)
ctime = util.creation_time(files_xml)
self.d.put(pkg, InstallInfo(state, version, release, build, distro, ctime), txn)
self.d.txn_proc(proc,txn)
def clear_pending(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
if self.is_installed(pkg, txn):
assert info.state == 'ip'
info.state = 'i'
self.d.put(pkg, info, txn)
self.dp.delete(pkg, txn)
self.d.txn_proc(proc,txn)
def remove(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
info.state = 'r'
self.d.put(pkg, info, txn)
self.d.txn_proc(proc, txn)
def purge(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.d.has_key(pkg, txn):
self.d.delete(pkg, txn)
self.d.txn_proc(proc, txn)
db = None
def init():
global db
if db:
return db
db = InstallDB()
return db
def finalize():
global db
if db:
db.close()
db = None
-220
View File
@@ -1,220 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import fcntl
import types
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
installed, thirdparty, repos, all = range(1, 5)
"""installed and thirdparty are special databases to keep track
of already installed stuff and third party stuff not in any real repository.
repos means search in repositories only, and all means search in
repositories and special databases (called tracking databases)
"""
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
pass
class ItemByRepoDB(object):
def __init__(self, name):
self.d = shelve.LockedDBShelf(name)
#self.dbyrepo = shelve.LockedDBShelf(name + '-byrepo')
def close(self):
self.d.close()
def clear(self, txn = None):
self.d.clear(txn=txn)
def txn_proc(self, proc, txn):
return self.d.txn_proc(proc, txn)
def items(self):
return self.d.items()
@staticmethod
def not_just_tracking(data):
keys = data.keys()
if len(keys)==1:
if 'trdparty' in keys or 'inst' in keys:
return False
elif len(keys)==2:
if 'trdparty' in keys and 'inst' in keys:
return False
return True
#below is a slower way
#for x in data.keys():
# if x.startsWith('repo-'):
# return True
#return False
def list_if(self, pred):
return [ k for k,data in self.d.items() if pred(k, data)]
def list(self, repo = None):
if repo == None:
repo = repos
if repo not in [repos, all]:
return [ k for k,data in self.d.items() if data.has_key(self.repo_str(repo))]
else:
if repo == all:
return [ pkg for pkg in self.d.keys() ]
else:
return self.list_if( lambda k,data: ItemByRepoDB.not_just_tracking(data) )
# TODO: carry this to repodb, really :/
def order(self, repo = None):
if repo == None:
repo = repos
assert repo in [all, repos]
import pisi.repodb
order = [ 'repo-' + x for x in ctx.repodb.list() ]
if repo == all:
order += ['trdparty', 'inst']
return order
# def list_repo(self, repo):
# return self.dbyrepo[repo]
def repo_str(self, repo):
if repo==thirdparty:
repo='trdparty'
elif repo==installed:
repo='inst'
else:
assert type(repo) == type("")
repo='repo-'+repo
return repo
def str_repo(self, str):
if str.startswith('repo-'):
return str[5:]
elif str=='trdparty':
return thirdparty
elif str=='inst':
return installed
else:
raise Error(_('Invalid repository string'))
def has_key(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
haskey = self.d.has_key(name, txn)
if repo == all:
return haskey
elif repo == repos:
data = self.d.get(name, txn)
return haskey and ItemByRepoDB.not_just_tracking(data)
else:
repostr = self.repo_str(repo)
return haskey and self.d.get(name, txn).has_key(repostr)
def get_item_repo(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
def proc(txn):
if not self.d.has_key(name, txn=txn):
raise NotfoundError(_('Key %s not found') % name)
s = self.d.get(name, txn=txn)
if repo in [repos, all]:
for repostr in self.order(repo):
if s.has_key(repostr):
return (s[repostr], self.str_repo(repostr))
else:
repostr = self.repo_str(repo)
if s.has_key(repostr):
return (s[repostr], repo)
raise NotfoundError(_('Key %s in repo %s not found') % (name, repo))
#return None
return self.d.txn_proc(proc, txn)
def get_item(self, name, repo = None, txn = None):
if repo == None:
repo = repos
x = self.get_item_repo(name, repo, txn)
if x:
item, repo = x
# discard repo, not always needed
return item
else:
return None
def which_repo(self, name, txn = None):
x = self.get_item_repo(name, txn=txn)
if x:
item, repo = x
return repo
else:
return None
def add_item(self, name, obj, repo, txn = None):
assert not repo in [all, repos]
repostr = self.repo_str(repo)
def proc(txn):
if not self.d.has_key(name):
s = dict()
else:
s = self.d.get(name, txn)
s[ repostr ] = obj
self.d.put(name, s, txn)
self.d.txn_proc(proc, txn)
def remove_item_repo(self, name, repo, txn = None):
assert not repo in [all, repos]
name = str(name)
def p(txn):
s = self.d.get(name, txn)
repostr = self.repo_str(repo)
if s.has_key(repostr):
del s[repostr]
if not len(s):
self.d.delete(name, txn)
else:
self.d.put(name, s, txn)
self.d.txn_proc(p, txn)
def remove_item_only(self, name, txn = None):
def p(txn):
repo = self.which_repo(name, txn=txn)
self.remove_item_repo(name, repo, txn=txn)
self.d.txn_proc(p, txn)
def remove_item(self, name, repo=None, txn=None):
if repo == None:
repo = repos
if not repo in [all, repos]:
self.remove_item_repo(name, repo,txn=txn)
else:
self.remove_item_only(name,txn=txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
for key in self.d.keys():
self.remove_item_repo(key, repo, txn=txn)
self.d.txn_proc(proc, txn)
-175
View File
@@ -1,175 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import fcntl
import types
import bsddb3.db as db
import bsddb3.dbobj as dbobj
import pisi.dbshelve as shelve
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.util import join_path
from pisi.version import Version
class Error(pisi.Error):
pass
# check database version
# if write is given it knows it has write access
# if force is given it updates the specified db version
def check_dbversion(versionfile, ver, write=False, update=False):
verfn = join_path(pisi.context.config.db_dir(), versionfile)
firsttime = False
if os.path.exists(verfn):
verfile = file(verfn, 'r')
ls = verfile.readlines()
currver = Version(ls[0])
dbver = Version(ver)
if currver < dbver:
if not update:
raise Error(_('Database version for %s insufficient. Please run rebuild-db command.') % versionfile)
else:
pass # continue to update, then
elif currver > dbver:
raise Error(_('Database version for %s greater than PiSi version. You need a newer PiSi.') % versionfile)
elif not update:
return True # db version is OK
else:
firsttime = True
if write and (update or firsttime):
if os.access(pisi.context.config.db_dir(), os.W_OK):
verfile = file(verfn, 'w')
verfile.write(ver)
verfile.close()
else:
raise Error(_('Cannot attain write access to database environment'))
else:
raise Error(_('Database version %s not present.') % versionfile)
def lock_dbenv():
ctx.dbenv_lock = file(join_path(pisi.context.config.db_dir(), 'dbenv.lock'), 'w')
try:
fcntl.flock(ctx.dbenv_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
# write: write access to database environment
# writeversion: would you like to be able
def init_dbenv(write=False, writeversion=False):
if os.access(pisi.context.config.db_dir(), os.R_OK):
# try to read version
check_dbversion('dbversion', pisi.__dbversion__, write=write, update=writeversion)
check_dbversion('filesdbversion', pisi.__filesdbversion__, write=write, update=writeversion)
else:
raise Error(_('Cannot attain read access to database environment'))
if write:
if os.access(pisi.context.config.db_dir(), os.W_OK):
lock_dbenv()
ctx.dbenv = dbobj.DBEnv()
flags = (db.DB_INIT_MPOOL | # cache
db.DB_INIT_TXN | # transaction subsystem
db.DB_INIT_LOG | # logging subsystem
db.DB_RECOVER | # run normal recovery
db.DB_CREATE) # allow db to create files
ctx.dbenv.set_cachesize(0, 4*1024*1024)
ctx.dbenv.open(pisi.context.config.db_dir(), flags)
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
else:
raise Error(_("Cannot write to PiSi database."))
else:
ctx.dbenv = None # read-only access to database
class LockedDBShelf(shelve.DBShelf):
"""A simple wrapper to implement locking for bsddb's dbshelf"""
def __init__(self, dbname, mode=0644,
filetype=db.DB_BTREE, dbenv = None):
if dbenv == None:
dbenv = ctx.dbenv
shelve.DBShelf.__init__(self, dbenv)
filename = join_path(pisi.context.config.db_dir(), dbname + '.bdb')
if dbenv and os.access(os.path.dirname(filename), os.W_OK):
flags = 'w'
elif os.access(filename, os.R_OK):
flags = 'r'
else:
raise Error(_('Cannot attain read or write access to database %s') % dbname)
self.open(filename, dbname, filetype, flags, mode)
def destroy(self):
os.unlink(self.filename)
def __del__(self):
# superclass does something funky, we don't need that
pass
def open(self, filename, dbname, filetype, flags=db.DB_CREATE, mode=0644):
self.filename = filename
self.closed = False
if type(flags) == type(''):
sflag = flags
if sflag == 'r':
flags = db.DB_RDONLY
elif sflag == 'rw':
flags = 0
elif sflag == 'w':
flags = db.DB_CREATE
elif sflag == 'c':
flags = db.DB_CREATE
elif sflag == 'n':
flags = db.DB_TRUNCATE | db.DB_CREATE
else:
raise Error, _("Flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
self.flags = flags
if self.flags & db.DB_RDONLY == 0:
flags |= db.DB_AUTO_COMMIT # use txn subsystem in write mode
self.lock()
filename = os.path.realpath(filename) # we give absolute path due to dbenv
#print 'opening', filename, filetype, flags, mode
return self.db.open(filename, None, filetype, flags, mode)
def lock(self):
self.lockfile = file(self.filename + '.lock', 'w')
try:
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
def close(self):
if self.closed:
return
self.db.close()
if self.flags & db.DB_RDONLY == 0:
self.unlock()
self.closed = True
def unlock(self):
self.lockfile.close()
os.unlink(self.filename + '.lock')
@staticmethod
def encodekey(key):
'''utility method for dbs that must store unicodes in keys'''
if type(key)==types.UnicodeType:
return key.encode('utf-8')
elif type(key)==types.StringType:
return key
else:
raise Error('Key must be either string or unicode')
-105
View File
@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
Metadata module provides access to metadata.xml. metadata.xml is
generated during the build process of a package and used in the
installation. Package repository also uses metadata.xml for building
a package index.
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
import pisi.specfile as specfile
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.util as util
class Source:
__metaclass__ = autoxml.autoxml
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [specfile.Packager, autoxml.mandatory]
class Package(specfile.Package):
__metaclass__ = autoxml.autoxml
t_Build = [ autoxml.Integer, autoxml.optional]
t_Distribution = [ autoxml.String, autoxml.mandatory]
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
t_Architecture = [ autoxml.String, autoxml.mandatory]
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
t_PackageSize = [ autoxml.Long, autoxml.optional]
t_PackageHash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
t_PackageURI = [ autoxml.String, autoxml.optional]
t_PackageFormat = [ autoxml.String, autoxml.optional]
t_Source = [ Source, autoxml.optional]
def decode_hook(self, node, errs, where):
self.version = self.history[0].version
self.release = self.history[0].release
def __str__(self):
s = specfile.Package.__str__(self)
s += _('Distribution: %s, Dist. Release: %s\n') % \
(self.distribution, self.distributionRelease)
s += _('Architecture: %s, Installed Size: %s') % \
(self.architecture, self.installedSize)
return s
class MetaData(xmlfile.XmlFile):
"""Package metadata. Metadata is composed of Specfile and various
other information. A metadata has two parts, Source and Package."""
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Source = [ Source, autoxml.mandatory]
t_Package = [ Package, autoxml.mandatory]
#t_History = [ [Update], autoxml.mandatory]
def from_spec(self, src, pkg, history):
# this just copies fields, it doesn't fix every necessary field
self.source.name = src.name
self.source.homepage = src.homepage
self.source.packager = src.packager
self.package.source = self.source # FIXME: I know that replication sucks here, but this is the easiest for now-- exa
self.package.name = pkg.name
self.package.summary = pkg.summary
self.package.description = pkg.description
self.package.icon = pkg.icon
# merge pkg.isA with src.isA
pkg.isA.extend(src.isA)
self.package.isA = pkg.isA
self.package.partOf = pkg.partOf
self.package.license = pkg.license
self.package.packageDependencies = pkg.packageDependencies
self.package.componentDependencies = pkg.componentDependencies
self.package.files = pkg.files
# FIXME: no need to copy full history with comments
self.package.history = history
self.package.conflicts = pkg.conflicts
self.package.providesComar = pkg.providesComar
#self.package.requiresComar = pkg.requiresComar
self.package.additionalFiles = pkg.additionalFiles
# FIXME: right way to do it?
self.source.version = src.version
self.source.release = src.release
self.package.version = src.version
self.package.release = src.release
-47
View File
@@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
import os.path
import pisi.context as ctx
from pisi import Error
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
class Mirrors:
def __init__(self, config=ctx.const.mirrors_conf):
self.mirrors = {}
self._parse(config)
def get_mirrors(self, name):
if self.mirrors.has_key(name):
return list(self.mirrors[name])
return None
def _add_mirror(self, name, url):
if self.mirrors.has_key(name):
self.mirrors[name].append(url)
else:
self.mirrors[name] = [url]
def _parse(self, config):
if os.path.exists(config):
for line in open(config, "r").readlines():
if not line.startswith('#') and not line == '\n':
mirror = line.strip().split()
if len(mirror) == 2:
(name, url) = mirror
self._add_mirror(name, url)
else:
raise Error(_('Mirrors file %s does not exist. Could not resolve mirrors://') % config)
-39
View File
@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
#
# Guido's cool metaclass examples. fair use. ahahah.
# I find these quite handy. Use them :)
class autoprop(type):
def __init__(cls, name, bases, dict):
super(autoprop, cls).__init__(name, bases, dict)
props = {}
for name in dict.keys():
if name.startswith("_get_") or name.startswith("_set_"):
props[name[5:]] = 1
for name in props.keys():
fget = getattr(cls, "_get_%s" % name, None)
fset = getattr(cls, "_set_%s" % name, None)
setattr(cls, name, property(fget, fset))
class autosuper(type):
def __init__(cls, name, bases, dict):
super(autosuper, cls).__init__(name, bases, dict)
setattr(cls, "_%s__super" % name, super(cls))
class autosuprop(autosuper, autoprop):
pass
class autoeq(type):
"useful for structures"
def __init__(cls, name, bases, dict):
super(autoeq, cls).__init__(name, bases, dict)
def equal(self, other):
return self.__dict__ == other.__dict__
cls.__eq__ = equal
class Struct:
__metaclass__ = autoeq
def __init__(self, **entries):
self.__dict__.update(entries)
-839
View File
@@ -1,839 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Package Operations: install/remove/upgrade"""
import os
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.uri import URI
import pisi.util as util
import pisi.dependency as dependency
import pisi.pgraph as pgraph
import pisi.packagedb as packagedb
import pisi.repodb
import pisi.installdb
from pisi.index import Index
import pisi.cli
import pisi.atomicoperations as atomicoperations
import pisi.ui as ui
from pisi.version import Version
class Error(pisi.Error):
pass
def upgrade_pisi():
"""forces to reload pisi modules and runs rebuild-db if needed."""
old_filesdbversion = pisi.__filesdbversion__
old_dbversion = pisi.__dbversion__
def rebuild_db():
"""rebuild_db is necessary if database structures has changed."""
from pisi.version import Version
import pisi.context as ctx
if Version(pisi.__filesdbversion__) > Version(old_filesdbversion) or \
Version(pisi.__dbversion__) > Version(old_dbversion):
pisi.api.init(database=False)
ctx.ui.info(_("* PiSi database version has changed. Rebuilding database..."))
pisi.api.rebuild_db()
ctx.ui.info(_("* Database rebuild operation is completed succesfully."))
pisi.api.finalize()
def reload_pisi():
for module in sys.modules.keys():
if module.startswith("pisi."):
"""removal from sys.modules forces reload via import"""
del(sys.modules[module])
reload(pisi)
pisi.api.finalize()
reload_pisi()
rebuild_db()
pisi.api.init()
# high level operations
def install(packages, reinstall = False):
"""install a list of packages (either files/urls, or names)"""
# FIXME: this function name "install" makes impossible to import
# and use install module directly.
from pisi.atomicoperations import Error as InstallError
# determine if this is a list of files/urls or names
if packages[0].endswith(ctx.const.package_suffix): # they all have to!
return install_pkg_files(packages)
else:
return install_pkg_names(packages, reinstall)
def install_pkg_files(package_URIs):
"""install a number of pisi package files"""
from package import Package
ctx.ui.debug('A = %s' % str(package_URIs))
for x in package_URIs:
if not x.endswith(ctx.const.package_suffix):
raise Error(_('Mixing file names and package names not supported yet.'))
if ctx.config.get_option('ignore_dependency'):
# simple code path then
for x in package_URIs:
atomicoperations.install_single_file(x)
return # short circuit
# read the package information into memory first
# regardless of which distribution they come from
d_t = {}
dfn = {}
for x in package_URIs:
package = Package(x)
package.read()
name = str(package.metadata.package.name)
d_t[name] = package.metadata.package
dfn[name] = x
def satisfiesDep(dep):
# is dependency satisfied among available packages
# or packages to be installed?
return dependency.installed_satisfies_dep(dep) \
or dependency.dict_satisfies_dep(d_t, dep)
# for this case, we have to determine the dependencies
# that aren't already satisfied and try to install them
# from the repository
dep_unsatis = []
for name in d_t.keys():
pkg = d_t[name]
deps = pkg.runtimeDependencies()
for dep in deps:
if not satisfiesDep(dep):
dep_unsatis.append(dep)
# now determine if these unsatisfied dependencies could
# be satisfied by installing packages from the repo
# if so, then invoke install_pkg_names
extra_packages = [x.package for x in dep_unsatis]
if extra_packages:
ctx.ui.info(_("""The following packages will be installed
in the respective order to satisfy extra dependencies:
""") + util.strlist(extra_packages))
if not ctx.ui.confirm(_('Do you want to continue?')):
raise Error(_('External dependencies not satisfied'))
install_pkg_names(extra_packages)
class PackageDB:
def get_package(self, key, repo = None):
return d_t[str(key)]
packagedb = PackageDB()
A = d_t.keys()
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
if dependency.dict_satisfies_dep(d_t, dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
if not ctx.get_option('ignore_package_conflicts'):
check_conflicts(order, packagedb)
order.reverse()
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
atomicoperations.install_single_file(dfn[x])
pisi_installed = ctx.installdb.is_installed('pisi')
if 'pisi' in order and pisi_installed:
upgrade_pisi()
def check_conflict(pkg):
conflicts = []
for c in pkg.conflicts:
if ctx.installdb.is_installed(c):
conflicts.append(c)
return conflicts
def calculate_conflicts(order, packagedb):
B_0 = set(order)
C = D = set()
pkg_conflicts = {}
for x in order:
pkg = packagedb.get_package(x)
B_p = set(check_conflict(pkg))
if B_p:
pkg_conflicts[x] = B_p
C = C.union(B_p)
B_i = B_0.intersection(set(pkg.conflicts))
# check if there are any conflicts within the packages that are
# going to be installed
if B_i:
D = D.union(B_i)
D.add(pkg.name)
return (C, D, pkg_conflicts)
def check_conflicts(order, packagedb):
"""check if upgrading to the latest versions will cause havoc
done in a simple minded way without regard for dependencies of
conflicts, etc."""
(C, D, pkg_conflicts) = calculate_conflicts(order, packagedb)
if D:
raise Error(_("Selected packages [%s] are in conflict with each other.") %
util.strlist(list(D)))
if pkg_conflicts:
conflicts = ""
for pkg in pkg_conflicts.keys():
conflicts += _("[%s conflicts with: %s]\n") % (pkg, util.strlist(pkg_conflicts[pkg]))
ctx.ui.info(_("The following packages have conflicts:\n%s") %
conflicts)
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
raise Error(_("Conflicts remain"))
if remove(list(C), ignore_dep=True, ignore_safety=True) == False:
raise Error(_("Conflicts remain"))
def expand_components(A):
Ap = set()
for x in A:
if ctx.componentdb.has_component(x):
Ap = Ap.union(ctx.componentdb.get_union_comp(x).packages)
else:
Ap.add(x)
return Ap
def is_upgradable(name, ignore_build = False):
if not ctx.installdb.is_installed(name):
return False
(version, release, build) = ctx.installdb.get_version(name)
try:
pkg = ctx.packagedb.get_package(name)
except:
return False
if ignore_build or (not build) or (not pkg.build):
return Version(release) < Version(pkg.release)
else:
return build < pkg.build
def upgrade_base(A = set()):
ignore_build = ctx.get_option('ignore_build_no')
if not ctx.get_option('ignore_safety'):
if ctx.componentdb.has_component('system.base'):
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
extra_installs = filter(lambda x: not ctx.installdb.is_installed(x), systembase - A)
if extra_installs:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
util.strlist(extra_installs))
G_f, install_order = plan_install_pkg_names(extra_installs)
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(install_order))
upgrade_order = []
if extra_upgrades:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
util.strlist(extra_upgrades))
G_f, upgrade_order = plan_upgrade(extra_upgrades, ignore_build)
# return packages that must be added to any installation
return set(install_order + upgrade_order)
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
return set()
def install_pkg_names(A, reinstall = False):
"""This is the real thing. It installs packages from
the repository, trying to perform a minimum number of
installs"""
A = [str(x) for x in A] #FIXME: why do we still get unicode input here? :/ -- exa
# A was a list, remove duplicates and expand components
A_0 = A = expand_components(set(A))
ctx.ui.debug('A = %s' % str(A))
# filter packages that are already installed
if not reinstall:
Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
d = A - Ap
if len(d) > 0:
ctx.ui.warning(_("The following package(s) are already installed and are not going to be installed again:\n") +
util.strlist(d))
A = Ap
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
A |= upgrade_base(A)
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_install_pkg_names(A)
else:
G_f = None
order = list(A)
if len(order) > 1:
ctx.ui.info(_("Following packages will be installed in the respective "
"order to satisfy dependencies:\n") + util.strlist(order))
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - A_0:
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order)
pisi_installed = ctx.installdb.is_installed('pisi')
for x in order:
atomicoperations.install_single_name(x, True) # allow reinstalls here
if 'pisi' in order and pisi_installed:
upgrade_pisi()
def plan_install_pkg_names(A, ignore_conflicts = False):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
ctx.ui.debug('checking %s' % str(dep))
# we don't deal with already *satisfied* dependencies
if not dependency.installed_satisfies_dep(dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
if not ctx.get_option('ignore_package_conflicts') and not ignore_conflicts:
check_conflicts(order, ctx.packagedb)
return G_f, order
def upgrade(A):
upgrade_pkg_names(A)
def upgrade_pkg_names(A = []):
"""Re-installs packages from the repository, trying to perform
a minimum or maximum number of upgrades according to options."""
ignore_build = ctx.get_option('ignore_build_no')
security_only = ctx.get_option('security_only')
if not A:
# if A is empty, then upgrade all packages
A = ctx.installdb.list_installed()
A_0 = A = expand_components(set(A))
Ap = []
for x in A:
if x.endswith(ctx.const.package_suffix):
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
if not ctx.installdb.is_installed(x):
ctx.ui.info(_('Package %s is not installed.') % x, True)
continue
(version, release, build) = ctx.installdb.get_version(x)
if ctx.packagedb.has_package(x):
pkg = ctx.packagedb.get_package(x)
else:
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
continue
if security_only:
updates = [i for i in pkg.history if Version(i.release) > Version(release)]
if not pisi.util.any(lambda i:i.type == 'security', updates):
continue
if ignore_build or (not build) or (not pkg.build):
if Version(release) < Version(pkg.release):
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest release %s.')
% (pkg.name, pkg.release), True)
else:
if build < pkg.build:
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest build %s.')
% (pkg.name, pkg.build), True)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to upgrade.'))
return True
A |= upgrade_base(A)
ctx.ui.debug('A = %s' % str(A))
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_upgrade(A, ignore_build)
else:
G_f = None
order = list(A)
ctx.ui.info(_('The following packages will be upgraded: ') +
util.strlist(order))
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - A_0:
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order)
paths = []
for x in order:
install_op = atomicoperations.Install.from_name(x)
paths.append(install_op.package_fname)
# fetch to be upgraded packages but do not install them.
if ctx.get_option('fetch_only'):
return
for path in paths:
install_op = atomicoperations.Install(path, ignore_file_conflicts = True)
install_op.install(True)
if 'pisi' in order:
upgrade_pisi()
def plan_upgrade(A, ignore_build = False, ignore_conflicts = False):
# try to construct a pisi graph of packages to
# install / reinstall
packagedb = ctx.packagedb
G_f = pgraph.PGraph(packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
# TODO: conflicts
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
# add packages that can be upgraded
if dependency.repo_satisfies_dep(dep):
if ctx.installdb.is_installed(dep.package):
if ctx.get_option('eager'):
if not is_upgradable(dep.package):
continue
else:
if dependency.installed_satisfies_dep(dep):
continue
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
else:
ctx.ui.error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
raise Error(_("Upgrade is not possible."))
B = Bp
# now, search reverse dependencies to see if anything
# should be upgraded
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
rev_deps = packagedb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
if ctx.get_option('eager'):
# add all upgradable reverse deps
if is_upgradable(rev_dep):
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
else:
# add only installed but unsatisfied reverse dependencies
if ctx.installdb.is_installed(rev_dep) and \
(not dependency.installed_satisfies_dep(depinfo)):
if not dependency.repo_satisfies_dep(depinfo):
raise Error(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
if not ctx.get_option('ignore_package_conflicts') and not ignore_conflicts:
check_conflicts(order, ctx.packagedb)
return G_f, order
def remove(A, ignore_dep = False, ignore_safety = False):
"""remove set A of packages from system (A is a list of package names)"""
A = [str(x) for x in A]
# filter packages that are not installed
A_0 = A = expand_components(set(A))
if not ctx.get_option('ignore_safety') and not ignore_safety:
if ctx.componentdb.has_component('system.base'):
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
refused = A.intersection(systembase)
if refused:
ctx.ui.warning(_('Safety switch: cannot remove the following packages in system.base: ') +
util.strlist(refused))
A = A - systembase
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
Ap = []
for x in A:
if ctx.installdb.is_installed(x):
Ap.append(x)
else:
ctx.ui.info(_('Package %s does not exist. Cannot remove.') % x)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to remove.'))
return False
if not ctx.config.get_option('ignore_dependency') and not ignore_dep:
G_f, order = plan_remove(A)
else:
G_f = None
order = A
ctx.ui.info(_("""The following minimal list of packages will be removed
in the respective order to satisfy dependencies:
""") + util.strlist(order))
if len(order) > len(A_0):
if not ctx.ui.confirm(_('Do you want to continue?')):
ctx.ui.warning(_('Package removal declined'))
return False
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
if ctx.installdb.is_installed(x):
atomicoperations.remove_single(x)
else:
ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x)
def plan_remove(A):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb, pisi.itembyrepodb.installed) # construct G_f
# find the (install closure) graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x, pisi.itembyrepodb.installed)
rev_deps = ctx.packagedb.get_rev_deps(x, pisi.itembyrepodb.installed)
for (rev_dep, depinfo) in rev_deps:
# we don't deal with uninstalled rev deps
# and unsatisfied dependencies (this is important, too)
if ctx.packagedb.has_package(rev_dep, pisi.itembyrepodb.installed) and \
dependency.installed_satisfies_dep(depinfo):
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
return G_f, order
def expand_src_components(A):
Ap = set()
for x in A:
if ctx.componentdb.has_component(x):
Ap = Ap.union(ctx.componentdb.get_union_comp(x).sources)
else:
Ap.add(x)
return Ap
##def build_names(A, rebuild = true):
##
## # A was a list, remove duplicates and expand components
## A_0 = A = expand_src_components(set(A))
## ctx.ui.debug('A = %s' % str(A))
##
## # filter packages that are already installed
## if not rebuild:
## Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
## d = A - Ap
## if len(d) > 0:
## ctx.ui.warning(_('Not re-building the following packages: ') +
## util.strlist(d))
## A = Ap
##
## if len(A)==0:
## ctx.ui.info(_('No packages to build.'))
## return
##
## if not ctx.config.get_option('ignore_dependency'):
## G_f, order_inst = plan_build_pkg_names(A)
## else:
## G_f = None
## order_inst = []
##
## ctx.ui.info(_("""The following minimal list of packages will be installed
##in the respective order to satisfy dependencies:
##""") + util.strlist(order))
##
## if ctx.get_option('dry_run'):
## return
##
## if len(order) > len(A_0):
## if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
## return False
##
## ctx.ui.notify(ui.packagestogo, order = order)
##
## for x in order:
## atomicoperations.install_single_name(x)
##
##def plan_build_names(A):
## # try to construct a pisi graph of packages to
## # install / reinstall
##
## G_f = pgraph.PGraph(packagedb) # construct G_f
##
## # find the "install closure" graph of G_f by package
## # set A using packagedb
## for x in A:
## G_f.add_package(x)
## B = A
##
## while len(B) > 0:
## Bp = set()
## for x in B:
## pkg = packagedb.get_package(x)
## for dep in pkg.runtimeDependencies():
## ctx.ui.debug('checking %s' % str(dep))
## # we don't deal with already *satisfied* dependencies
## if not dependency.installed_satisfies_dep(dep):
## if not dep.package in G_f.vertices():
## Bp.add(str(dep.package))
## G_f.add_dep(x, dep)
## B = Bp
## if ctx.config.get_option('debug'):
## G_f.write_graphviz(sys.stdout)
## order = G_f.topological_sort()
## order.reverse()
## check_conflicts(order)
## return G_f, order
def emerge(A, rebuild_all = False):
# A was a list, remove duplicates and expand components
A = [str(x) for x in A]
A_0 = A = expand_src_components(set(A))
ctx.ui.debug('A = %s' % str(A))
if len(A)==0:
ctx.ui.info(_('No packages to emerge.'))
return
#A |= upgrade_base(A)
# FIXME: Errr... order_build changes type conditionally and this
# is not good. - baris
if not ctx.config.get_option('ignore_dependency'):
G_f, order_inst, order_build = plan_emerge(A, rebuild_all)
else:
G_f = None
order_inst = []
order_build = A
if order_inst:
ctx.ui.info(_("""The following minimal list of packages will be installed
from repository in the respective order to satisfy dependencies:
""") + util.strlist(order_inst))
ctx.ui.info(_("""The following minimal list of packages will be built and
installed in the respective order to satisfy dependencies:
""") + util.strlist(order_build))
if ctx.get_option('dry_run'):
return
if len(order_inst) + len(order_build) > len(A_0):
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order_inst)
pisi_installed = ctx.installdb.is_installed('pisi')
for x in order_inst:
atomicoperations.install_single_name(x)
#ctx.ui.notify(ui.packagestogo, order = order_build)
for x in order_build:
package_names = atomicoperations.build(x)[0]
install_pkg_files(package_names) # handle inter-package deps here
# FIXME: take a look at the fixme above :(, we have to be sure
# that order_build is a known type...
U = set(order_build)
U.update(order_inst)
if 'pisi' in order_build or (('pisi' in U) and pisi_installed):
upgrade_pisi()
def plan_emerge(A, rebuild_all):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pisi.graph.Digraph()
def get_spec(name):
if ctx.sourcedb.has_spec(name):
return ctx.sourcedb.get_spec(name)
else:
raise Error(_('Cannot find source package: %s') % name)
def get_src(name):
return get_spec(name).source
def add_src(src):
if not str(src.name) in G_f.vertices():
G_f.add_vertex(str(src.name), (src.version, src.release))
def pkgtosrc(pkg):
return ctx.sourcedb.pkgtosrc(pkg)
# setup first
#specfiles = [ ctx.sourcedb.get_source(x)[1] for x in A ]
#pkgtosrc = {}
B = A
install_list = set()
while len(B) > 0:
Bp = set()
for x in B:
sf = get_spec(x)
src = sf.source
add_src(src)
# add dependencies
def process_dep(dep):
if not dependency.installed_satisfies_dep(dep):
if dependency.repo_satisfies_dep(dep):
install_list.add(dep.package)
return
srcdep = pkgtosrc(dep.package)
if not srcdep in G_f.vertices():
Bp.add(srcdep)
add_src(get_src(srcdep))
if not src.name == srcdep: # firefox - firefox-devel thing
G_f.add_edge(src.name, srcdep)
for builddep in src.buildDependencies:
process_dep(builddep)
for pkg in sf.packages:
for rtdep in pkg.packageDependencies:
process_dep(rtdep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order_build = G_f.topological_sort()
order_build.reverse()
G_f2, order_inst = plan_install_pkg_names(install_list)
return G_f, order_inst, order_build
-147
View File
@@ -1,147 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""package abstraction methods to add/remove files, extract control files"""
from os.path import join, exists, basename, dirname
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.archive as archive
from pisi.uri import URI
from pisi.metadata import MetaData
from pisi.files import Files
import pisi.util as util
class Error(pisi.Error):
pass
class Package:
"""PiSi Package Class provides access to a pisi package (.pisi
file)."""
def __init__(self, packagefn, mode='r'):
self.filepath = packagefn
url = URI(packagefn)
if url.is_remote_file():
self.fetch_remote_file(url)
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
def fetch_remote_file(self, url):
from fetcher import fetch_url
dest = ctx.config.packages_dir()
self.filepath = join(dest, url.filename())
#FIXME: also check sha1sum
if not exists(self.filepath):
try:
fetch_url(url, dest, ctx.ui.Progress)
except pisi.fetcher.FetchError:
# Bug 3465
if ctx.get_option('reinstall'):
raise Error(_("There was a problem while fetching '%s'.\nThe package "
"may have been upgraded. Please try to upgrade the package.") % url);
raise
else:
ctx.ui.info(_('%s [cached]') % url.filename())
def add_to_package(self, fn, an=None):
"""Add a file or directory to package"""
self.impl.add_to_archive(fn, an)
def close(self):
"""Close the package archive"""
self.impl.close()
def extract(self, outdir):
"""Extract entire package contents to directory"""
self.extract_dir('', outdir) # means package root
def extract_files(self, paths, outdir):
"""Extract paths to outdir"""
self.impl.unpack_files(paths, outdir)
def extract_file(self, path, outdir):
"""Extract file with path to outdir"""
self.extract_files([path], outdir)
def extract_dir(self, dir, outdir):
"""Extract directory recursively, this function
copies the directory archiveroot/dir to outdir"""
self.impl.unpack_dir(dir, outdir)
def extract_install(self, outdir):
if self.impl.has_file(ctx.const.install_tar_lzma):
self.extract_file(ctx.const.install_tar_lzma, ctx.config.tmp_dir())
tar = archive.ArchiveTar(join(ctx.config.tmp_dir(), ctx.const.install_tar_lzma), 'tarlzma', False)
tar.unpack_dir(outdir)
else:
self.extract_dir_flat('install', outdir)
def extract_dir_flat(self, dir, outdir):
"""Extract directory recursively, this function
unpacks the *contents* of directory archiveroot/dir inside outdir
this is the function used by the installer"""
self.impl.unpack_dir_flat(dir, outdir)
def extract_pisi_files(self, outdir):
"""Extract PiSi control files: metadata.xml, files.xml,
action scripts, etc."""
self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
self.extract_dir('config', outdir)
def get_metadata(self):
"""reads metadata.xml from the PiSi package and returns MetaData object"""
md = MetaData()
md.parse(self.impl.read_file(ctx.const.metadata_xml))
return md
def get_files(self):
"""reads files.xml from the PiSi package and returns Files object"""
files = Files()
files.parse(self.impl.read_file(ctx.const.files_xml))
return files
def read(self, outdir = None):
if not outdir:
outdir = ctx.config.tmp_dir()
# extract control files
self.extract_pisi_files(outdir)
self.metadata = MetaData()
self.metadata.read( join(outdir, ctx.const.metadata_xml) )
errs = self.metadata.errors()
if errs:
util.Checks.print_errors(errs)
raise Error, _("MetaData format wrong")
self.files = Files()
self.files.read( join(outdir, ctx.const.files_xml) )
if self.files.errors():
raise Error, _("Invalid %s") % ctx.const.files_xml
def pkg_dir(self):
packageDir = self.metadata.package.name + '-' \
+ self.metadata.package.version + '-' \
+ self.metadata.package.release
return join( ctx.config.lib_dir(), 'package', packageDir)
def comar_dir(self):
return join(self.pkg_dir(), ctx.const.comar_dir)
-172
View File
@@ -1,172 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""package database
interface for update/query to local package repository
we basically store everything in PackageInfo class
yes, we are cheap
"""
import os
import fcntl
import types
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
from pisi.itembyrepodb import ItemByRepoDB
import pisi.itembyrepodb as itembyrepodb
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
def __init__(self, pkg):
pisi.Error.__init__("Package %s not found" % pkg)
self.pkg = pkg
class PackageDB(object):
"""PackageDB class provides an interface to the package database
using shelf objects"""
def __init__(self):
self.d = ItemByRepoDB('package')
self.dr = ItemByRepoDB('revdep')
def close(self):
self.d.close()
self.dr.close()
def destroy(self):
self.d.destroy()
self.dr.destroy()
def has_package(self, name, repo=None, txn = None):
return self.d.has_key(name, repo, txn=txn)
def get_package(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Package %s not found') % name)
def get_package_repo(self, name, repo=None, txn = None):
return self.d.get_item_repo(name, repo, txn=txn)
def which_repo(self, name, txn = None):
return self.d.which_repo(name, txn=txn)
def get_rev_deps(self, name, repo = None, txn = None):
if self.dr.has_key(name, repo, txn=txn):
return self.dr.get_item(name, repo, txn=txn)
else:
return []
def get_deps(self, name, repo = None, txn = None):
if self.d.has_key(name, repo, txn=txn):
pinfo = self.d.get_item(name, repo, txn=txn)
return pinfo.packageDependencies
else:
return []
def list_packages(self, repo=None):
return self.d.list(repo)
def add_package(self, package_info, repo, txn = None):
name = str(package_info.name)
def proc(txn):
self.d.add_item(name, package_info, repo, txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, repo, txn):
revdep = self.dr.get_item(dep_name, repo, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
revdep.append( (name, dep) )
self.dr.add_item(dep_name, revdep, repo, txn)
else:
self.dr.add_item(dep_name, [ (name, dep) ], repo, txn)
# add component
ctx.componentdb.add_package(package_info.partOf, package_info.name, repo, txn)
# index summary and description
for (lang, doc) in package_info.summary.iteritems():
if lang in ['en', 'tr']:
pisi.search.add_doc('summary', lang, package_info.name, doc, repo=repo, txn=txn)
for (lang, doc) in package_info.description.iteritems():
if lang in ['en', 'tr']:
pisi.search.add_doc('description', lang, package_info.name, doc, repo=repo, txn=txn)
ctx.txn_proc(proc, txn)
def clear(self, txn = None):
self.d.clear()
self.dr.clear()
def remove_package(self, name, repo = None, txn = None):
name = str(name)
def proc(txn):
package_info = self.d.get_item(name, repo, txn=txn)
self.d.remove_item(name, repo, txn=txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, repo, txn):
revdep = self.dr.get_item(dep_name, repo, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
if revdep:
self.dr.add_item(dep_name, revdep, repo, txn)
else:
# Bug 3558: removal of revdep list of a package from revdepdb
# should only be done by the list members (dep. packages), not
# the package itself. So if a package is removed, it is removed
# from packagedb but its revdepdb part may still exist, until
# all the list members are removed.
self.dr.remove_item(dep_name, repo, txn=txn)
ctx.componentdb.remove_package(package_info.partOf, package_info.name, repo, txn)
for (lang, doc) in package_info.summary.iteritems():
if lang in ['en', 'tr']:
pisi.search.remove_doc('summary', lang, package_info.name, doc, repo=repo, txn=txn)
for (lang, doc) in package_info.description.iteritems():
if lang in ['en', 'tr']:
pisi.search.remove_doc('description', lang, package_info.name, doc, repo=repo, txn=txn)
self.d.txn_proc(proc, txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dr.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
pkgdb = None
def remove_tracking_package(name, txn = None):
# remove the guy from the tracking databases
if pkgdb.has_package(name, itembyrepodb.installed, txn=txn):
pkgdb.remove_package(name, itembyrepodb.installed, txn=txn)
if pkgdb.has_package(name, itembyrepodb.thirdparty, txn=txn):
pkgdb.remove_package(name, itembyrepodb.thirdparty, txn=txn)
def init_db():
global pkgdb
pkgdb = PackageDB()
return pkgdb
def finalize_db():
global pkgdb
if pkgdb:
pkgdb.close()
-84
View File
@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""PiSi package relation graph that represents the state of packagedb"""
import string
from graph import *
# Cache the results from packagedb queries in a graph
class PGraph(Digraph):
def __init__(self, packagedb, repo = pisi.itembyrepodb.repos):
super(PGraph, self).__init__()
self.packagedb = packagedb
self.repo = repo
def add_package(self, pkg):
pkg1 = self.packagedb.get_package(pkg, self.repo)
self.add_vertex(str(pkg), (pkg1.version, pkg1.release))
def add_plain_dep(self, pkg1name, pkg2name):
pkg1data = None
if not pkg1name in self.vertices():
pkg1 = self.packagedb.get_package(pkg1name, self.repo)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg2name in self.vertices():
pkg2 = self.packagedb.get_package(pkg2name, self.repo)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
pkg1data, pkg2data )
def add_dep(self, pkg, depinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg, self.repo)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(depinfo.package, self.repo)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
pkg1data, pkg2data )
def add_rev_dep(self, depinfo, pkg):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(depinfo.package, self.repo)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(pkg, self.repo)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo),
pkg1data, pkg2data )
def add_conflict(self, pkg, conflinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg, self.repo)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg in self.vertices():
pkg2 = self.packagedb.get_package(conflinfo.package, self.repo)
pkg2data = (pkg2.version, pkg2.release)
self.add_biedge(str(pkg), str(conflinfo.package), ('c', conflinfo)
, pkg1data, pkg2data )
def write_graphviz_vlabel(self, f, u):
(v, r) = self.vertex_data(u)
f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]')
-23
View File
@@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import locale
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
pass
-846
View File
@@ -1,846 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
autoxml is a metaclass for automatic XML translation, using
a miniature type system. (w00t!) This is based on an excellent
high-level XML processing prototype that Gurer prepared.
Method names are mixedCase for compatibility with minidom,
an old library.
"""
# System
import locale
import codecs
import types
import formatter
import sys
from StringIO import StringIO
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi
import pisi
from pisi.pxml.xmlext import *
from pisi.pxml.xmlfile import XmlFile
import pisi.context as ctx
import pisi.util as util
import pisi.oo as oo
class Error(pisi.Error):
pass
# requirement specs
mandatory, optional = range(2) # poor man's enum
# basic types
String = types.StringType
Text = types.UnicodeType
Integer = types.IntType
Long = types.LongType
Float = types.FloatType
#class datatype(type):
# def __init__(cls, name, bases, dict):
# """entry point for metaclass code"""
# # standard initialization
# super(autoxml, cls).__init__(name, bases, dict)
class LocalText(dict):
"""Handles XML tags with localized text"""
def __init__(self, tag = "", req = optional):
self.tag = tag
self.req = req
dict.__init__(self)
def decode(self, node, errs, where = ""):
# flags, tag name, instance attribute
assert self.tag != ''
nodes = getAllNodes(node, self.tag)
if not nodes:
if self.req == mandatory:
errs.append(where + ': ' + _("At least one '%s' tag should have local text") %
self.tag )
else:
for node in nodes:
lang = getNodeAttribute(node, 'xml:lang')
c = getNodeText(node)
if not c:
errs.append(where + ': ' + _("'%s' language of tag '%s' is empty") %
(lang, self.tag))
# FIXME: check for dups and 'en'
if not lang:
lang = 'en'
self[lang] = c
def encode(self, node, errs):
assert self.tag != ''
for key in self.iterkeys():
newnode = addNode(node, self.tag)
setNodeAttribute(newnode, 'xml:lang', key)
addText(newnode, '', self[key])
#FIXME: maybe more appropriate for pisi.util
@staticmethod
def get_lang():
try:
(lang, encoding) = locale.getlocale()
if not lang:
(lang, encoding) = locale.getdefaultlocale()
if lang==None: # stupid python means it is C locale
return 'en'
else:
return lang[0:2]
except:
raise Error(_('LocalText: unable to get either current or default locale'))
def errors(self, where = unicode()):
errs = []
langs = [ LocalText.get_lang(), 'en', 'tr', ]
if self.keys() and not util.any(lambda x : self.has_key(x), langs):
errs.append( where + ': ' + _("Tag should have at least the current locale, or failing that an English or Turkish version"))
#FIXME: check if all entries are unicode
return errs
def format(self, f, errs):
L = LocalText.get_lang()
if self.has_key(L):
f.add_flowing_data(self[L])
elif self.has_key('en'):
# fallback to English, blah
f.add_flowing_data(self['en'])
elif self.has_key('tr'):
# fallback to Turkish
f.add_flowing_data(self['tr'])
else:
errs.append(_("Tag should have at least the current locale, or failing that an English or Turkish version"))
#FIXME: factor out these common routines
def print_text(self, file = sys.stdout):
w = Writer(file) # plain text
f = formatter.AbstractFormatter(w)
errs = []
self.format(f, errs)
if errs:
for x in errs:
ctx.ui.warning(x)
def __str__(self):
L = LocalText.get_lang()
if self.has_key(L):
return unicode(self[L])
elif self.has_key('en'):
# fallback to English, blah
return unicode(self['en'])
elif self.has_key('tr'):
# fallback to Turkish
return unicode(self['tr'])
else:
return unicode()
class Writer(formatter.DumbWriter):
"""adds unicode support"""
def __init__(self, file=None, maxcol=78):
formatter.DumbWriter.__init__(self, file, maxcol)
def send_literal_data(self, data):
self.file.write(data.encode("utf-8"))
i = data.rfind('\n')
if i >= 0:
self.col = 0
data = data[i+1:]
data = data.expandtabs()
self.col = self.col + len(data)
self.atbreak = 0
class autoxml(oo.autosuper, oo.autoprop):
"""High-level automatic XML transformation interface for xmlfile.
The idea is to declare a class for each XML tag. Inside the
class the tags and attributes nested in the tag are further
elaborated. A simple example follows:
class Employee:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
a_Type = [xmlfile.Integer, xmlfile.optional]
This class defines a tag and an attribute nested in Employee
class. Name is a string and type is an integer, called basic
types.
While the tag is mandatory, the attribute may be left out.
Other basic types supported are: xmlfile.Float, xmlfile.Double
and (not implemented yet): xmlfile.Binary
By default, the class name is taken as the corresponding tag,
which may be overridden by defining a tag attribute. Thus,
the same tag may also be written as:
class EmployeeXML:
...
tag = 'Employee'
...
In addition to basic types, we allow for two kinds of complex
types: class types and list types.
A declared class can be nested in another class as follows
class Position:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
t_Description = [xmlfile.Text, xmlfile.optional]
which we can add to our Employee class.
class Employee:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
a_Type = [xmlfile.Integer, xmlfile.optional]
t_Position = [Position, xmlfile.mandatory]
Note some unfortunate redundancy here with Position; this is
justified by the implementation (kidding). Still, you might
want to assign a different name than the class name that
goes in there, which may be fully qualified.
There is more! Suppose we want to define a company, with
of course many employees.
class Company:
__metaclass__ = autoxml
t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee']
Logically, inside the Company/Employees tag, we will have several
Employee tags, which are inserted to the Employees instance variable of
Company in order of appearance. We can define lists of any other valid
type. Here we used a list of an autoxml class defined above.
The mandatory flag here asserts that at least one such record
is to be found.
You see, it works like magic, when it works of course. All of it
done without a single brain exploding.
"""
def __init__(cls, name, bases, dict):
"""entry point for metaclass code"""
#print 'generating class', name
# standard initialization
super(autoxml, cls).__init__(name, bases, dict)
xmlfile_support = XmlFile in bases
cls.autoxml_bases = filter(lambda base: isinstance(base, autoxml), bases)
#TODO: initialize class attribute __xml_tags
#setattr(cls, 'xml_variables', [])
# default class tag is class name
if not dict.has_key('tag'):
cls.tag = name
# generate helper routines, for each XML component
names = []
inits = []
decoders = []
encoders = []
errorss = []
formatters = []
# read declaration order from source
# code contributed by bahadir kandemir
from inspect import getsourcelines
from itertools import ifilter
import re
fn = re.compile('\s*([tas]_[a-zA-Z]+).*').findall
lines = filter(fn, getsourcelines(cls)[0])
decl_order = map(lambda x:x.split()[0], lines)
# there should be at most one str member, and it should be
# the first to process
order = filter(lambda x: not x.startswith('s_'), decl_order)
# find string member
str_members = filter(lambda x:x.startswith('s_'), decl_order)
if len(str_members)>1:
raise Error('Only one str member can be defined')
elif len(str_members)==1:
order.insert(0, str_members[0])
for var in order:
if var.startswith('t_') or var.startswith('a_') or var.startswith('s_'):
name = var[2:]
if var.startswith('a_'):
x = autoxml.gen_attr_member(cls, name)
elif var.startswith('t_'):
x = autoxml.gen_tag_member(cls, name)
elif var.startswith('s_'):
x = autoxml.gen_str_member(cls, name)
(name, init, decoder, encoder, errors, format_x) = x
names.append(name)
inits.append(init)
decoders.append(decoder)
encoders.append(encoder)
errorss.append(errors)
formatters.append(format_x)
# generate top-level helper functions
cls.initializers = inits
def initialize(self, uri = None, keepDoc = False, tmpDir = '/tmp',
**args):
if xmlfile_support:
if args.has_key('tag'):
XmlFile.__init__(self, tag = args['tag'])
else:
XmlFile.__init__(self, tag = cls.tag)
for base in cls.autoxml_bases:
base.__init__(self)
for init in inits:
init(self)
for x in args.iterkeys():
setattr(self, x, args[x])
# init hook
if hasattr(self, 'init'):
self.init(tag)
if xmlfile_support and uri:
self.read(uri, keepDoc, tmpDir)
cls.__init__ = initialize
cls.decoders = decoders
def decode(self, node, errs, where = unicode(cls.tag)):
for base in cls.autoxml_bases:
base.decode(self, node, errs, where)
for decode_member in decoders:#self.__class__.decoders:
decode_member(self, node, errs, where)
if hasattr(self, 'decode_hook'):
self.decode_hook(node, errs, where)
cls.decode = decode
cls.encoders = encoders
def encode(self, node, errs):
for base in cls.autoxml_bases:
base.encode(self, node, errs)
for encode_member in encoders:#self.__class__.encoders:
encode_member(self, node, errs)
if hasattr(self, 'encode_hook'):
self.encode_hook(node, errs)
cls.encode = encode
cls.errorss = errorss
def errors(self, where = unicode(name)):
errs = []
for base in cls.autoxml_bases:
errs.extend(base.errors(self, where))
for errors in errorss:#self.__class__.errorss:
errs.extend(errors(self, where))
if hasattr(self, 'errors_hook'):
errs.extend(self.errors_hook(where))
return errs
cls.errors = errors
def check(self):
errs = self.errors()
if errs:
errs.append(_("autoxml.check: '%s' errors") % len(errs))
raise Error(*errs)
cls.check = check
cls.formatters = formatters
def format(self, f, errs):
for base in cls.autoxml_bases:
base.format(self, f, errs)
for formatter in formatters:#self.__class__.formatters:
formatter(self, f, errs)
cls.format = format
def print_text(self, file = sys.stdout):
w = Writer(file) # plain text
f = formatter.AbstractFormatter(w)
errs = []
self.format(f, errs)
if errs:
for x in errs:
ctx.ui.warning(x)
cls.print_text = print_text
if not dict.has_key('__str__'):
def str(self):
strfile = StringIO()
self.print_text(strfile)
str = strfile.getvalue()
strfile.close()
return str
cls.__str__ = str
if not dict.has_key('__eq__'):
def equal(self, other):
# handle None
if other ==None:
return False # well, must be False at this point :)
for name in names:
try:
if getattr(self, name) != getattr(other, name):
return False
except:
return False
return True
def notequal(self, other):
return not self.__eq__(other)
cls.__eq__ = equal
cls.__ne__ = notequal
if xmlfile_support:
def parse(self, xml, keepDoc = False):
"parse XML string and decode it into a python object"
self.parsexml(xml)
errs = []
self.decode(self.rootNode(), errs)
if errs:
errs.append(_("autoxml.parse: String '%s' has errors") % xml)
raise Error(*errs)
if hasattr(self, 'read_hook'):
self.read_hook(errs)
if not keepDoc:
self.unlink() # get rid of the tree
errs = self.errors()
if errs:
errs.append(_("autoxml.parse: String '%s' has errors") % xml)
def read(self, uri, keepDoc = False, tmpDir = '/tmp',
sha1sum = False, compress = None, sign = None, copylocal = False):
"read XML file and decode it into a python object"
self.readxml(uri, tmpDir, sha1sum=sha1sum,
compress=compress, sign=sign, copylocal=copylocal)
errs = []
self.decode(self.rootNode(), errs)
if errs:
errs.append(_("autoxml.read: File '%s' has errors") % uri)
raise Error(*errs)
if hasattr(self, 'read_hook'):
self.read_hook(errs)
if not keepDoc:
self.unlink() # get rid of the tree
errs = self.errors()
if errs:
errs.append(_("autoxml.read: File '%s' has errors") % uri)
raise Error(*errs)
def write(self, uri, keepDoc = False, tmpDir = '/tmp',
sha1sum = False, compress = None, sign = None):
"encode the contents of the python object into an XML file"
errs = self.errors()
if errs:
errs.append(_("autoxml.write: object validation has failed"))
raise Error(*errs)
errs = []
self.newDocument()
self.encode(self.rootNode(), errs)
if hasattr(self, 'write_hook'):
self.write_hook(errs)
if errs:
errs.append(_("autoxml.write: File encoding '%s' has errors") % uri)
raise Error(*errs)
self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign)
if not keepDoc:
self.unlink() # get rid of the tree
cls.read = read
cls.write = write
cls.parse = parse
def gen_attr_member(cls, attr):
"""generate readers and writers for an attribute member"""
#print 'attr:', attr
spec = getattr(cls, 'a_' + attr)
tag_type = spec[0]
assert type(tag_type) == type(type)
def readtext(node, attr):
return getNodeAttribute(node, attr)
def writetext(node, attr, text):
#print 'write attr', attr, text
setNodeAttribute(node, attr, text)
anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext)
return cls.gen_named_comp(attr, spec, anonfuns)
def gen_tag_member(cls, tag):
"""generate helper funs for tag member of class"""
#print 'tag:', tag
spec = getattr(cls, 't_' + tag)
anonfuns = cls.gen_tag(tag, spec)
return cls.gen_named_comp(tag, spec, anonfuns)
def gen_tag(cls, tag, spec):
"""generate readers and writers for the tag"""
tag_type = spec[0]
if type(tag_type) is types.TypeType and \
autoxml.basic_cons_map.has_key(tag_type):
def readtext(node, tagpath):
#print 'read tag', node, tagpath
return getNodeText(node, tagpath)
def writetext(node, tagpath, text):
#print 'write tag', node, tagpath, text
addText(node, tagpath, text)
return cls.gen_anon_basic(tag, spec, readtext, writetext)
elif type(tag_type) is types.ListType:
return cls.gen_list_tag(tag, spec)
elif tag_type is LocalText:
return cls.gen_insetclass_tag(tag, spec)
elif type(tag_type) is autoxml or type(tag_type) is types.TypeType:
return cls.gen_class_tag(tag, spec)
else:
raise Error(_('gen_tag: unrecognized tag type %s in spec') %
str(tag_type))
def gen_str_member(cls, token):
"""generate readers and writers for a string member"""
spec = getattr(cls, 's_' + token)
tag_type = spec[0]
assert type(tag_type) == type(type)
def readtext(node, blah):
#node.normalize() # piksemel doesn't have this
return getNodeText(node)
def writetext(node, blah, text):
addText(node, "", text)
anonfuns = cls.gen_anon_basic(token, spec, readtext, writetext)
return cls.gen_named_comp(token, spec, anonfuns)
def gen_named_comp(cls, token, spec, anonfuns):
"""generate a named component tag/attr. a decoration of
anonymous functions that do not bind to variable names"""
name = cls.mixed_case(token)
token_type = spec[0]
req = spec[1]
(init_a, decode_a, encode_a, errors_a, format_a) = anonfuns
def init(self):
"""initialize component"""
setattr(self, name, init_a())
def decode(self, node, errs, where):
"""decode component from DOM node"""
setattr(self, name, decode_a(node, errs, where + '.' + unicode(name)))
def encode(self, node, errs):
"""encode self inside, possibly new, DOM node using xml"""
if hasattr(self, name):
value = getattr(self, name)
else:
value = None
encode_a(node, value, errs)
def errors(self, where):
"""return errors in the object"""
errs = []
if hasattr(self, name) and getattr(self, name) != None:
value = getattr(self,name)
errs.extend(errors_a(value, where + '.' + name))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory variable %s not available') % name)
return errs
def format(self, f, errs):
if hasattr(self, name):
value = getattr(self,name)
f.add_literal_data(token + ': ')
format_a(value, f, errs)
f.add_line_break()
else:
if req == mandatory:
errs.append(_('Mandatory variable %s not available') % name)
return (name, init, decode, encode, errors, format)
def mixed_case(cls, identifier):
"""helper function to turn token name into mixed case"""
if identifier is "":
return ""
else:
if identifier[0]=='I':
lowly = 'i' # because of pythonic idiots we can't choose locale in lower
else:
lowly = identifier[0].lower()
return lowly + identifier[1:]
def tagpath_head_last(cls, tagpath):
"returns split of the tag path into last tag and the rest"
try:
lastsep = tagpath.rindex('/')
except ValueError, e:
return ('', tagpath)
return (tagpath[:lastsep], tagpath[lastsep+1:])
def parse_spec(cls, token, spec):
"""decompose member specification"""
name = cls.mixed_case(token)
token_type = spec[0]
req = spec[1]
if len(spec)>=3:
path = spec[2] # an alternative path specified
elif type(token_type) is type([]):
if type(token_type[0]) is autoxml:
# if list of class, by default nested like in most PSPEC
path = token + '/' + token_type[0].tag
else:
# if list of ordinary type, just take the name for
path = token
elif type(token_type) is autoxml:
# if a class, by default its tag
path = token_type.tag
else:
path = token # otherwise it's the same name as
# the token
return name, token_type, req, path
def gen_anon_basic(cls, token, spec, readtext, writetext):
"""Generate a tag or attribute with one of the basic
types like integer. This has got to be pretty generic
so that we can invoke it from the complex types such as Class
and List. The readtext and writetext arguments achieve
the DOM text access for this datatype."""
name, token_type, req, tagpath = cls.parse_spec(token, spec)
def initialize():
"""default value for all basic types is None"""
return None
def decode(node, errs, where):
"""decode from DOM node, the value, watching the spec"""
text = readtext(node, token)
#print 'read text ', text
if text:
try:
value = autoxml.basic_cons_map[token_type](text)
except:
value = None
errs.append(where + ': ' + _('Type mismatch: read text cannot be decoded'))
return value
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory token %s not available') % token)
return None
def encode(node, value, errs):
"""encode given value inside DOM node"""
if value:
writetext(node, token, unicode(value))
else:
if req == mandatory:
errs.append(_('Mandatory token %s not available') % token)
def errors(value, where):
errs = []
if value and not isinstance(value, token_type):
errs.append(where + ': ' + _('Type mismatch. Expected %s, got %s') %
(token_type, type(value)) )
return errs
def format(value, f, errs):
"""format value for pretty printing"""
f.add_literal_data(unicode(value))
return initialize, decode, encode, errors, format
def gen_class_tag(cls, tag, spec):
"""generate a class datatype"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
def make_object():
obj = tag_type.__new__(tag_type)
obj.__init__(tag=tag, req=req)
return obj
def init():
return make_object()
def decode(node, errs, where):
node = getNode(node, tag)
if node:
try:
obj = make_object()
obj.decode(node, errs, where)
return obj
except Error:
errs.append(where + ': '+ _('Type mismatch: DOM cannot be decoded'))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory argument not available'))
return None
def encode(node, obj, errs):
if node and obj:
try:
#FIXME: this doesn't look pretty
classnode = newNode(node, tag)
obj.encode(classnode, errs)
addNode(node, '', classnode)
except Error:
if req == mandatory:
# note: we can receive an error if obj has no content
errs.append(_('Object cannot be encoded'))
else:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
def errors(obj, where):
return obj.errors(where)
def format(obj, f, errs):
try:
obj.format(f, errs)
except Error:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
return (init, decode, encode, errors, format)
def gen_list_tag(cls, tag, spec):
"""generate a list datatype. stores comps in tag/comp_tag"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
pathcomps = path.split('/')
comp_tag = pathcomps.pop()
list_tagpath = util.makepath(pathcomps, sep='/', relative=True)
if len(tag_type) != 1:
raise Error(_('List type must contain only one element'))
x = cls.gen_tag(comp_tag, [tag_type[0], mandatory])
(init_item, decode_item, encode_item, errors_item, format_item) = x
def init():
return []
def decode(node, errs, where):
l = []
nodes = getAllNodes(node, path)
#print node, tag + '/' + comp_tag, nodes
if len(nodes)==0 and req==mandatory:
errs.append(where + ': ' + _('Mandatory list "%s" under "%s" node is empty.') % (path, node.name()))
ix = 1
for node in nodes:
dummy = newNode(node, "Dummy")
addNode(dummy, '', node)
l.append(decode_item(dummy, errs, where + unicode("[%s]" % ix)))
#l.append(decode_item(node, errs, where + unicode("[%s]" % ix)))
ix += 1
return l
def encode(node, l, errs):
if l and len(l) > 0:
for item in l:
if list_tagpath:
listnode = addNode(node, list_tagpath, branch = False)
else:
listnode = node
encode_item(listnode, item, errs)
#encode_item(node, item, errs)
else:
if req is mandatory:
errs.append(_('Mandatory list "%s" under "%s" node is empty.') % (path, node.name()))
def errors(l, where):
errs = []
ix = 1
for node in l:
errs.extend(errors_item(node, where + '[%s]' % ix))
ix += 1
return errs
def format(l, f, errs):
l.sort()
for node in l:
format_item(node, f, errs)
f.add_literal_data(' ')
return (init, decode, encode, errors, format)
def gen_insetclass_tag(cls, tag, spec):
"""generate a class datatype that is highly integrated
don't worry if that means nothing to you. this is a silly
hack to implement local text quickly. it's not the most
elegant thing in the world. it's basically a copy of
class tag"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
def make_object():
obj = tag_type.__new__(tag_type)
obj.__init__(tag=tag, req=req)
return obj
def init():
return make_object()
def decode(node, errs, where):
if node:
try:
obj = make_object()
obj.decode(node, errs, where)
return obj
except Error:
errs.append(where + ': ' + _('Type mismatch: DOM cannot be decoded'))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory argument not available'))
return None
def encode(node, obj, errs):
if node and obj:
try:
#FIXME: this doesn't look pretty
obj.encode(node, errs)
except Error:
if req == mandatory:
# note: we can receive an error if obj has no content
errs.append(_('Object cannot be encoded'))
else:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
def errors(obj, where):
return obj.errors(where)
def format(obj, f, errs):
try:
obj.format(f, errs)
except Error:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
return (init, decode, encode, errors, format)
basic_cons_map = {
types.StringType : str,
types.UnicodeType : unicode,
types.IntType : int,
types.FloatType : float,
types.LongType : long
}
-167
View File
@@ -1,167 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
xmlext is a helper module for accessing XML files using
xml.dom.minidom . It is a convenient wrapper for some
DOM functions, and provides path based get/add functions
as in KDE API.
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses piksemel
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import piksemel as iks
parse = iks.parse
newDocument = iks.newDocument
def getAllNodes(node, tagPath):
"""retrieve all nodes that match a given tag path."""
tags = tagPath.split('/')
if len(tags) == 0:
return []
nodeList = [node] # basis case
for tag in tags:
results = map(lambda x: getTagByName(x, tag), nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
return node.getAttribute(attrname)
def setNodeAttribute(node, attrname, value):
"""get named attribute from DOM node"""
return node.setAttribute(attrname, value)
def getChildElts(parent):
"""get only child elements"""
return [x for x in parent.tags()]
def getTagByName(parent, childName):
return [x for x in parent.tags(childName)]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
if not node:
return None
child = node.firstChild()
if not child:
return None
if child.type() == iks.DATA:
# in any case, strip whitespaces...
return child.data().strip()
else:
raise XmlError(_("getNodeText: Expected text node, got something else!"))
def getChildText(node_s, tagpath):
"""get the text of a child at the end of a tag path"""
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
if tagpath == "":
return node
assert type(tagpath)==str
tags = tagpath.split('/')
assert len(tags)>0
# iterative code to search for the path
for tag in tags:
currentNode = None
for child in node.tags():
if child.name() == tag:
currentNode = child
break
if not currentNode:
return None
else:
node = currentNode
return currentNode
def createTagPath(node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
if len(tags)==0:
return node
for tag in tags:
node = node.insertTag(tag)
return node
def addTagPath(node, tags, newnode=None):
"""add newnode at the end of a tag chain, smart one"""
node = createTagPath(node, tags)
if newnode: # node to add specified
node.insertNode(newnode)
return node
def addNode(node, tagpath, newnode = None, branch=True):
"""add a new node at the end of the tree and returns it
if newnode is given adds that node, too."""
assert type(tagpath)==str
tags = []
if tagpath != "":
tags = tagpath.split('/') # tag chain
else:
addTagPath(node, [], newnode)
return node #FIXME: is this correct!?!?
assert len(tags)>0 # we want a chain
# iterative code to search for the path
if branch:
rem = 1
else:
rem = 0
while len(tags) > rem:
tag = tags.pop(0)
nodeList = getTagByName(node, tag)
if len(nodeList) == 0: # couldn't find
tags.insert(0, tag) # put it back in
return addTagPath(node, tags, newnode)
else:
node = nodeList[len(nodeList)-1] # discard other matches
else:
# had only one tag..
return addTagPath(node, tags, newnode)
return node
def addText(node, tagpath, text):
node = addNode(node, tagpath)
node.insertData(text)
def newNode(node, tag):
return iks.newDocument(tag)
-95
View File
@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses piksemel
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import codecs
import exceptions
import piksemel as iks
import pisi
from pisi.file import File
from pisi.util import join_path as join
class Error(pisi.Error):
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
def newDocument(self):
"""clear DOM"""
self.doc = iks.newDocument(self.rootTag)
def unlink(self):
"""deallocate DOM structure"""
del self.doc
def rootNode(self):
"""returns root document element"""
return self.doc
def parsexml(self, xml):
"""parses xml string and returns DOM"""
try:
self.doc = iks.parseString(xml)
return self.doc
except Exception, e:
raise Error(_("String '%s' has invalid XML") % (xml))
def readxmlfile(self, file):
raise Exception("not implemented")
try:
self.doc = iks.parse(file)
return self.doc
except Exception, e:
raise Error(_("File '%s' has invalid XML") % (localpath) )
def readxml(self, uri, tmpDir='/tmp', sha1sum=False,
compress=None, sign=None, copylocal = False):
uri = File.make_uri(uri)
#try:
localpath = File.download(uri, tmpDir, sha1sum=sha1sum,
compress=compress,sign=sign, copylocal=copylocal)
#except IOError, e:
# raise Error(_("Cannot read URI %s: %s") % (uri, unicode(e)) )
try:
self.doc = iks.parse(localpath)
return self.doc
except Exception, e:
raise Error(_("File '%s' has invalid XML") % (localpath) )
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
f.write(self.doc.toPrettyString())
f.close()
def writexmlfile(self, f):
f.write(self.doc.toPrettyString())
-117
View File
@@ -1,117 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os, fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.lockeddbshelve as shelve
import pisi.context as ctx
import pisi.packagedb as packagedb
import pisi.util as util
from pisi.uri import URI
class Error(pisi.Error):
pass
class Repo:
def __init__(self, indexuri):
self.indexuri = indexuri
#class HttpRepo
#class FtpRepo
#class RemovableRepo
class RepoDB(object):
"""RepoDB maps repo ids to repository information"""
def __init__(self, txn = None):
self.d = shelve.LockedDBShelf("repo")
def proc(txn):
if not self.d.has_key("order", txn):
self.d.put("order", [], txn)
self.d.txn_proc(proc, txn)
def close(self):
self.d.close()
def repo_name(self, ix):
l = self.list()
return l[ix]
def has_repo(self, name):
name = str(name)
return self.d.has_key("repo-" + name)
def get_repo(self, name):
name = str(name)
return self.d["repo-" + name]
def add_repo(self, name, repo_info, txn = None, at = None):
"""add repository with name and repo_info at a given optional position"""
name = str(name)
assert (isinstance(repo_info,Repo))
def proc(txn):
if self.d.has_key("repo-" + name, txn):
raise Error(_('Repository %s already exists') % name)
self.d.put("repo-" + name, repo_info, txn)
order = self.d.get("order", txn)
if at == None:
order.append(name)
else:
if at<0 or at>len(order):
raise Error(_("Cannot add repository at position %s") % at)
order.insert(at, name)
self.d.put("order", order, txn)
self.d.txn_proc(proc, txn)
def list(self):
return self.d["order"]
def clear(self):
self.d.clear()
def remove_repo(self, name, txn = None):
name = str(name)
def proc(txn):
self.d.delete("repo-" + name, txn)
list = self.d.get("order", txn)
list.remove(name)
self.d.put("order", list, txn)
ctx.packagedb.remove_repo(name, txn=txn)
ctx.sourcedb.remove_repo(name, txn=txn)
ctx.componentdb.remove_repo(name, txn=txn)
self.d.txn_proc(proc, txn)
db = None
def init():
global db
if db:
return db
db = RepoDB()
return db
def finalize():
global db
if db:
db.close()
db = None
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
import pisi.context as ctx
def error(msg):
if ctx.config.get_option('ignore_action_errors'):
ctx.ui.error(msg)
else:
raise Error(msg)
@@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
class Actions:
template = """
from pisi.actionsapi import pisitools
WorkDir = "skeleton"
def install():
pisitools.dobin("skeleton.py")
pisitools.rename("/usr/bin/skeleton.py", "%s")
"""
def __init__(self, name, filepath):
self.name = name
self.filepath = filepath
def write(self):
open(self.filepath, "w").write(self.template % self.name)
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
import locale
from os.path import join
class _constant:
"Constant members implementation"
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't rebind constant: %s" % name
# Binding an attribute once to a const is available
self.__dict__[name] = value
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't unbind constant: %s" % name
# we don't have an attribute by this name
raise NameError, name
class Constants:
__c = _constant()
def __getattr__(self, attr):
return getattr(self.__c, attr)
def __setattr__(self, attr, value):
setattr(self.__c, attr, value)
def __delattr__(self, attr):
delattr(self.__c, attr)
consts = Constants()
consts.scenarios_path = "scenarios/"
consts.pisi_db = "db/"
consts.repo_name = "scenario-db"
consts.repo_path = "repo/"
consts.repo_url = consts.repo_path + "pisi-index.xml.bz2"
consts.glob_pisis = "*.pisi"
consts.pisi_suffix = ".pisi"
consts.pspec_path = "/tmp/pspec.xml"
consts.actionspy_path = "/tmp/actions.py"
consts.packager_name = "Faik Uygur"
consts.packager_email = "faik@pardus.org.tr"
consts.homepage = "http://cekirdek.uludag.org.tr/~faik/pisi"
consts.summary = "%s is a good application"
consts.description = "%s is a free software that can do anything it wants"
consts.license = ["GPL-2"]
consts.skel_sha1sum = "cc64dfa6e068fe1f6fb68a635878b1ea21acfac7"
consts.skel_type = "targz"
consts.skel_uri = "http://cekirdek.uludag.org.tr/~faik/pisi/skeleton.tar.gz"
consts.skel_bindir = "/usr/bin"
consts.skel_dirtype = "executable"
@@ -1,96 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import glob
from pisi.scenarioapi.pspec import Pspec
from pisi.scenarioapi.actions import Actions
from pisi.scenarioapi.constants import *
from pisi.scenarioapi.withops import *
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
class Package:
def __init__(self, name, deps = [], cons = [], date = "2006-18-18", ver = "1.0", partOf="None"):
self.name = name
self.dependencies = deps
self.conflicts = cons
self.version = ver
self.date = date
self.partOf = partOf
self.pspec = None
self.actions = None
self.create_package()
def create_pisi(self):
os.system("pisi build %s -O %s > /dev/null 2>&1" % (consts.pspec_path, consts.repo_path))
def create_package(self):
pspec = Pspec(self.name, consts.pspec_path)
pspec.set_source(consts.homepage, consts.summary % self.name,
consts.description % self.name, consts.license, self.partOf)
pspec.set_packager(consts.packager_name, consts.packager_email)
pspec.set_archive(consts.skel_sha1sum, consts.skel_type, consts.skel_uri)
pspec.set_package(self.dependencies, self.conflicts)
pspec.add_file_path(consts.skel_bindir, consts.skel_dirtype)
pspec.set_history(self.date, self.version)
actions = Actions(self.name, consts.actionspy_path)
self.pspec = pspec
self.actions = actions
self.pspec.write()
self.actions.write()
self.create_pisi()
def get_file_name(self):
# use glob. there may be buildnos at the end of the package name
pkg = consts.repo_path + self.name + "-" + \
self.version + "-" + self.pspec.pspec.history[0].release
found = glob.glob(pkg + consts.glob_pisis)
if not found:
raise Exception(_("No pisi package: %s* found.") % pkg)
return os.path.basename(found[0])
def version_bump(self, *args):
for with in args:
if with.types == CONFLICT and with.action == ADDED:
self.pspec.add_conflicts(with.data)
if with.types == CONFLICT and with.action == REMOVED:
self.pspec.remove_conflicts(with.data)
if with.types == DEPENDENCY and with.action == ADDED:
self.pspec.add_dependencies(with.data)
if with.types == DEPENDENCY and with.action == REMOVED:
self.pspec.remove_dependencies(with.data)
if with.types == VERSION and with.action == INIT:
self.version = with.data
self.pspec.update_history(self.date, self.version)
self.actions.name = self.name
self.actions.write()
self.create_pisi()
if __name__ == "__main__":
p = Package("w0rmux", [], [], "0.7")
p.version_bump()
@@ -1,41 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import pisi.util as util
from pisi.scenarioapi.constants import *
def pisi_upgraded(*args):
packages = util.strlist(args).rstrip()
os.system("pisi -D%s upgrade %s" % (consts.pisi_db, packages))
def pisi_info(package):
os.system("pisi -D%s info %s" % (consts.pisi_db, package))
def pisi_removed(*args):
packages = util.strlist(args).rstrip()
os.system("pisi -D%s remove %s" % (consts.pisi_db, packages))
def pisi_added_repo(name, url):
os.system("pisi -D%s add-repo -y %s %s" % (consts.pisi_db, name, url))
def pisi_updated_repo():
os.system("pisi -D%s update-repo" % consts.pisi_db)
def pisi_installed(*args):
packages = util.strlist(args).rstrip()
os.system("pisi -D%s install %s" % (consts.pisi_db, packages))
def pisi_reinstalled(package):
os.system("pisi -D%s install --reinstall %s" % (consts.pisi_db, package))
-140
View File
@@ -1,140 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
from pisi.specfile import SpecFile, Package, Update, Path
from pisi.dependency import Dependency
from pisi.pxml.autoxml import LocalText
class Pspec:
def __init__(self, pkgname, filepath):
self.pspec = SpecFile()
self.package = Package()
self.update = Update()
self.filepath = filepath
self.name = pkgname
@staticmethod
def read(filepath):
spec = SpecFile()
spec.read(filepath)
tmp = Pspec(spec.source.name, filepath)
tmp.pspec.source.packager = spec.source.packager
tmp.pspec.source.homepage = spec.source.homepage
tmp.pspec.source.archive = spec.source.archive
tmp.pspec.source.name = spec.source.name
tmp.pspec.source.license = spec.source.license
tmp.pspec.source.partOf = spec.source.partOf
tmp.pspec.source.summary = spec.source.summary
tmp.pspec.source.description = spec.source.description
for pkg in spec.packages:
p = Package()
p.name = pkg.name
p.files = pkg.files
p.conflicts = pkg.conflicts
p.packageDependencies = pkg.packageDependencies
tmp.pspec.packages.append(p)
tmp.pspec.history = spec.history
return tmp
def add_dependencies(self, dependencies):
# special case of given one dependency package
# with depedency versioning info [**kw, name]
# [{"versionFrom":"0.4.2"}, "udev"]
if type(dependencies[0]) == dict:
dep = Dependency()
(kw, dep.package) = dependencies
dep.__dict__[kw.keys()[0]] = kw.values()[0]
self.package.packageDependencies.append(dep)
return
for depname in dependencies:
dep = Dependency()
dep.package = depname
self.package.packageDependencies.append(dep)
def remove_dependencies(self, dependencies):
for depname in dependencies:
for dep in self.package.packageDependencies:
if dep.package == depname:
self.package.packageDependencies.remove(dep)
def add_conflicts(self, conflicts):
for con in conflicts:
self.package.conflicts.append(con)
def remove_conflicts(self, conflicts):
for con in conflicts:
self.package.conflicts.remove(con)
def update_history(self, date, version):
new = Update()
new.name = self.update.name
new.email = self.update.email
new.date = date
new.version = version
new.release = str(int(self.update.release) + 1)
self.update = new
self.pspec.history.append(self.update)
self.pspec.history.reverse()
self.write()
def set_source(self, homepage, summary, description, license, partOf):
self.pspec.source.name = self.name
self.pspec.source.homepage = homepage
self.pspec.source.license = license
self.pspec.source.partOf = partOf
self.pspec.source.summary = LocalText("Summary")
self.pspec.source.description = LocalText("Description")
self.pspec.source.summary["en"] = summary
self.pspec.source.description["en"] = description
def set_packager(self, name, email):
self.pspec.source.packager.name = unicode(name)
self.pspec.source.packager.email = email
self.update.name = unicode(name)
self.update.email = email
def set_archive(self, sha1sum, type, uri):
self.pspec.source.archive.sha1sum = sha1sum
self.pspec.source.archive.type = type
self.pspec.source.archive.uri = uri
def add_file_path(self, path, type):
p = Path()
p.path = path
p.fileType = type
self.pspec.packages[0].files.append(p)
def set_package(self, dependencies, conflicts):
self.package.name = self.name
self.package.conflicts = conflicts
if dependencies:
for depname in dependencies:
dep = Dependency()
dep.package = depname
self.package.packageDependencies.append(dep)
self.pspec.packages.append(self.package)
def set_history(self, date, version, comment = "No Comment", release = "1"):
self.update.date = date
self.update.version = version
self.update.comment = comment
self.update.release = release
self.pspec.history.append(self.update)
def write(self):
self.pspec.write(self.filepath)
@@ -1,73 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
from pisi.scenarioapi.package import Package
from pisi.scenarioapi.withops import *
from pisi.scenarioapi.constants import *
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
repodb = {}
def repo_added_package(package, *args):
if repodb.has_key(package):
raise Exception(_("Repo already has package named %s.") % package)
version = "1.0"
partOf = "None"
dependencies = []
conflicts = []
for with in args:
if with.types == CONFLICT and with.action == INIT:
conflicts = with.data
if with.types == DEPENDENCY and with.action == INIT:
dependencies = with.data
if with.types == VERSION and with.action == INIT:
version = with.data
if with.types == PARTOF and with.action == INIT:
partOf = with.data
repodb[package] = Package(package, dependencies, conflicts, ver=version, partOf=partOf)
def repo_removed_package(package):
if not repodb.has_key(package):
raise Exception(_("Repo does not have package named %s.") % package)
os.unlink(os.path.join(consts.repo_path, repodb[package].get_file_name()))
del repodb[package]
def repo_version_bumped(package, *args):
if not repodb.has_key(package):
raise Exception(_("Repo does not have package named %s.") % package)
old_file = repodb[package].get_file_name()
repodb[package].version_bump(*args)
os.unlink(os.path.join(consts.repo_path, old_file))
def repo_updated_index():
cur = os.getcwd()
path = os.path.join(cur, consts.repo_path)
os.chdir(consts.repo_path)
os.system("pisi index --skip-signing %s >/dev/null 2>&1" % path)
os.chdir(cur)
def repo_get_url():
return "."
@@ -1,28 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
from pisi.scenarioapi.repoops import *
from pisi.scenarioapi.pisiops import *
from pisi.scenarioapi.constants import *
def let_repo_had(package, *args):
repo_added_package(package, *args)
repo_updated_index()
def let_pisi_had(*args):
url = os.path.join(os.getcwd(), consts.repo_url)
pisi_added_repo(consts.repo_name, url)
packages = util.strlist(args).rstrip()
os.system("pisi -D%s install --ignore-dependency %s" % (consts.pisi_db, packages))
@@ -1,67 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
ADDED, REMOVED, INIT = range(3)
PARTOF, VERSION, CONFLICT, DEPENDENCY = range(4)
class with:
def __init__(self):
pass
def with_action(types, action, data):
w = with()
w.types = types
w.action = action
w.data = data
return w
def with_partof(partof):
return with_action(PARTOF, INIT, partof)
def with_version(version):
return with_action(VERSION, INIT, version)
def with_conflicts(*cons):
return with_action(CONFLICT, INIT, cons)
def with_dependencies(*deps):
return with_action(DEPENDENCY, INIT, deps)
def with_added_conflicts(*cons):
return with_action(CONFLICT, ADDED, cons)
def with_removed_conflicts(*cons):
return with_action(CONFLICT, REMOVED, cons)
def with_added_dependencies(*deps):
return with_action(DEPENDENCY, ADDED, deps)
def with_removed_dependencies(*deps):
return with_action(DEPENDENCY, REMOVED, deps)
def with_added_dependency(package, **kw):
assert len(kw) <= 1
if not len(kw):
return with_action(DEPENDENCY, ADDED, [package])
if (kw.has_key("versionFrom") or
kw.has_key("versionTo") or
kw.has_key("version") or
kw.has_key("releaseFrom") or
kw.has_key("releaseTo") or
kw.has_key("release")):
return with_action(DEPENDENCY, ADDED, [kw, package])
# pass other keywords.
return with_action(DEPENDENCY, ADDED, [package])
-62
View File
@@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
# API
from invertedindex import InvertedIndex
import preprocess as p
def init(ids, langs):
"initialize databases"
assert type(ids)==type([])
assert type(langs)==type([])
ctx.invidx = {}
for id in ids:
ctx.invidx[id] = {}
for lang in langs:
ctx.invidx[id][lang] = InvertedIndex(id, lang)
def finalize():
import pisi.context as ctx
if ctx.invidx:
for id in ctx.invidx.iterkeys():
for lang in ctx.invidx[id].iterkeys():
ctx.invidx[id][lang].close()
ctx.invidx = {}
def add_doc(id, lang, docid, str, repo = None, txn = None):
terms = p.preprocess(lang, str)
ctx.invidx[id][lang].add_doc(docid, terms, repo=repo, txn=txn)
def remove_doc(id, lang, docid, str, repo = None, txn = None):
terms = p.preprocess(lang, str)
ctx.invidx[id][lang].remove_doc(docid, terms, repo = repo, txn = txn)
def query_terms(id, lang, terms, repo = None, txn = None):
terms = map(lambda x: p.lower(lang, x), terms)
return ctx.invidx[id][lang].query(terms, repo = repo, txn = txn)
def query(id, lang, str, repo = None, txn = None):
terms = p.preprocess(lang, str)
return query_terms(id, lang, terms, repo = repo, txn = txn)
@@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import types
import pisi.lockeddbshelve as shelve
from pisi.itembyrepodb import ItemByRepoDB
import pisi.itembyrepodb as itembyrepodb
class InvertedIndex(object):
"""a database of term -> set of documents"""
def __init__(self, id, lang):
self.d = ItemByRepoDB('ii-%s-%s' % (id, lang))
def close(self):
self.d.close()
def has_term(self, term, repo = None, txn = None):
return self.d.has_key(shelve.LockedDBShelf.encodekey(term), repo=repo,txn=txn)
def get_term(self, term, repo = None, txn = None):
"""get set of doc ids given term"""
term = shelve.LockedDBShelf.encodekey(term)
def proc(txn):
if not self.has_term(term, repo=repo, txn=txn):
return set()
return self.d.get_item(term, repo=repo, txn=txn)
return self.d.txn_proc(proc, txn)
def get_union_term(self, name, txn = None, repo = itembyrepodb.repos ):
"""get a union of all repository terms, not just the first repo in order.
get only basic repo info from the first repo"""
name = shelve.LockedDBShelf.encodekey(name)
def proc(txn):
terms= set()
if self.d.d.has_key(name):
s = self.d.d.get(name, txn=txn)
for repostr in self.d.order(repo = repo):
if s.has_key(repostr):
terms |= s[repostr]
return terms
return self.d.txn_proc(proc, txn)
def query(self, terms, repo = None, txn = None):
def proc(txn):
docs = [ self.get_union_term(x, repo=repo, txn=txn) for x in terms ]
if docs:
return reduce(lambda x,y: x.intersection(y), docs)
else:
return set()
return self.d.txn_proc(proc, txn)
def list_terms(self, repo = None, txn= None):
return self.d.list(f, repo=repo, txn=txn)
def add_doc(self, doc, terms, repo = None, txn = None):
def f(txn):
for term_i in terms:
term_i = shelve.LockedDBShelf.encodekey(term_i)
term_i_docs = self.get_term(term_i, repo=repo, txn=txn)
term_i_docs.add(doc)
self.d.add_item(term_i, term_i_docs, repo=repo, txn=txn) # update
return self.d.txn_proc(f, txn)
def remove_doc(self, doc, terms,repo=None, txn=None):
def f(txn):
for term_i in terms:
term_i = shelve.LockedDBShelf.encodekey(term_i)
term_i_docs = self.get_term(term_i,repo=repo, txn=txn)
if doc in term_i_docs:
term_i_docs.remove(doc)
self.d.add_item(term_i, term_i_docs, repo=repo, txn=txn) # update
return self.d.txn_proc(f, txn)
@@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import tokenize
def lowly_python(str):
def lowly_char(c):
if c=='I':
lowly = 'i' # because of some fools we can't choose locale in lower
else:
lowly = c.lower()
return c
r = ""
for c in str:
r += lowly_char(c)
return r
def lower(lang, str):
if lang=='tr':
return lowly_python(str)
else:
return str.lower()
def preprocess(lang, str):
terms = tokenize.tokenize(lang, str)
# normalize
terms = map(lambda x: lower(lang, x), terms)
return terms
-31
View File
@@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import string
def tokenize(lang, str):
if type(str) != type(unicode()):
str = unicode(str)
tokens = []
token = unicode()
for x in str:
if x in string.whitespace or x in string.punctuation:
if len(token) > 0:
tokens.append(token)
token = unicode()
else:
token += x
if token:
tokens.append(token)
return tokens
-106
View File
@@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# python standard library
from os.path import join, basename
from os import access, R_OK
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.util as util
import pisi.context as ctx
from pisi.archive import Archive
from pisi.uri import URI
from pisi.fetcher import fetch_url
from pisi.mirrors import Mirrors
class Error(pisi.Error):
pass
class SourceArchive:
"""source archive. this is a class responsible for fetching
and unpacking a source archive"""
def __init__(self, spec, pkg_work_dir):
self.url = URI(spec.source.archive.uri)
self.pkg_work_dir = pkg_work_dir
self.archiveFile = join(ctx.config.archives_dir(), self.url.filename())
self.archive = spec.source.archive
def fetch(self, interactive=True):
if not self.is_cached(interactive):
if interactive:
self.progress = ctx.ui.Progress
else:
self.progress = None
try:
if self.url.get_uri().startswith("mirrors://"):
self.fetch_from_mirror()
else:
fetch_url(self.url, ctx.config.archives_dir(), self.progress)
except pisi.fetcher.FetchError:
if ctx.config.values.build.fallback:
self.fetch_from_fallback()
else:
raise
def fetch_from_fallback(self):
archive = basename(self.url.get_uri())
src = join(ctx.config.values.build.fallback, archive)
ctx.ui.warning(_('Trying fallback address: %s') % src)
fetch_url(src, ctx.config.archives_dir(), self.progress)
def fetch_from_mirror(self):
uri = self.url.get_uri()
sep = uri[len("mirrors://"):].split("/")
name = sep.pop(0)
archive = "/".join(sep)
mirrors = Mirrors().get_mirrors(name)
if not mirrors:
raise Error(_("%s mirrors are not defined.") % name)
for mirror in mirrors:
try:
url = join(mirror, archive)
ctx.ui.warning(_('Fetching source from mirror: %s') % url)
fetch_url(url, ctx.config.archives_dir(), self.progress)
return
except pisi.fetcher.FetchError:
pass
raise pisi.fetcher.FetchError(_('Could not fetch source from %s mirrors.') % name);
def is_cached(self, interactive=True):
if not access(self.archiveFile, R_OK):
return False
# check hash
if util.check_file_hash(self.archiveFile, self.archive.sha1sum):
if interactive:
ctx.ui.info(_('%s [cached]') % self.archive.name)
return True
return False
def unpack(self, clean_dir=True):
# check archive file's integrity
if not util.check_file_hash(self.archiveFile, self.archive.sha1sum):
raise Error, _("unpack: check_file_hash failed")
archive = Archive(self.archiveFile, self.archive.type)
archive.unpack(self.pkg_work_dir, clean_dir)
-112
View File
@@ -1,112 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
package source database
interface for update/query to local package repository
we basically store everything in sourceinfo class
yes, we are cheap
to handle multiple repositories, for sources, we
store a set of repositories in which the source appears.
the actual guy to take is determined from the repo order.
"""
import os
import fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
import pisi.repodb
from pisi.itembyrepodb import ItemByRepoDB
class NotfoundError(pisi.Error):
pass
class SourceDB(object):
def __init__(self):
self.d = ItemByRepoDB('source')
self.dpkgtosrc = ItemByRepoDB('pkgtosrc')
def close(self):
self.d.close()
self.dpkgtosrc.close()
def list(self):
return self.d.list()
def has_spec(self, name, repo=None, txn=None):
return self.d.has_key(name, repo, txn)
def get_spec(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn)
except pisi.itembyrepodb.NotfoundError, e:
raise NotfoundError(_("Source package %s not found") % name)
def get_spec_repo(self, name, repo=None, txn = None):
try:
return self.d.get_item_repo(name, repo, txn)
except pisi.itembyrepodb.NotfoundError, e:
raise NotfoundError(_("Source package %s not found") % name)
def pkgtosrc(self, name, txn = None):
return self.dpkgtosrc.get_item(name, txn=txn)
def add_spec(self, spec, repo, txn = None):
assert not spec.errors()
name = str(spec.source.name)
def proc(txn):
self.d.add_item(name, spec, repo, txn)
for pkg in spec.packages:
self.dpkgtosrc.add_item(pkg.name, name, repo, txn)
ctx.componentdb.add_spec(spec.source.partOf, spec.source.name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_spec(self, name, repo, txn = None):
name = str(name)
def proc(txn):
assert self.has_spec(name, txn=txn)
spec = self.d.get_item(name, repo, txn)
self.d.remove_item(name, txn=txn)
for pkg in spec.packages:
self.dpkgtosrc.remove_item_repo(pkg.name, repo, txn)
ctx.componentdb.remove_spec(spec.source.partOf, spec.source.name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dpkgtosrc.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
sourcedb = None
def init():
global sourcedb
if sourcedb:
return sourcedb
sourcedb = SourceDB()
return sourcedb
def finalize():
global sourcedb
if sourcedb:
sourcedb.close()
sourcedb = None
-245
View File
@@ -1,245 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
Specfile module is our handler for PSPEC files. PSPEC (PiSi SPEC)
files are specification files for PiSi source packages. This module
provides read and write routines for PSPEC files.
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# standard python modules
from os.path import basename
# pisi modules
from pisi.pxml.xmlfile import XmlFile
import pisi.pxml.autoxml as autoxml
import pisi.context as ctx
from pisi.dependency import Dependency
import pisi.dependency
import pisi.component as component
from pisi.util import Checks
import pisi.util as util
class Error(pisi.Error):
pass
__metaclass__ = autoxml.autoxml
class Packager:
t_Name = [autoxml.Text, autoxml.mandatory]
t_Email = [autoxml.String, autoxml.mandatory]
def __str__(self):
s = "%s <%s>" % (self.name, self.email)
return s
class AdditionalFile:
s_Filename = [autoxml.String, autoxml.mandatory]
a_target = [autoxml.String, autoxml.mandatory]
a_permission = [autoxml.String, autoxml.optional]
def __str__(self):
s = "%s -> %s " % (self.filename, self.target)
if self.permission:
s += '(%s)' % self.permission
return s
class Patch:
s_Filename = [autoxml.String, autoxml.mandatory]
a_compressionType = [autoxml.String, autoxml.optional]
a_level = [autoxml.Integer, autoxml.optional]
a_target = [autoxml.String, autoxml.optional]
#FIXME: what's the cleanest way to give a default value for reading level?
#def decode_hook(self, node, errs, where):
# if self.level == None:
# self.level = 0
def __str__(self):
s = self.filename
if self.compressionType:
s += ' (' + self.compressionType + ')'
if self.level:
s += ' level:' + self.level
if self.target:
s += ' target:' + self.target
return s
class Update:
a_release = [autoxml.String, autoxml.mandatory]
a_type = [autoxml.String, autoxml.optional]
t_Date = [autoxml.String, autoxml.mandatory]
t_Version = [autoxml.String, autoxml.mandatory]
t_Name = [autoxml.Text, autoxml.optional]
t_Email = [autoxml.String, autoxml.optional]
t_Comment = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.date
s += ", ver=" + self.version
s += ", rel=" + self.release
if self.type:
s += ", type=" + self.type
return s
class Path:
s_Path = [autoxml.String, autoxml.mandatory]
a_fileType = [autoxml.String, autoxml.optional]
a_permanent = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.path
s += ", type=" + self.fileType
return s
class ComarProvide:
s_om = [autoxml.String, autoxml.mandatory]
a_script = [autoxml.String, autoxml.mandatory]
def __str__(self):
# FIXME: descriptive enough?
s = self.script
s += ' (' + self.om + ')'
return s
class Archive:
s_uri = [ autoxml.String, autoxml.mandatory ]
a_type =[ autoxml.String, autoxml.mandatory ]
a_sha1sum =[ autoxml.String, autoxml.mandatory ]
def decode_hook(self, node, errs, where):
self.name = basename(self.uri)
def __str__(self):
s = _('URI: %s, type: %s, sha1sum: %s') % (self.uri, self.type, self.sha1sum)
return s
class Source:
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [Packager, autoxml.mandatory]
t_Summary = [autoxml.LocalText, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.optional]
t_IsA = [ [autoxml.String], autoxml.optional]
t_PartOf = [autoxml.String, autoxml.optional]
t_Icon = [ autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.mandatory]
t_Archive = [Archive, autoxml.mandatory ]
t_Patches = [ [Patch], autoxml.optional]
t_BuildDependencies = [ [Dependency], autoxml.optional]
t_Version = [ autoxml.String, autoxml.optional]
t_Release = [ autoxml.String, autoxml.optional]
t_SourceURI = [ autoxml.String, autoxml.optional ] # used in index
class Package:
t_Name = [ autoxml.String, autoxml.mandatory ]
t_Summary = [ autoxml.LocalText, autoxml.optional ]
t_Description = [ autoxml.LocalText, autoxml.optional ]
t_IsA = [ [autoxml.String], autoxml.optional]
t_PartOf = [autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.optional]
t_Icon = [ autoxml.String, autoxml.optional]
t_PackageDependencies = [ [Dependency], autoxml.optional, "RuntimeDependencies/Dependency"]
t_ComponentDependencies = [ [autoxml.String], autoxml.optional, "RuntimeDependencies/Component"]
t_Files = [ [Path], autoxml.optional]
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
t_History = [ [Update], autoxml.optional]
# FIXME: needed in build process, to distinguish dynamically generated debug packages.
# find a better way to do this.
debug_package = False
def runtimeDependencies(self):
deps = self.packageDependencies
deps += [ ctx.componentdb.get_component[x].packages for x in self.componentDependencies ]
return deps
def pkg_dir(self):
packageDir = self.name + '-' \
+ self.version + '-' \
+ self.release
return util.join_path( ctx.config.lib_dir(), 'package', packageDir)
def installable(self):
"""calculate if pkg is installable currently"""
deps = self.runtimeDependencies()
return pisi.dependency.satisfies_dependencies(self.name, deps)
def __str__(self):
if self.build:
build = self.build
else:
build = '--'
s = _('Name: %s, version: %s, release: %s, build %s\n') % (
self.name, self.version, self.release, build)
s += _('Summary: %s\n') % unicode(self.summary)
s += _('Description: %s\n') % unicode(self.description)
s += _('Component: %s\n') % unicode(self.partOf)
s += _('Provides: ')
for x in self.providesComar:
s += x.om + ' '
s += '\n'
s += _('Dependencies: ')
for x in self.componentDependencies:
s += x.package + ' '
for x in self.packageDependencies:
s += x.package + ' '
return s + '\n'
class SpecFile(XmlFile):
__metaclass__ = autoxml.autoxml #needed when we specify a superclass
tag = "PISI"
t_Source = [ Source, autoxml.mandatory]
t_Packages = [ [Package], autoxml.mandatory, "Package"]
t_History = [ [Update], autoxml.mandatory]
t_Components = [ [component.Component], autoxml.optional, "Component"]
def getSourceVersion(self):
return self.history[0].version
def getSourceRelease(self):
return self.history[0].release
def dirtyWorkAround(self):
#TODO: Description should be mandatory. Remove this crap when repo is ready.
#http://liste.pardus.org.tr/gelistirici/2006-September/002332.html
self.source.description = autoxml.LocalText("Description")
self.source.description["en"] = self.source.summary["en"]
-101
View File
@@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# generic user interface
#
import sys
import pisi
import pisi.context as ctx
(installed, upgraded, removed, installing, removing, configuring, configured, extracting,
downloading, packagestogo, updatingrepo) = range(11)
class UI(object):
"Abstract class for UI operations, derive from this."
class Progress:
def __init__(self, totalsize, existsize = 0):
self.totalsize = totalsize
try:
self.percent = (existsize * 100) / totalsize
except:
self.percent = 0
def update(self, size):
if not self.totalsize:
return 100
try:
self.percent = (size * 100) / self.totalsize
except:
self.percent = 0
return self.percent
def __init__(self, debuggy = False, verbose = False):
self.show_debug = debuggy
self.show_verbose = verbose
def close(self):
"cleanup stuff here"
pass
def set_verbose(self, flag):
self.show_verbose = flag
def set_debug(self, flag):
self.show_debug = flag
def info(self, msg, verbose = False, noln = False):
"give an informative message"
pass
def ack(self, msg):
"inform the user of an important event and wait for acknowledgement"
pass
def debug(self, msg):
"show debugging info"
if self.show_debug:
self.info('DEBUG: ' + msg)
def warning(self,msg):
"warn the user"
pass
def error(self,msg):
"inform a (possibly fatal) error"
pass
#FIXME: merge this with info, this just means "important message"
def action(self,msg):
"uh?"
pass
def choose(self, msg, list):
"ask the user to choose from a list of alternatives"
pass
def confirm(self, msg):
"ask a yes/no question"
pass
def display_progress(self, pd):
"display progress"
pass
def status(self, msg = None):
"set status, if not given clear it"
pass
def notify(self, event, **keywords):
"notify UI of a significant event"
pass
-110
View File
@@ -1,110 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Simplifies working with URLs, purl module provides common URL
parsing and processing"""
from urlparse import urlparse
from os.path import basename
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.util as util
class URI(object):
"""URI class provides a URL parser and simplifies working with
URLs."""
def __init__(self, uri=None):
if uri:
self.set_uri(str(uri))
else:
self.__scheme = None
self.__location = None
self.__path = None
self.__filename = None
self.__params = None
self.__query = None
self.__fragment = None
self.__uri = None
self.__authinfo = None
def get_uri(self):
if self.__uri:
return self.__uri
return None
def set_uri(self, uri):
# (scheme, location, path, params, query, fragment)
uri = str(uri)
u = urlparse(uri, "file")
self.__scheme = u[0]
self.__location = u[1]
self.__path = u[2]
self.__filename = basename(self.__path)
self.__params = u[3]
self.__query = u[4]
self.__fragment = u[5]
self.__uri = uri
def is_local_file(self):
if self.scheme() == "file":
return True
else:
return False
def is_remote_file(self):
return not self.is_local_file()
def is_absolute_path(self):
return util.absolute_path(self.__path)
def is_relative_path(self):
return not self.is_absolute_path()
def set_auth_info(self, authTuple):
if not isinstance(authTuple, tuple):
raise Exception, _("setAuthInfo needs a tuple (user, pass)")
self.__authinfo = authTuple
def auth_info(self):
return self.__authinfo
def scheme(self):
return self.__scheme
def location(self):
return self.__location
def path(self):
return self.__path
def filename(self):
return self.__filename
def params(self):
return self.__params
def query(self):
return self.__query
def fragment(self):
return self.__fragment
def __str__(self):
return self.get_uri()
uri = property(get_uri, set_uri)
-654
View File
@@ -1,654 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""misc. utility functions, including process and file utils"""
# standard python modules
import os
import re
import sys
import sha
import shutil
import string
import statvfs
import operator
import subprocess
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
class FileError(Error):
pass
#########################
# spec validation utility #
#########################
class Checks:
def __init__(self):
self.list = []
def add(self, err):
self.list.append(err)
def join(self, list):
self.list.extend(list)
def has_tag(self, var, section, name):
if not var:
self.list.append(_("%s section should have a '%s' tag") % (section, name))
def has_error():
return len(self.list)>0
def print_errors(list):
for x in list:
ctx.ui.error(x)
print_errors = staticmethod(print_errors)
#########################
# string/list/functional#
#########################
def every(pred, seq):
return reduce(operator.and_, map(pred, seq), True)
def any(pred, seq):
return reduce(operator.or_, map(pred, seq), False)
def unzip(seq):
return zip(*seq)
def concat(l):
'''concatenate a list of lists'''
return reduce( operator.concat, l )
def strlist(l):
"""concatenate string reps of l's elements"""
return "".join(map(lambda x: str(x) + ' ', l))
def multisplit(str, chars):
""" split str with any of chars"""
l = [str]
for c in chars:
l = concat(map(lambda x:x.split(c), l))
return l
def same(l):
'''check if all elements of a sequence are equal'''
if len(l)==0:
return True
else:
last = l.pop()
for x in l:
if x!=last:
return False
return True
def prefix(a, b):
'''check if sequence a is a prefix of sequence b'''
if len(a)>len(b):
return False
for i in range(0,len(a)):
if a[i]!=b[i]:
return False
return True
def remove_prefix(a,b):
"remove prefix a from sequence b"
assert prefix(a,b)
return b[len(a):]
def human_readable_size(size = 0):
symbols, depth = [' B', 'KB', 'MB', 'GB'], 0
while size > 1000 and depth < 3:
size = float(size / 1024)
depth += 1
return size, symbols[depth]
def human_readable_rate(size = 0):
x = human_readable_size(size)
return x[0], x[1] + '/s'
##############################
# Process Releated Functions #
##############################
def run_batch(cmd):
"""run command and report return value and output"""
ctx.ui.info(_('Running ') + cmd, verbose=True)
p = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
return (p.returncode, out, err)
# you can't use the following for Popen, oops
class TeeOutFile:
def __init__(self, file):
self.file = file
def write(self, str):
self.write(str)
ctx.ui.debug(str)
# TODO: it might be worthwhile to try to remove the
# use of ctx.stdout, and use run_batch()'s return
# values instead. but this is good enough :)
def run_logged(cmd):
"""run command and get return value"""
ctx.ui.info(_('Running ') + cmd, verbose=True)
if ctx.stdout:
stdout = ctx.stdout
else:
if ctx.get_option('debug'):
stdout = None
else:
stdout = subprocess.PIPE
if ctx.stderr:
stderr = ctx.stderr
else:
if ctx.get_option('debug'):
stderr = None
else:
stderr = subprocess.STDOUT
p = subprocess.Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
out, err = p.communicate()
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
return p.returncode
######################
# Terminal functions #
######################
def has_xterm():
return os.environ.has_key("TERM") and sys.stderr.isatty()
def xterm_title(message):
"""sets message as a console window's title"""
if os.environ.has_key("TERM") and sys.stderr.isatty():
terminalType = os.environ["TERM"]
for term in ["xterm", "Eterm", "aterm", "rxvt", "screen", "kterm", "rxvt-unicode"]:
if terminalType.startswith(term):
sys.stderr.write("\x1b]2;"+str(message)+"\x07")
sys.stderr.flush()
break
def xterm_title_reset():
"""resets console window's title"""
if os.environ.has_key("TERM"):
terminalType = os.environ["TERM"]
xterm_title(os.environ["TERM"])
#############################
# Path Processing Functions #
#############################
def splitpath(a):
"""split path into components and return as a list
os.path.split doesn't do what I want like removing trailing /"""
comps = a.split(os.path.sep)
if comps[len(comps)-1]=='':
comps.pop()
return comps
def makepath(comps, relative = False, sep = os.path.sep):
"""reconstruct a path from components"""
path = reduce(lambda x,y: x + sep + y, comps, '')
if relative:
return path[len(sep):]
else:
return path
def parentpath(a, sep = os.path.sep):
# remove trailing '/'
a = a.rstrip(sep)
return a[:a.rfind(sep)]
def parenturi(a):
return parentpath(a, '/')
# I'm not sure how necessary this is. Ahem.
def commonprefix(l):
"""an improved version of os.path.commonprefix,
returns a list of path components"""
common = []
comps = map(splitpath, l)
for i in range(0, min(len,l)):
compi = map(lambda x: x[i], comps) # get ith slice
if same(compi):
common.append(compi[0])
return common
# but this one is necessary
def subpath(a, b):
"find if path a is before b in the directory tree"
return prefix(splitpath(a), splitpath(b))
def removepathprefix(prefix, path):
"remove path prefix a from b, finding the pathname rooted at a"
comps = remove_prefix(splitpath(prefix), splitpath(path))
if len(comps) > 0:
return join_path(*tuple(comps))
else:
return ""
def absolute_path(path):
"determine if given @path is absolute"
comps = splitpath(path)
return comps[0] == ''
def join_path(a, *p):
"""Join two or more pathname components, inserting '/' as needed"""
"""The python original version has a silly logic"""
path = a
for b in p:
b = b.lstrip('/')
if path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
####################################
# File/Directory Related Functions #
####################################
def check_file(file, mode = os.F_OK):
"shorthand to check if a file exists"
if not os.access(file, mode):
raise FileError("File " + file + " not found")
return True
def check_dir(dir):
"""check if directory exists, and create if it doesn't.
works recursively"""
dir = dir.strip().rstrip("/")
if not os.access(dir, os.F_OK):
os.makedirs(dir)
def clean_dir(path):
"Remove all content of a directory (top)"
# don't reimplement the wheel
if os.path.exists(path):
shutil.rmtree(path)
def creation_time(file):
"""returns the creation time of the given file"""
if check_file(file):
import time
st = os.stat(file)
return time.localtime(st.st_ctime)
def dir_size(dir):
""" calculate the size of files under a dir
based on the os module example"""
# It's really hard to give an approximate value for package's
# installed size. Gettin a sum of all files' sizes if far from
# being true. Using 'du' command (like Debian does) can be a
# better solution :(.
# Not really, du calculates size on disk, this is much better -- exa
from os.path import getsize, islink, isdir, exists
join = join_path
if exists(dir) and (not isdir(dir) and not islink(dir)):
#so, this is not a directory but file..
return getsize(dir)
if islink(dir):
return long(len(os.readlink(dir)))
def sizes():
for root, dirs, files in os.walk(dir):
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
yield sum([long(len(os.readlink((join(root, name))))) for name in files if islink(join(root,name))])
return sum( sizes() )
def copy_file(src,dest):
"""copy source file to destination file"""
check_file(src)
check_dir(os.path.dirname(dest))
shutil.copyfile(src, dest)
def is_ar_file(file_path):
return open(file_path).readline().strip() == '!<arch>'
def clean_ar_timestamps(ar_file):
"""clean the timestamps of the ar files"""
if not is_ar_file(ar_file):
return
content = open(ar_file).readlines()
fp = open(ar_file, 'w')
for line in content:
pos = line.rfind(chr(32) + chr(96))
if pos > -1 and line[pos - 57:pos + 2].find(chr(47)) > -1:
line = line[:pos - 41] + '0000000000' + line[pos - 31:]
fp.write(line)
fp.close()
# FIXME: this should be done in a much much simpler way
# as it stands, it seems to be a kludge to solve
# an unrelated problem
def get_file_hashes(top, excludePrefix=None, removePrefix=None):
"""Generator function iterates over a toplevel path and returns the
(filePath, sha1Hash) tuples for all files. If excludePrefixes list
is given as a parameter, function will exclude the filePaths
matching those prefixes. The removePrefix string parameter will be
used to remove prefix from filePath while matching excludes, if
given."""
def sha1_sum(f, data=False):
if not data and f.endswith('.a'):
#workaround for .a issue..
#don't skip .a files,
#but pad their timestamps with '0'..
clean_ar_timestamps(f)
func = None
if data:
func = sha1_data
else:
func = sha1_file
try:
return func(f)
except FileError, e:
if os.path.islink(f):
ctx.ui.info(_("Including external link '%s'") % f)
elif os.path.isdir(f):
ctx.ui.info(_("Including directory '%s'") % f)
else:
raise e
return None
def has_excluded_prefix(filename):
if excludePrefix and removePrefix:
tempfnam = remove_prefix(removePrefix, filename)
for p in excludePrefix:
if tempfnam.startswith(p):
return 1
return 0
# handle single file
if os.path.isfile(top):
yield (top, sha1_sum(top))
return
# handle single symlink declaration here.
if os.path.islink(top):
yield (top, sha1_sum(os.readlink(top), True))
return
for root, dirs, files in os.walk(top, topdown=False):
#bug 339
if os.path.islink(root) and not has_excluded_prefix(root):
#yield the symlink..
#bug 373
yield (root, sha1_sum(os.readlink(root), True))
excludePrefix.append(remove_prefix(removePrefix, root) + "/")
continue
#bug 397
for dir in dirs:
d = join_path(root, dir)
if os.path.islink(d) and not has_excluded_prefix(d):
yield (d, sha1_sum(os.readlink(d), True))
excludePrefix.append(remove_prefix(removePrefix, d) + "/")
#bug 340
if os.path.isdir(root) and not has_excluded_prefix(root):
parent, r, d, f = root, '', '', ''
for r, d, f in os.walk(parent, topdown=False): pass
if not f and not d:
yield (parent, sha1_sum(parent))
for fname in files:
f = join_path(root, fname)
if has_excluded_prefix(f):
continue
#bug 373
elif os.path.islink(f):
yield (f, sha1_sum(os.readlink(f), True))
else:
yield (f, sha1_sum(f))
def copy_dir(src, dest):
"""copy source dir to destination dir recursively"""
shutil.copytree(src, dest)
def check_file_hash(filename, hash):
"""Check the files integrity with a given hash"""
return sha1_file(filename) == hash
def sha1_file(filename):
"""calculate sha1 hash of filename"""
# Broken links can cause problem!
try:
m = sha.new()
f = file(filename, 'rb')
for line in f:
m.update(line)
return m.hexdigest()
except IOError:
raise FileError(_("I/O Error: Cannot calculate SHA1 hash of %s") % filename)
def sha1_data(data):
"""calculate sha1 hash of given data"""
try:
m = sha.new()
m.update(data)
return m.hexdigest()
except:
raise Error(_("Cannot calculate SHA1 hash of given data"))
def uncompress(patchFile, compressType="gz", targetDir=None):
"""uncompresses a file and returns the path of the uncompressed
file"""
if targetDir:
filePath = join_path(targetDir,
os.path.basename(patchFile))
else:
filePath = os.path.basename(patchFile)
if compressType == "gz":
from gzip import GzipFile
obj = GzipFile(patchFile)
elif compressType == "bz2":
from bz2 import BZ2File
obj = BZ2File(patchFile)
open(filePath, "w").write(obj.read())
return filePath
def do_patch(sourceDir, patchFile, level = 0, target = ''):
"""simple function to apply patches.."""
cwd = os.getcwd()
os.chdir(sourceDir)
if level == None:
level = 0
if target == None:
target = ''
check_file(patchFile)
(ret, out, err) = run_batch("patch -p%d %s < %s" %
(level, target, patchFile))
if ret:
if out is None and err is None:
# Which means stderr and stdout directed so they are None
raise Error(_("ERROR: patch (%s) failed") % (patchFile))
else:
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile, out))
os.chdir(cwd)
def strip_directory(top, excludelist=[]):
for root, dirs, files in os.walk(top):
for fn in files:
frpath = join_path(root, fn)
drpath = join_path(os.path.dirname(top),
ctx.const.debug_dir_suffix,
remove_prefix(top, frpath))
# Some upstream sources have buggy libtool and ltmain.sh with them,
# which causes wrong path entries in *.la files. And these wrong path
# entries sometimes triggers compile-time errors or linkage problems.
# Instead of patching all these buggy sources and maintain these patches,
# PiSi removes wrong paths...
extension = os.path.splitext(frpath)[1]
if extension == ".la":
# FIXME: I'm regular expr. idiot, so one can convert this to python...
os.system("sed -i -e 's~-L/var/tmp/pisi/[[:graph:]]*~~g' %s" % frpath)
os.system("sed -i -e 's~/var/tmp/pisi/[[:graph:]]*/install/~/~g' %s" % frpath)
# real path in .pisi package
p = '/' + removepathprefix(top, frpath)
strip = True
for exclude in excludelist:
if p.startswith(exclude):
strip = False
ctx.ui.debug("%s [%s]" %(p, "NoStrip"))
if strip:
if strip_file(frpath, drpath):
ctx.ui.debug("%s [%s]" %(p, "stripped"))
def strip_file(filepath, outpath):
"""strip a file"""
p = os.popen("file \"%s\"" % filepath)
o = p.read()
def run_strip(f, flags=""):
p = os.popen("strip %s %s" %(flags, f))
ret = p.close()
if ret:
ctx.ui.warning(_("strip command failed for file '%s'!") % f)
def save_elf_debug(f, o):
"""copy debug info into file.debug file"""
p = os.popen("objcopy --only-keep-debug %s %s%s" % (f, o, ctx.const.debug_file_suffix))
ret = p.close()
if ret:
ctx.ui.warning(_("objcopy (keep-debug) command failed for file '%s'!") % f)
"""mark binary/shared objects to use file.debug"""
p = os.popen("objcopy --add-gnu-debuglink=%s%s %s" % (o, ctx.const.debug_file_suffix, f))
ret = p.close()
if ret:
ctx.ui.warning(_("objcopy (add-debuglink) command failed for file '%s'!") % f)
if "current ar archive" in o:
run_strip(filepath, "-g")
return True
elif "SB executable" in o:
if ctx.config.values.build.generatedebug:
check_dir(os.path.dirname(outpath))
save_elf_debug(filepath, outpath)
run_strip(filepath)
return True
elif "SB shared object" in o:
if ctx.config.values.build.generatedebug:
check_dir(os.path.dirname(outpath))
save_elf_debug(filepath, outpath)
run_strip(filepath, "--strip-unneeded")
# FIXME: warn for TEXTREL
return True
return False
def partition_freespace(directory):
""" returns free space of given directory's partition """
st = os.statvfs(directory)
return st[statvfs.F_BSIZE] * st[statvfs.F_BFREE]
def clean_locks(top = '.'):
for root, dirs, files in os.walk(top):
for fn in files:
if fn.endswith('.lock'):
path = join_path(root, fn)
ctx.ui.info(_('Removing lock %s'), path)
os.unlink(path)
########################################
# Package/Repository Related Functions #
########################################
def package_name(name, version, release, build, prependSuffix=True):
fn = name + '-' + version + '-' + release
if build:
fn += '-' + str(build)
if prependSuffix:
fn += ctx.const.package_suffix
return fn
def is_package_name(fn, package_name = None):
"check if fn is a valid filename for given package_name"
"if not given a package name, see if fn fits the package name rules"
if (package_name==None) or fn.startswith(package_name + '-'):
if fn.endswith(ctx.const.package_suffix):
# get version string, skip separator '-'
verstr = fn[len(package_name) + 1:
len(fn)-len(ctx.const.package_suffix)]
import string
for x in verstr.split('-'):
# weak rule: version components after '-' start with a digit
if x is '' or (not x[0] in string.digits):
return False
return True
return False
def env_update():
import pisi.environment
ctx.ui.info(_('Updating environment...'))
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
if not os.path.exists(env_dir):
os.makedirs(env_dir, 0755)
pisi.environment.update_environment(ctx.config.dest_dir())
def parse_package_name(package_name):
"return package name and version string"
"ex: package_name=tasma-1.0.3-5-2, returns (tasma, 1.0.3-5-2)"
# but we should handle package names like 855resolution
name = []
for part in package_name.split("-"):
if name != [] and part[0] in string.digits:
break
else:
name.append(part)
name = "-".join(name)
version = package_name[len(name) + 1:]
return (name, version)
-215
View File
@@ -1,215 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""version structure"""
import re
import string
import exceptions
import pisi
import pisi.util as util
maxdashes = 2
# Basic rule is:
# p > (no suffix) > m > rc > pre > beta > alpha
# m: milestone. this was added for OO.o
# p: patch-level
keywords = {"alpha": 0,
"beta" : 1,
"pre" : 2,
"rc" : 3,
"m" : 4,
"NOKEY" : 5,
"p" : 6}
# helper functions
def has_keyword(versionitem):
if versionitem._keyword != "NOKEY":
return True
return False
class VersionException(pisi.Exception):
def __init__(self, str):
pisi.Exception.__init__(self, str)
class VersionItem:
_keyword = "NOKEY"
_value = 0
def __init__(self, itemstring):
# special-case: 1.5p
# here "p" comes as a separate itemstring and conflicts with
# keyword "p". But keyword should allways contain an integer prefix.
# So, skipping looking for keywords if the length() is 1 makes the
# trick.
if len(itemstring) > 1:
for keyword in keywords.keys():
if itemstring.startswith(keyword):
if self._keyword == "NOKEY":
self._keyword = keyword
else:
# longer match is correct
if len(keyword) > len(self._keyword):
self._keyword = keyword
if self._keyword == "NOKEY":
if len(itemstring) == 1 and itemstring in string.ascii_letters:
# single letter version item ('a' to 'Z')
self._value = itemstring
else:
self._value = int(itemstring)
else:
# rest is the version item's value. And each must have
# one!
self._value = int(itemstring[len(self._keyword):])
def __str__(self):
return str(self._value)
def __lt__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l < r:
return True
elif l == r:
return self._value < rhs._value
else: # l > r
return False
def __le__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l < r:
return True
elif l == r:
return self._value <= rhs._value
else: # l > r
return False
def __gt__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l > r:
return True
elif l == r:
return self._value > rhs._value
else: # l < r
return False
def __ge__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l > r:
return True
elif l == r:
return self._value >= rhs._value
else: # l < r
return False
def __eq__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l == r and self._value == rhs._value:
return True
return False
class Version:
def __init__(self, verstring):
# PiSi version policy does not allow "-" in version strings.
# They are special and used for build and release no separation.
if verstring.count("-") > maxdashes:
raise VersionException("%s is not a valid PiSi version format" % verstring)
verchunks = verstring.split("-")
verchunks.extend("0" * (maxdashes - verstring.count("-")))
(version, release, build) = verchunks
self.comps = []
for i in util.multisplit(version,'._'):
# some version strings can contain ascii chars at the
# back. As an example: 2.11a
# We split '11a' as two items like '11' and 'a'
s = re.compile("[a-z-A-Z]$").search(i)
if s:
head = i[:s.start()]
tail = s.group()
self.comps.append(VersionItem(head))
self.comps.append(VersionItem(tail))
else:
self.comps.append(VersionItem(i))
self.verstring = verstring
self.release = VersionItem(release)
self.build = VersionItem(build)
def string(self):
return self.verstring
def compare(self, ver):
"""this comparison routine is essentially a comparison routine
for two rationals in (0,1) interval. we compare two sequences
of digits one by one. We start with the leftmost digit
in the expansion. If they are equal, we proceed to the next. If
not we use the comparison operator. And we iterate to the left.
The result is, 0 if two are equal, -1 if self < rhs, and +1
if self>rhs"""
lhs = self.comps
rhs = ver.comps
# pad the short version string with zeros
if len(lhs) < len(rhs):
lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) )
elif len(lhs) > len(rhs):
rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) )
# now let's iterate from left to right in version items
for (litem, ritem) in zip(lhs, rhs):
if litem < ritem:
return -1
elif litem > ritem:
return +1
# now let's compare release and build
lhs = (self.release, self.build)
rhs = (ver.release, ver.build)
for (litem, ritem) in zip(lhs, rhs):
if litem < ritem:
return -1
elif litem > ritem:
return +1
return 0
# premature optimization is the root of all evil
def __lt__(self,rhs):
return self.compare(rhs) < 0
def __le__(self,rhs):
return self.compare(rhs) <= 0
def __gt__(self,rhs):
return self.compare(rhs) > 0
def __ge__(self,rhs):
return self.compare(rhs) >= 0
def __eq__(self,rhs):
return self.compare(rhs) == 0
def __str__(self):
return self.verstring
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
#
# Scenario : bug3237scen.py
#
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3237
#
# Problem : If the revdep of the "upgraded only packages"' dependency is not satisfied
# it is also added to the upgrade list and creates "confusion"
#
# # pisi lu
# amarok - KDE için müzik çalıcısı
# ksynaptics - Synaptics touchpad yapılandırma aracı
# libwmf - Microsoft Word gibi uygulamaların kullan.....
# tunepimp - MusicBrainz uyumluluğu olan uygulamalar.......
#
# pisi up tunepimp libwmf ksynaptics
# Depolar güncelleniyor
# * pardus-1.1 deposu güncelleniyor
# pardus-1.1 deposu için güncelleme yok.
# Aşağıdaki paketlerde çakışmalar bulunuyor: [amarok: lastfm-player ile çakışıyor]
# Bu çakışan paketler kaldırılsın mı? (evet/hayır)hayır
#
# Problem Description:
#
# There appears to be some upgrades needed. We only wanted to upgrade some of the packages.
# But the package we did not include in the list of upgrades also is tried to be upgraded.
# The purpose to not include it in the upgrade list, was, it was conflicting with another
# package.
#
# Expected:
#
# pisi either should not upgrade packages out of the given upgrade list or should show
# all the "to be upgraded" list before the conflict warning.
from pisi.scenarioapi.scenario import *
AMAROK = "amarok"
KSYNAPTICS = "ksynaptics"
LIBWMF = "libwmf"
TUNEPIMP = "tunepimp"
LASTFM = "lastfm-player"
let_repo_had(AMAROK, with_conflicts(LASTFM))
let_repo_had(LASTFM)
let_repo_had(TUNEPIMP, with_version("0.3.9"))
let_repo_had(KSYNAPTICS)
let_repo_had(LIBWMF)
let_pisi_had(AMAROK, LASTFM, TUNEPIMP, KSYNAPTICS, LIBWMF)
def run():
repo_version_bumped(TUNEPIMP, with_version("0.4.1"))
repo_version_bumped(AMAROK, with_added_dependency(TUNEPIMP, versionFrom="0.4.1"))
repo_version_bumped(KSYNAPTICS)
repo_version_bumped(LIBWMF)
repo_updated_index()
pisi_upgraded(LIBWMF, TUNEPIMP, KSYNAPTICS)
@@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
#
# Scenario : bug3390scen.py
#
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3390
#
# Problem : Package reverse dependencies are not updated in pisi database
#
# caglar@zangetsu ~ $ pisi info valgrind
# Yüklü paket:
# Ad: valgrind, versiyon 3.2.0, sürüm 5, inşa 4
# Özet: Valgrind, x86-GNU/Linux ve ppc-GNU/Linux için geliştirilmiş, bellek
# hatalarını ayıklayıcı, açık kaynaklı bir yazılımdır.
# Açıklama: Valgrind, x86-GNU/Linux ve ppc-GNU/Linux için geliştirilmiş, bellek
# hatalarını ayıklayıcı, açık kaynaklı bir yazılımdır.
# Bileşen: programming.tools
# Sağladıkları:
# Bağımlılıklar:xorg openmpi
# Dağıtım: Pardus, Dağıtım Sürümü: 1.1
# Mimari: Any, Yerleşik Boyut: 19128120
# Ters bağımlılıklar: kdesdk
#
# Problem Description:
#
# Valgrind package had a dependency of openmpi package. It is version bumped with removed
# dependeny of openmpi at the repository. Also openmpi is version bumped. After upgrading
# pisi repository and trying to remove openmpi, it is seen that openmpi still has a reverse
# dependency of valgrind.
#
# Expected:
#
# Pisi should have updated the reverse dependency informations correctly and should not show
# a reverse dependency of valgrind while removing openmpi, after a succesfull repository upgrade.
from pisi.scenarioapi.scenario import *
VALGRIND = "valgrind"
OPENMPI = "openmpi"
let_repo_had(VALGRIND, with_dependencies(OPENMPI))
let_repo_had(OPENMPI)
let_pisi_had(VALGRIND, OPENMPI)
def run():
repo_version_bumped(VALGRIND, with_removed_dependencies(OPENMPI))
repo_version_bumped(OPENMPI)
repo_updated_index()
pisi_upgraded()
pisi_removed(OPENMPI)
@@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
#
# Scenario : bug3465scen.py
#
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3465
#
# Problem : Pisi does not warn the user about updated packages in the repository.
#
# sudo pisi it wormux --reinstall
# Bağımlılıkları sağlamak için bu paketler verilen sırada kurulacaktır:
# wormux
# Paketlerin toplam boyu: 17.62 MB
# Paket wormux, pardus-1.1 deposunda bulundu
# Program sonlandırıldı.
# http://paketler.pardus.org.tr/pardus-1.1/wormux-0.7-2-1.pisi indirilemiyor; HTTP
# Error 404: Not Found
#
# Problem Description:
#
# The user had not updated pisi's repository database for some time. Then, when pisi is asked
# to reinstall an installed package, it failed to fetch the requested version of the package.
# Because the package had been upgraded at the repository and the old package has been removed.
#
# Expected:
#
# Pisi should warn the user to update repository.
from pisi.scenarioapi.scenario import *
WORMUX = "wormux"
let_repo_had(WORMUX)
let_pisi_had(WORMUX)
def run():
repo_version_bumped(WORMUX)
repo_updated_index()
pisi_reinstalled(WORMUX)
@@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
#
# Scenario : bug3481scen.py
#
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3481
#
# Problem : PISI asks if conflicting system.base application to be removed but does not
# allow it without -S
#
# Aşağıdaki paketlerde çakışmalar bulunuyor: [coreutils: hashalot ile çakışıyor]
# Bu çakışan paketler kaldırılsın mı? (evet/hayır)evet
# Emniyet mandalı: taban sistem system.base deki bu paketler kaldırılamıyor: hashalot
# Kaldıracak paket yok.
# Program sonlandırıldı.
# pisi.operations.Error: Çakışmalar var
# Genel yardım için lütfen 'pisi help' komutunu kullanınız.
#
# Problem Description:
#
# PISI upgrade command sees some system.base packages conflict with each other. It asks if you
# want to remove the conflicting package but does not allow it to be removed without
# --bypass-safety parameter.
#
# Expected:
#
# Pisi should remove the package if answered yes.
#
from pisi.scenarioapi.scenario import *
HASHALOT="hashalot"
COREUTILS="coreutils"
GLIBC="glibc"
UTIL_LINUX="util-linux"
let_repo_had(HASHALOT, with_partof("system.base"))
let_repo_had(COREUTILS, with_partof("system.base"))
let_repo_had(GLIBC, with_partof("system.base"))
let_repo_had(UTIL_LINUX, with_partof("system.base"))
let_pisi_had(COREUTILS, HASHALOT, GLIBC, UTIL_LINUX)
def run():
repo_version_bumped(GLIBC)
repo_version_bumped(UTIL_LINUX)
repo_version_bumped(COREUTILS, with_added_conflicts(HASHALOT))
repo_updated_index()
pisi_upgraded()
@@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
#
# Scenario : bug3558scen.py
#
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3558
#
# Problem : reverse dependency information disappears
#
# faik@iago scenarios $ pisi info kdelibs
# Yüklü paket:
# Ad: kdelibs, versiyon 3.5.4, sürüm 35, inşa 17
# Özet: Tüm KDE programlarının ihtiyaç duyduğu KDE kütüphaneleri
# Açıklama: Tüm KDE programlarının ihtiyaç duyduğu KDE kütüphaneleri
# Bileşen: desktop.kde.base
# Sağladıkları:
# Bağımlılıklar:qt arts freetype fontconfig libxslt libxml2 libpcre libart_lgpl libidn utempter alsa-lib cups tiff aspell
# jasper mDNSResponder ghostscript acl zpspell openexr mit-kerberos tulliana2
# Dağıtım: Pardus, Dağıtım Sürümü: 1.1
# Mimari: Any, Yerleşik Boyut: 222414685
# Ters bağımlılıklar: kdebase gwenview
#
# Problem Description:
#
# Package's reverse dependencies disappears unexpectedly. If a package which has reverse dependencies has been upgraded.
# The revdep list is removed from revdep db. So upgraded package will have an empty revdep list.
#
# Expected:
#
# They should not. :)
#
from pisi.scenarioapi.scenario import *
FLIGHTGEAR = "flightgear"
FLIGHTGEAR_DATA = "flightgear-data"
let_repo_had(FLIGHTGEAR, with_dependencies(FLIGHTGEAR_DATA))
let_repo_had(FLIGHTGEAR_DATA)
let_pisi_had(FLIGHTGEAR, FLIGHTGEAR_DATA)
def run():
pisi_info(FLIGHTGEAR_DATA)
repo_version_bumped(FLIGHTGEAR_DATA)
repo_updated_index()
pisi_upgraded()
pisi_info(FLIGHTGEAR_DATA)

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