Move pisi-1.0_alpha2 tag
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
Like every serious project, there are guidelines.
|
||||
Oooooo. "Coding Standards".
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
1. When using dirnames, don't expect the dir to end
|
||||
with a trailing slash, and please use the dirnames
|
||||
in pisiconfig
|
||||
2. Python indentation is usually 4 chars.
|
||||
3. Follow python philosophy of 'batteries included'
|
||||
4. Don't make the code have runtime dependencies on
|
||||
a particular distribution (as much as possible)
|
||||
5. Don't assume narrow use cases.
|
||||
6. 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!
|
||||
|
||||
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:
|
||||
|
||||
$ ./unittests/run.py
|
||||
|
||||
If you know what you are doing, you can run the tests seperately. But
|
||||
keep in your mind that tests can depend on each other. (?) The unit test
|
||||
system doesn't know about that. The following command will run tests
|
||||
in specfiletests and archivetests in unittests dir:
|
||||
|
||||
$ ./unittests/run.py specfile archive
|
||||
|
||||
|
||||
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.
|
||||
@@ -1,346 +0,0 @@
|
||||
NOTE! The GPL below is copyrighted by the Free Software Foundation, but
|
||||
the instance of code that it refers to (the kde programs) are copyrighted
|
||||
by the authors who actually wrote it.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -1,4 +0,0 @@
|
||||
recursive-include tests *.py
|
||||
recursive-include doc *
|
||||
recursive-include spec *
|
||||
recursive-include samples *
|
||||
@@ -1,12 +0,0 @@
|
||||
PISI - Packages Installed Succesfully as Intented
|
||||
|
||||
PISI is a new package manager implemented in python for the PARDUS
|
||||
distribution.
|
||||
|
||||
Main features
|
||||
|
||||
- Implemented in python.
|
||||
- All specification and intermediate data is in an XML format.
|
||||
- Fast database access implemented with berkeley DB.
|
||||
- Integrates both low-level and high-level package operations.
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
|
||||
|
||||
PiSi TODO List
|
||||
==============
|
||||
|
||||
A list of tasks to accomplish, organized into priority sections
|
||||
|
||||
Legend:
|
||||
|
||||
- Todo
|
||||
? Not determined if/how we have to do
|
||||
/ In progress
|
||||
+ Accomplished
|
||||
|
||||
1. Pre-Alpha
|
||||
|
||||
+ reading spec file
|
||||
+ writing
|
||||
+ files
|
||||
+ metadata
|
||||
+ Actions API framework
|
||||
+ sample api modules (autotools)
|
||||
+ unit test
|
||||
+ source building
|
||||
+ package module
|
||||
+ extraction
|
||||
+ add file / compression
|
||||
+ package creation
|
||||
+ install database
|
||||
+ package database
|
||||
+ command line interface
|
||||
+ pisi-build
|
||||
+ pisi-install
|
||||
+ pisi-index
|
||||
+ pisi-updatedb
|
||||
+ single repository index
|
||||
+ simple dependency checking
|
||||
+ Package/Files
|
||||
|
||||
2. Alpha
|
||||
+ multi-package dependency analysis (eray)
|
||||
+ design a package operation planner
|
||||
+ install/remove operations (eray)
|
||||
+ command line interface:
|
||||
+ pisi remove
|
||||
+ svn-like CLI
|
||||
+ configuration file
|
||||
+ define the format of the configuration file
|
||||
+ extend Config module (config.py) accordingly
|
||||
+ define configuration keys (baris - meren)
|
||||
+ query
|
||||
+ list of installed packages (eray)
|
||||
+ refactor actionsAPI (caglar)
|
||||
+ fix xml indentation (meren)
|
||||
+ define file types (doc, executable, conf, etc...) (baris - meren)
|
||||
+ write specfile (eray)
|
||||
+ file locking for database access [not tested!] (eray)
|
||||
+ implement file uri
|
||||
+ extend the modules dealing with files accordingly
|
||||
+ ui module improvements
|
||||
+ methods for interaction (yes, no questions, etc.)
|
||||
+ implement source database (eray)
|
||||
+ easy package preparation
|
||||
+ convert ebuild to pisi
|
||||
+ COMAR interface
|
||||
+ what do we need to specify in a package.
|
||||
+ package install: register config script
|
||||
+ package remove: unregister config script
|
||||
+ internet installation
|
||||
+ support URI's whereever a filename is supported
|
||||
+ http server
|
||||
+ pisi updatedb over internet
|
||||
|
||||
3. Beta
|
||||
|
||||
- configure-pending (eray)
|
||||
/ autoxml: automated xml processing (eray)
|
||||
+ design
|
||||
+ basic types
|
||||
/ list type
|
||||
- class type
|
||||
/ localtext type
|
||||
/ components (eray)
|
||||
+ requirements
|
||||
- xml format
|
||||
- query components: list of components/packages in a component
|
||||
- install/remove components
|
||||
/ API: we even have application users (eray)
|
||||
/ multiple package repository (eray)
|
||||
+ decide how to implement
|
||||
+ support repo order
|
||||
- support medium types
|
||||
- internet: http/ftp
|
||||
- local repository: file://
|
||||
- removable media: media://
|
||||
/ i18n support
|
||||
/ UI
|
||||
? pyqt UI class to interface
|
||||
- PISIMAT
|
||||
/ pykde GUI (cartman)
|
||||
+ improve interface
|
||||
+ refactor UI, reintroduce base class
|
||||
+ add an ack interface, start cleaning up a bit
|
||||
+ cli
|
||||
+ ask alternatives to choose from (eray)
|
||||
+ eliminate \n's from infos, what's the point?
|
||||
+ metaclass coolness for CLI command framework (eray)
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
- partial caching and automatic resume for file download (meren)
|
||||
- use a separate "partial/" subdir like urpmi
|
||||
/ exception handling (baris)
|
||||
/ better/more sensible exception hierarchy (eray)
|
||||
? recovery from exceptions where necessary
|
||||
/ query
|
||||
- comar OM information (Provides)
|
||||
+ package by name, summary/description
|
||||
- files (rpm -ql)
|
||||
/ actionsAPI documentation, unittests (caglar - meren)
|
||||
/ versioning information document
|
||||
/ verify metodlari
|
||||
+ SpecFile
|
||||
+ MetaData
|
||||
+ Files
|
||||
- Index (o kadar onemli degil)
|
||||
? non-interactive use (baris)
|
||||
+ implement missing unit tests
|
||||
+ sourcedb
|
||||
+ package (baris)
|
||||
+ configuration file (baris)
|
||||
+ upgrade (eray)
|
||||
+ upgrade operation
|
||||
+ test upgrade op
|
||||
+ system-wide upgrade (upgrade-all) komutu
|
||||
+ incremental build (eray)
|
||||
+ generate binary release number by comparing MD5s (eray)
|
||||
+ keep track of successfully completed configure, make, install
|
||||
steps (necessary for large-scale builds?) (meren)
|
||||
+ database locking bugs (eray)
|
||||
+ provide a library interface for users outside (YALI, TASMA). "import pisi" (baris)
|
||||
|
||||
4. Release (bug fix, guzellestirme vs.)
|
||||
|
||||
5. Post Release
|
||||
|
||||
- a database of components: faster access
|
||||
- more support for categories:
|
||||
- put categories into a database
|
||||
- fast query for categories
|
||||
/ make a package and sourcedb for each repo
|
||||
+ packagedb
|
||||
- sourceb
|
||||
- transaction stuff for database (eray)
|
||||
- multi-architecture support (baris, caglar)
|
||||
/ design decisions
|
||||
/ extend XML specs to support that
|
||||
- cross-platform building support
|
||||
- incremental build/fetch for repository index (pisi-index.xml)
|
||||
- diffsets (caglar)
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
% ALGORITHM STYLE -- Released 8 April 1996
|
||||
% for LaTeX-2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail Peter.Williams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithm}
|
||||
\typeout{Document Style `algorithm' - floating environment}
|
||||
|
||||
\RequirePackage{float}
|
||||
\RequirePackage{ifthen}
|
||||
\newcommand{\ALG@within}{nothing}
|
||||
\newboolean{ALG@within}
|
||||
\setboolean{ALG@within}{false}
|
||||
\newcommand{\ALG@floatstyle}{ruled}
|
||||
\newcommand{\ALG@name}{Algorithm}
|
||||
\newcommand{\listalgorithmname}{List of \ALG@name s}
|
||||
|
||||
% Declare Options
|
||||
% first appearance
|
||||
\DeclareOption{plain}{
|
||||
\renewcommand{\ALG@floatstyle}{plain}
|
||||
}
|
||||
\DeclareOption{ruled}{
|
||||
\renewcommand{\ALG@floatstyle}{ruled}
|
||||
}
|
||||
\DeclareOption{boxed}{
|
||||
\renewcommand{\ALG@floatstyle}{boxed}
|
||||
}
|
||||
% then numbering convention
|
||||
\DeclareOption{part}{
|
||||
\renewcommand{\ALG@within}{part}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{chapter}{
|
||||
\renewcommand{\ALG@within}{chapter}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{section}{
|
||||
\renewcommand{\ALG@within}{section}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsection}{
|
||||
\renewcommand{\ALG@within}{subsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsubsection}{
|
||||
\renewcommand{\ALG@within}{subsubsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{nothing}{
|
||||
\renewcommand{\ALG@within}{nothing}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption*{\edef\ALG@name{\CurrentOption}}
|
||||
|
||||
% ALGORITHM
|
||||
%
|
||||
\ProcessOptions
|
||||
\floatstyle{\ALG@floatstyle}
|
||||
\ifthenelse{\boolean{ALG@within}}{
|
||||
\ifthenelse{\equal{\ALG@within}{part}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[part]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{chapter}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[chapter]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{section}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[section]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsubsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsubsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{nothing}}
|
||||
{\newfloat{algorithm}{htbp}{loa}}{}
|
||||
}{
|
||||
\newfloat{algorithm}{htbp}{loa}
|
||||
}
|
||||
\floatname{algorithm}{\ALG@name}
|
||||
|
||||
\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
% ALGORITHMIC STYLE -- Released 8 APRIL 1996
|
||||
% for LaTeX version 2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail PeterWilliams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithmic}
|
||||
\typeout{Document Style `algorithmic' - environment}
|
||||
%
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\newboolean{ALC@noend}
|
||||
\setboolean{ALC@noend}{false}
|
||||
\newcounter{ALC@line}
|
||||
\newcounter{ALC@rem}
|
||||
\newlength{\ALC@tlm}
|
||||
%
|
||||
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
|
||||
%
|
||||
\ProcessOptions
|
||||
%
|
||||
% ALGORITHMIC
|
||||
\newcommand{\algorithmicrequire}{\textbf{Require:}}
|
||||
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
|
||||
\newcommand{\algorithmiccomment}[1]{\{#1\}}
|
||||
\newcommand{\algorithmicend}{\textbf{end}}
|
||||
\newcommand{\algorithmicif}{\textbf{if}}
|
||||
\newcommand{\algorithmicthen}{\textbf{then}}
|
||||
\newcommand{\algorithmicelse}{\textbf{else}}
|
||||
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
|
||||
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
|
||||
\newcommand{\algorithmicfor}{\textbf{for}}
|
||||
\newcommand{\algorithmicforall}{\textbf{for all}}
|
||||
\newcommand{\algorithmicdo}{\textbf{do}}
|
||||
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
|
||||
\newcommand{\algorithmicwhile}{\textbf{while}}
|
||||
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
|
||||
\newcommand{\algorithmicloop}{\textbf{loop}}
|
||||
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
|
||||
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
|
||||
\newcommand{\algorithmicuntil}{\textbf{until}}
|
||||
\def\ALC@item[#1]{%
|
||||
\if@noparitem \@donoparitem
|
||||
\else \if@inlabel \indent \par \fi
|
||||
\ifhmode \unskip\unskip \par \fi
|
||||
\if@newlist \if@nobreak \@nbitem \else
|
||||
\addpenalty\@beginparpenalty
|
||||
\addvspace\@topsep \addvspace{-\parskip}\fi
|
||||
\else \addpenalty\@itempenalty \addvspace\itemsep
|
||||
\fi
|
||||
\global\@inlabeltrue
|
||||
\fi
|
||||
\everypar{\global\@minipagefalse\global\@newlistfalse
|
||||
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
|
||||
\penalty\z@ \fi
|
||||
\everypar{}}\global\@nobreakfalse
|
||||
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
|
||||
\sbox\@tempboxa{\makelabel{#1}}%
|
||||
\global\setbox\@labels
|
||||
\hbox{\unhbox\@labels \hskip \itemindent
|
||||
\hskip -\labelwidth \hskip -\ALC@tlm
|
||||
\ifdim \wd\@tempboxa >\labelwidth
|
||||
\box\@tempboxa
|
||||
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
|
||||
\hskip \ALC@tlm}\ignorespaces}
|
||||
%
|
||||
\newenvironment{algorithmic}[1][0]{
|
||||
\let\@item\ALC@item
|
||||
\newcommand{\ALC@lno}{%
|
||||
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
|
||||
{{\footnotesize \arabic{ALC@line}:}}{}%
|
||||
}
|
||||
\let\@listii\@listi
|
||||
\let\@listiii\@listi
|
||||
\let\@listiv\@listi
|
||||
\let\@listv\@listi
|
||||
\let\@listvi\@listi
|
||||
\let\@listvii\@listi
|
||||
\newenvironment{ALC@g}{
|
||||
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
|
||||
\listparindent\z@ \rightmargin\z@
|
||||
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
|
||||
\leftmargin 1em
|
||||
\addtolength{\ALC@tlm}{\leftmargin}
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
\newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
|
||||
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
|
||||
{}{\ \algorithmiccomment{##1}}}
|
||||
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
|
||||
\newcommand{\ENSURE}{\item[\algorithmicensure]}
|
||||
\newcommand{\STATE}{\ALC@it}
|
||||
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
|
||||
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\renewcommand{\\}{\@centercr}
|
||||
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSIF}[2][default]%
|
||||
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@whl}}
|
||||
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
|
||||
\ALC@com{##1}\begin{ALC@loop}}
|
||||
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
|
||||
\ALC@com{##1}\begin{ALC@rpt}}
|
||||
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
|
||||
\ifthenelse{\boolean{ALC@noend}}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}}
|
||||
}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
|
||||
}
|
||||
\renewcommand{\@toodeep}{}
|
||||
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
|
||||
\itemsep\z@ \itemindent\z@ \listparindent\z@%
|
||||
\partopsep\z@ \parskip\z@ \parsep\z@%
|
||||
\labelsep 0.5em \topsep 0.2em%
|
||||
\ifthenelse{\equal{#1}{0}}
|
||||
{\labelwidth 0.5em }
|
||||
{\labelwidth 1.2em }
|
||||
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
|
||||
\ALC@tlm\labelsep
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: t
|
||||
%%% End:
|
||||
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{algorithm}
|
||||
\usepackage{algorithmic}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amstext}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amsbsy}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{prettyref}
|
||||
%\newrefformat{alg}{Algorithm~\ref{#1}}
|
||||
%\newrefformat{eq}{Equation~\ref{#1}}
|
||||
%\newrefformat{lem}{Lemma~\ref{#1}}
|
||||
%\newrefformat{thm}{Theorem~\ref{#1}}
|
||||
%\newrefformat{chp}{Chapter~\ref{#1}}
|
||||
%\newrefformat{sec}{Section~\ref{#1}}
|
||||
%\newrefformat{apx}{Appendix~\ref{#1}}
|
||||
%\newrefformat{tab}{Table~\ref{#1}}
|
||||
%\newrefformat{fig}{Figure~\ref{#1}}
|
||||
%usepackage[active]{srcltx}
|
||||
\title{ Dependency Resolution in PISI}
|
||||
|
||||
\author{Eray \"{O}zkural}
|
||||
|
||||
\date{\today}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
Dependency resolution in package management systems have a
|
||||
significance in that they are the key to providing system stability
|
||||
and internet upgrades. The scale of package databases requires the
|
||||
dependency resolution mechanism to be efficient and correct,
|
||||
motivating a closer look at the theory.
|
||||
|
||||
|
||||
\section{Review}
|
||||
|
||||
Dependency resolution has been taken in the most general setting as
|
||||
the famous SAT problem of propositional logic. If we consider a system
|
||||
$D$ of dependency statements $D_i$, each statement can be taken as a
|
||||
proposition in propositional logic which states, for instance:
|
||||
|
||||
$D_i$: if package $a$ is installed or package $b$ is installed, then
|
||||
package $i$ is installable.\\
|
||||
...
|
||||
|
||||
The system is thus understood as the conjunction of such facts, giving
|
||||
us a logical programming formulation to determine installation
|
||||
conditions. Note that for simplicity we do not consider the nuances in
|
||||
upgrade and remove operations at the moment.
|
||||
|
||||
However, using a SAT solver for this operation may be shooting a fly
|
||||
with a bazooka. We observe that only certain forms of propositions
|
||||
will be necessary for a dependency system. Furthermore, as we shall
|
||||
see further constraints and optimizations may be required of the
|
||||
system that are not modelled well with the SAT problem.
|
||||
|
||||
We use a graph theoretic approach instead. A directed graph (digraph)
|
||||
$G=(V,E)$ is formally a set of vertices $V$ and a set of edges $E$
|
||||
where each edge $(u,v)$ represents an edge from a vertex to another.
|
||||
Accessor functions $V(G)$ and $E(G)$ yield the vertex and edge set of
|
||||
the graph $G$. Topological sort of a graph gives a total ordering of
|
||||
the vertices in which there are only forward edges. A vertex induced
|
||||
subgraph of $G$ by vertex set $A$ contains only the vertex set $A$ and
|
||||
edges incident to members of $A$.
|
||||
|
||||
\section{Package operation planning}
|
||||
|
||||
The dependency resolution problem may be viewed as a simple forward
|
||||
chaining problem, where we would like to begin from an initial state
|
||||
$S_0$ and by following allowable system transitions $t_i: S \to S$,
|
||||
arrive at a desired system state $S_f$ (where $S$ is the set of all states).
|
||||
|
||||
A system state $S_i$ is defined as the set of installed packages on
|
||||
the system together with their versions, i.e. $S_i = \{ (x,v) : x
|
||||
\text{ is installed}, v=version(x)\} $. An atomic system transition
|
||||
$t_i$ chains one system state into another, making one ACID change on
|
||||
the system. The usual atomic transitions are the single package
|
||||
install, remove and reinstall (upgrade or downgrade) operations found
|
||||
in low-level package management code of PISI. Note that in PISI, an
|
||||
upgrade operation is identical to a remove operation followed by an
|
||||
install operation (which sets it apart from some other packaging
|
||||
systems).
|
||||
|
||||
A package operation plan is thus naturally conceived of as a sequence
|
||||
of atomic system transitions. Given an initial state and a final
|
||||
state, the job of the package operation planner is to determine
|
||||
whether there is a plan, and if so find the "best" one.
|
||||
|
||||
Where there are no versions involved (e.g. upgrade/downgrade), we will
|
||||
replace the pair $(x,v)$ with $x$ in the definitions for simplicity.
|
||||
|
||||
\subsection{System consistency}
|
||||
|
||||
It is worth mentioning here the concept of system consistency. As in a
|
||||
database transaction, it is not acceptable that the system violates an
|
||||
invariant afterwards. In the context of PISI, system consistency is
|
||||
composed of two conditions for the current set of installed packages.
|
||||
\begin{enumerate}
|
||||
\item All package dependencies are satisfied (we may call this a
|
||||
closed system)
|
||||
\item No package conflicts are present.
|
||||
\end{enumerate}
|
||||
|
||||
Therefore, by atomic transition we also mean one that does not corrupt
|
||||
system consistency. The system is thus never in an inconsistent
|
||||
state. We will explain the conflicts later, for the present let us
|
||||
look at the dependency condition.
|
||||
|
||||
\subsection{Solving the simplest case with topological sorting}
|
||||
|
||||
We will now concentrate on a simple form of the problem which can be
|
||||
solved with topological sorting. This form is not concerned with
|
||||
versions. Neither do we consider remote repositories. From an initial
|
||||
set of packages $S_0$, we would like to install in addition a new set
|
||||
$A$ of packages obtaining $S_f = S_0 \cup A$, for a static set of package
|
||||
relations.
|
||||
|
||||
The only relations considered are of the form: $a$ Depends on $b$, or
|
||||
more briefly $aDb$. The graph of all such simple dependency relations
|
||||
is a digraph $G$. For each dependency relation $aDb$, there is an
|
||||
edge $a \to b$ in $G$. Accessing graph $G$ usually requires a database
|
||||
operation and is therefore expensive.
|
||||
|
||||
We now consider the digraph $G_A$ of the minimal set of simple
|
||||
dependency relations which contains all information required to
|
||||
construct a plan to install packages $A$. $G_A$ is a vertex induced
|
||||
graph such that the fringe of $A$, e.g. vertices with out-degree $0$
|
||||
depend only on packages that are already installed (or none). Vertices
|
||||
of $G_A$ are taken from $S_f$. First, let us explain the labelling
|
||||
scheme. Already installed vertices are labelled with 'i'. Packages to
|
||||
be added are labelled with 'a', and packages to be installed due to
|
||||
dependencies are labelled with 'd'. We construct the graph as follows
|
||||
\begin{algorithm}
|
||||
\caption{$\textsc{Make-}G_A(G, A)$}
|
||||
\label{alg:cons-graph}
|
||||
\begin{algorithmic}[1]
|
||||
\STATE $G_A \gets$ vertex induced subgraph of $G$ by $A$ labelled with 'a'
|
||||
\REPEAT
|
||||
\STATE done $\gets$ true
|
||||
\FOR{each $u \in V(G_A)$ with out-degree $0$}
|
||||
\FOR{ $v \in adj(u) $ of $G$}
|
||||
\IF{$v \notin V(G_A)$}
|
||||
\STATE done $\gets$ false
|
||||
\IF{$v$ is installed}
|
||||
\STATE label $v$ with 'i'
|
||||
\ELSE
|
||||
\STATE label $v$ with 'd'
|
||||
\ENDIF
|
||||
\STATE add $(u,v)$ to $G_A$
|
||||
\ENDIF
|
||||
\ENDFOR
|
||||
\ENDFOR
|
||||
\UNTIL{done}
|
||||
\end{algorithmic}
|
||||
\end{algorithm}
|
||||
|
||||
By this iterative expansion, we do a minimum number of database
|
||||
accesses to $G$ and construct a dependency graph in memory. If the
|
||||
$G_A$'s fringe has vertices with non 'i'-labels, then $A$ cannot be
|
||||
installed. Otherwise, we find a topological sort $L$ of $G_A$, and in
|
||||
the reverse order, install packages for vertices labelled with
|
||||
'a' or 'd'. Observe that, by definition of a topological sort,
|
||||
installing packages in the reverse order of a topological sort
|
||||
guarantees that no package is installed before all of its dependencies
|
||||
are installed. Thus, this yields a consistency-preserving plan.
|
||||
|
||||
\subsection{Dependency conditions}
|
||||
|
||||
In the PISI specification, we allow a dependency to specify a local
|
||||
condition, for instance a program may require a dependency on
|
||||
\texttt{libx} with pardus source release $3$ or greater. Another
|
||||
program may require a dependency on a particular source release. These
|
||||
conditions are local because they can be computed over the elements of
|
||||
system state $S_i$, e.g. package (name, version) pairs. Let us denote this
|
||||
condition by a predicate $P(b)$ such that $aDb$ iff $P(b)$. The
|
||||
predicate $P$ for the dependency $aDb$ can be stored as edge data for
|
||||
$(u,v)$ on the graph.
|
||||
% thus when we say $P(u,v)$, this means the predicate stored on
|
||||
%$(u,v)$ edge for vertex $v$.
|
||||
|
||||
In this case, the vertices of the package dependency graph $G$ and the
|
||||
planning graph $G_A$ retain the version information along with the
|
||||
package name. The dependency relation thus holds between two pairs
|
||||
$(p_1,v_1)$ and $(p_2,v_2)$, satisfying a given predicate
|
||||
$P(p_2,v_2)$. When constructing the graph, we therefore take this
|
||||
predicate into account and admit a new edge $(u,v)$, and thus a new
|
||||
vertex $v$ into $G_A$ if and only if the target vertex satisfies
|
||||
$P(v)$.
|
||||
|
||||
\subsection{Conflicts and COMAR dependencies}
|
||||
|
||||
The tags \texttt{Conflicts} and \texttt{Provides} in PISI are
|
||||
inherited from Debian distribution. A conflict between two packages
|
||||
($a$ conflicts with $b$) is a symmetric relation that prevents the
|
||||
packages $a,b$ from being installed simultaneously (It is
|
||||
sufficient that only one direction of the relation is declared, the
|
||||
other direction is inferred). Provision in the form of $a$ provides $A$
|
||||
denotes that $a$ implements a virtual package abstraction $A$.
|
||||
|
||||
In PISI, a package can provide an object of a COMAR Object Model (OM),
|
||||
and is currently the only model of a ``virtual package''. In the
|
||||
following example, let $a_1,a_2,\ldots,a_n$ provide the OM $A$. A package
|
||||
can depend on another package's OM, for instance $b$ comar-depends on
|
||||
$A$ (or in short form $bDA$) (Currently, conditions on virtual
|
||||
dependencies are not supported). In this case, it is sufficient that
|
||||
only one of the $a_i$ are installed. To resolve this, the user is
|
||||
asked to choose from a list of alternatives immediately, since
|
||||
otherwise there is unavoidable combinatorial explosion (in the form of
|
||||
having to consider $\Pi_{bDA}num(A)$ graphs in the worst case where
|
||||
$num(A)$ is the number of alternatives for comar OM $A$; the problem
|
||||
is that there seems to be no simple solution to solve satisfiability
|
||||
with arbitrary disjunctions in package dependency, short of a $SAT$
|
||||
solver).
|
||||
|
||||
The resolution of conflicts to maintain system consistency condition
|
||||
$2$ is easier to achieve. This can be satisfied by disallowing
|
||||
installation of a package that would violate the condition, or
|
||||
removing currently installed packages which conflict with the newly
|
||||
installed package and its dependencies. In most package managers, the
|
||||
second option is confirmed by the user for making it easier. In the
|
||||
install operation, after constructing the partial dependency graph
|
||||
$G_A$, we merely have to check whether any conflict appears among the
|
||||
vertices of $G_A$. If so, then the operation is untenable, since $G_A$
|
||||
shows the future state of the installed system. Since a conflict is
|
||||
symmetric, it is represented as a bidirectional edge $a \leftrightarrow b$. To
|
||||
distinguish dependencies from conflicts, the edges would have to be
|
||||
labelled in this case, for instance with 'd' and 'c'. The removal
|
||||
option can be implemented by invoking a multi-package remove operation
|
||||
on the packages in conflict.
|
||||
|
||||
\subsection{Remove operation}
|
||||
|
||||
Dependency resolution for remove operation is similar to install. The
|
||||
only significant difference is that we remove the packages in the
|
||||
topological order rather than installing packages in the reverse
|
||||
topological order.
|
||||
|
||||
\section{Remote repositories and upgrade operation}
|
||||
|
||||
|
||||
The upgrade operation is more complicated. First of all, the system
|
||||
has to distinguish between the current relation graph (e.g.
|
||||
dependencies and conflicts), and the future relation graph which may
|
||||
be different in rather important aspects. In theory, we allow any
|
||||
dependency and conflict to change. Therefore, we have a $G_0$ which
|
||||
represents the current relations (among installed packages) in the
|
||||
system, and a $G_f$ which is probably taken from a remote package
|
||||
repository. We begin by noting that $G_0$ and $G_f$ have to be
|
||||
compatible. That is, to say, if a package $(p_1,v_1)$ is shared across
|
||||
two graphs, then the declarations made by the package are one and the
|
||||
same.
|
||||
|
||||
$G_0$ can be calculated from the package information (e.g. metadata)
|
||||
of the installed packages and is stored by PISI in a dedicated
|
||||
database. $G_f$ is most likely constructed from a PISI Index file
|
||||
corresponding to a particular package repository. Accessing both of
|
||||
these entities is expensive and we should take care to minimize access
|
||||
as in the previous section.
|
||||
|
||||
To preserve consistency during individual transitions, the planner can
|
||||
choose to remove a minimum number of packages from the system to bring
|
||||
it to a clean state, and then install the new versions of these
|
||||
packages in the correct order. Let us assume that it is indeed
|
||||
possible to achieve this ``clean state''. Apparently, this is not
|
||||
always possible because other packages may depend on the package(s) to
|
||||
upgrade. At any rate, to achieve this, first we need to
|
||||
calculate subgraphs of $G_0$ and $G_f$. We can calculate alternative
|
||||
plans from these subgraphs if need be.
|
||||
|
||||
Let $A$ be the set of packages to be upgraded from a given repository.
|
||||
$G_{A,0}$ is the subgraph of $G_{0}$ induced by the ``upgrade
|
||||
closure'' of $A$. The ``upgrade closure'' of a set $A$ of packages is
|
||||
defined as a minimal set of packages $B \supseteq A$ such that there is no
|
||||
package in $B$ that requires an upgrade for $A$ to be upgraded. This
|
||||
is found by assuming that the current system state $S_0$ is
|
||||
consistent, and by constructing a relation graph of the future state
|
||||
of the system to detect the dependencies that have changed.
|
||||
|
||||
Obviously, to make a plan, we must first know the goal state. In a
|
||||
multi-package upgrade, the exact details of the goal state depend on
|
||||
the graph $G_f$ of the repository. Thus, we construct a graph
|
||||
$G_{A,f}$ that is a vertex-induced subgraph of $G_f$ such that it
|
||||
contains all information relevant to upgrading packages $A$. We begin
|
||||
by a vertex induced subgraph of $G_f$ by $A$. These are the packages
|
||||
that will be upgraded in any case. Then, we make a pass on the
|
||||
vertices, and look at all the outgoing edges, we compare whether this
|
||||
edge has changed in any substantial way from the previous version. In
|
||||
particular, we are interested in whether the predicate of the edge is
|
||||
valid for the version of the same package in our current system. Every
|
||||
compared vertex in this manner is marked done, and the edges not valid
|
||||
for the current system pull new unmarked vertices into $G_f$, this
|
||||
continues until there are no unmarked vertices left. Hence, the
|
||||
vertices of $G_f$ are the packages that must be upgraded.
|
||||
|
||||
To actually carry out the upgrade a strategy is to upgrade all the
|
||||
packages in $G_{A,f}$ in some order. A good order is again the reverse
|
||||
topological order order, in fact, the upgrade operation is merely a
|
||||
special case of a multi-package installation code that can install
|
||||
from a remote repository, since a multi-package installation can
|
||||
contain upgrades in addition to new packages. However, in case no
|
||||
package depends on the packages to be upgraded, then we can carry out
|
||||
a completely consistency-preserving plan as discussed above. The
|
||||
conflicts are resolved in the usual fashion, by removing those
|
||||
packages in conflict with new packages that are installed. This can be
|
||||
accomplished by invoking a remove operation prior to the upgrade
|
||||
operation.
|
||||
|
||||
\section{Examples}
|
||||
|
||||
\subsection{A single package upgrade}
|
||||
|
||||
goal: upgrade $(a,1)$ to $(a,2)$\\
|
||||
\\
|
||||
rules:\\
|
||||
$(a,1)$ depends on $(b,1), (c,1)$ \\
|
||||
$(a,1)$ conflicts with $(d,1)$\\
|
||||
$(a,2)$ depends on $(c,3), (d,2)$\\
|
||||
$(a,2)$ conflicts with $(b,1)$\\
|
||||
\\
|
||||
initial state:\\
|
||||
$(a,1), (b,1), (c,1)$ installed \\
|
||||
|
||||
In this case, we can find a consistency-preserving plan in terms of
|
||||
install and remove operations.
|
||||
\\
|
||||
plan:\\
|
||||
remove $(a,1)$\\
|
||||
remove $(b,1)$\\
|
||||
remove $(c,1)$\\
|
||||
install $(c,3)$\\
|
||||
install $(d,2)$\\
|
||||
install $(a,2)$\\
|
||||
|
||||
\subsection{Another upgrade}
|
||||
|
||||
goal: upgrade $(b,1) \to (b,2)$\\
|
||||
\\
|
||||
current dep: $(a,1) \to[=1] (b,1) \to[=1] (c,1) \to[=1] (d,1)$\\
|
||||
repo dep: \nobreakspace{} \nobreakspace$(a,1) \to[=2] (b,2) \to[=2] (c,2) \to[=1] (d,1)$\\
|
||||
|
||||
In this case, we cannot remove $(b,1)$ because it's locked in the
|
||||
chain. In fact, here there is no consistency-preserving plan in terms
|
||||
of atomic single package transitions: install, remove, upgrade. In
|
||||
these cases, it seems best to resort to upgrade in place, and in the
|
||||
reverse topological order of dependencies.
|
||||
\\
|
||||
plan:\\
|
||||
upgrade $(c,1) \to (c,2)$\\
|
||||
upgrade $(b,1) \to (b,2)$\\
|
||||
|
||||
|
||||
\subsection{A multi package remove}
|
||||
|
||||
goal: remove $(a,2), (b,3), (c,2)$\\
|
||||
\\
|
||||
rules:\\
|
||||
$(c,2)$ depends $(a,2)$\\
|
||||
$(d,2)$ depends on $(b,3), (c,2)$\\
|
||||
$(e,1)$ depends on $(a,2)$\\
|
||||
$(f,2)$ depends on $(e,1)$\\
|
||||
$(g,2)$ depends on $(e,1)$\\\\
|
||||
plan:\\
|
||||
remove $(f,2)$\\
|
||||
remove $(g,2)$\\
|
||||
remove $(e,1)$\\
|
||||
remove $(d,2)$\\
|
||||
remove $(b,3)$\\
|
||||
remove $(c,2)$\\
|
||||
remove $(a,2)$\\
|
||||
\end{document}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 71 KiB |
@@ -1,37 +0,0 @@
|
||||
%%
|
||||
%% This is file `prettyref.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% prettyref.dtx (with options: `style')
|
||||
%%
|
||||
%% Copyright (c) 1995 Kevin Ruland
|
||||
%%
|
||||
%%
|
||||
%% prettyref v3.0
|
||||
%%
|
||||
%% Copyright 1995,1998. by Kevin Ruland kevin@rodin.wustl.edu
|
||||
%%
|
||||
\ProvidesPackage{prettyref}[1998/07/09 v3.0]
|
||||
\def\newrefformat#1#2{%
|
||||
\@namedef{pr@#1}##1{#2}}
|
||||
\newrefformat{eq}{\textup{(\ref{#1})}}
|
||||
\newrefformat{lem}{Lemma \ref{#1}}
|
||||
\newrefformat{thm}{Theorem \ref{#1}}
|
||||
\newrefformat{cha}{Chapter \ref{#1}}
|
||||
\newrefformat{sec}{Section \ref{#1}}
|
||||
\newrefformat{tab}{Table \ref{#1} on page \pageref{#1}}
|
||||
\newrefformat{fig}{Figure \ref{#1} on page \pageref{#1}}
|
||||
\def\prettyref#1{\@prettyref#1:}
|
||||
\def\@prettyref#1:#2:{%
|
||||
\expandafter\ifx\csname pr@#1\endcsname\relax%
|
||||
\PackageWarning{prettyref}{Reference format #1\space undefined}%
|
||||
\ref{#1:#2}%
|
||||
\else%
|
||||
\csname pr@#1\endcsname{#1:#2}%
|
||||
\fi%
|
||||
}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `prettyref.sty'.
|
||||
@@ -1,54 +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 gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
from pisi.cli.pisicli import PisiCLI
|
||||
|
||||
def handle_exception(exception, value, tb):
|
||||
import traceback
|
||||
import exceptions
|
||||
|
||||
import pisi.ui
|
||||
from pisi.xmlext import XmlError
|
||||
|
||||
ui = pisi.cli.CLI()
|
||||
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("\nKeyboardInterrupt: Exiting...\n"))
|
||||
sys.exit(1)
|
||||
elif exception == XmlError:
|
||||
ui.error(str(value))
|
||||
sys.exit(1)
|
||||
|
||||
ui.error(_("""
|
||||
Internal PISI Error:
|
||||
Please file a bug report! (http://bugs.uludag.org.tr)
|
||||
|
||||
"""))
|
||||
|
||||
ui.error("%s" %exception)
|
||||
ui.error("%s\n" %value)
|
||||
ui.info(_("Traceback:"))
|
||||
traceback.print_tb(tb)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
cli = PisiCLI()
|
||||
cli.run_command()
|
||||
@@ -1,351 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
|
||||
<!-- Project file for project pisi -->
|
||||
<!-- Saved: 2005-09-20, 22:00:02 -->
|
||||
<!-- Copyright (C) 2005 PiSi Development Team, -->
|
||||
<Project version="3.8">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
<UIType>Qt</UIType>
|
||||
<Description>The package management software of Pardus distribution.
|
||||
</Description>
|
||||
<Author>PiSi Development Team</Author>
|
||||
<Email></Email>
|
||||
<Sources>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>ebuild2pisi.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>cat-db.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>popt</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>zip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>unzip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>configfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>sourcedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>utiltests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>installdbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>run.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>metadatatests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>constantstests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>fetchertests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>archivetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>versiontests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>actionsapitests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>specfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>graphtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>get.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>shelltools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>variables.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>autotools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>kde.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>libtools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>coreutils.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>pisicli.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>commands.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>lockeddbshelve.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>specfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>package.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>build.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>xmlfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>packagedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>config.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>index.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>repodb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>ui.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>util.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>dependency.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>metadata.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>pgraph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>operations.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>context.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcefetcher.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>graph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcearchive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>configfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>install.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>installdb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>constants.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>fetcher.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>archive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>xmlext.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>version.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>setup.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitoolsfunctions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>repostats.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>uri.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>api.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>xmlfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>specfilenew.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>TODO</Name>
|
||||
</Source>
|
||||
</Sources>
|
||||
<Forms>
|
||||
</Forms>
|
||||
<Translations>
|
||||
</Translations>
|
||||
<Interfaces>
|
||||
</Interfaces>
|
||||
<Others>
|
||||
<Other>
|
||||
<Name>pisi-cli</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta-light.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta-upgrade.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>sandbox</Name>
|
||||
</Other>
|
||||
</Others>
|
||||
<MainScript>
|
||||
<Name>pisi-cli</Name>
|
||||
</MainScript>
|
||||
<Vcs>
|
||||
<VcsType>Subversion</VcsType>
|
||||
<VcsOptions>{'status': [], 'log': [], 'global': [], 'update': [], 'remove': [], 'add': [], 'tag': [], 'export': [], 'diff': [], 'commit': [], 'checkout': [], 'history': []}</VcsOptions>
|
||||
<VcsOtherData>{'standardLayout': 1}</VcsOtherData>
|
||||
</Vcs>
|
||||
<FiletypeAssociations>
|
||||
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.ui" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
|
||||
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
|
||||
<FiletypeAssociation pattern="*.py" type="SOURCES" />
|
||||
</FiletypeAssociations>
|
||||
</Project>
|
||||
@@ -1,26 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PiSi version
|
||||
|
||||
__version__ = "1.0_alpha2"
|
||||
|
||||
__all__ = [ 'api', 'packagedb', 'installdb' ]
|
||||
|
||||
class Error(Exception):
|
||||
"""Class of exceptions that lead to program termination"""
|
||||
pass
|
||||
|
||||
class Exception(Exception):
|
||||
"""Class of exceptions that must be caught and handled within PISI"""
|
||||
pass
|
||||
|
||||
# FIXME: can't do this due to name clashes in config and other singletons booo
|
||||
#from pisi.api import *
|
||||
@@ -1,11 +0,0 @@
|
||||
ReFactor ActionsAPI:
|
||||
|
||||
pisitools.py - %90
|
||||
autotools.py - %100
|
||||
get.py - %75
|
||||
kde.py - %75
|
||||
libtools.py - %90
|
||||
pisitoolsfunctions.py - %75
|
||||
shelltools.py - %75
|
||||
variables.py - %100
|
||||
coreutils.py - %100
|
||||
@@ -1,20 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# pisi.actionsapi version
|
||||
__version__ = '1.0_alpha2'
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
@@ -1,142 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
from pisi.actionsapi.libtools import gnuconfig_update
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
if can_access_file('config.log'):
|
||||
ctx.ui.error('\n!!! Please attach the config.log to your bug report:\n%s/config.log\n' % os.getcwd())
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
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...\n')
|
||||
else:
|
||||
raise ConfigureError('!!! No configure script found...\n')
|
||||
|
||||
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...\n')
|
||||
else:
|
||||
raise ConfigureError('!!! No configure script found...\n')
|
||||
|
||||
def compile(parameters = ''):
|
||||
#FIXME: Only one package uses this until now, hmmm
|
||||
system('%s %s %s' % (get.GCC(), get.CFLAGS(), parameters))
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters = "all" || "doc" etc.'''
|
||||
if system('make %s' % parameters):
|
||||
raise MakeError('!!! Make failed...\n')
|
||||
|
||||
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...\n')
|
||||
else:
|
||||
raise InstallError('!!! No Makefile found...\n')
|
||||
|
||||
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...\n')
|
||||
else:
|
||||
raise InstallError('!!! No Makefile found...\n')
|
||||
|
||||
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,77 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import sys
|
||||
import re
|
||||
from itertools import izip, imap, count, ifilter, ifilterfalse
|
||||
|
||||
# ActionsAPI
|
||||
import pisi.actionsapi
|
||||
|
||||
def cat(filename):
|
||||
return file(filename).xreadlines()
|
||||
|
||||
class grep:
|
||||
'''keep only lines that match the regexp'''
|
||||
def __init__(self, pat, flags = 0):
|
||||
self.fun = re.compile(pat, flags).match
|
||||
def __ror__(self, input):
|
||||
return ifilter(self.fun, input)
|
||||
|
||||
class tr:
|
||||
'''apply arbitrary transform to each sequence element'''
|
||||
def __init__(self, transform):
|
||||
self.tr = transform
|
||||
def __ror__(self, input):
|
||||
return imap(self.tr, input)
|
||||
|
||||
class printto:
|
||||
'''print sequence elements one per line'''
|
||||
def __init__(self, out = sys.stdout):
|
||||
self.out = out
|
||||
def __ror__(self,input):
|
||||
for line in input:
|
||||
print >> self.out, line
|
||||
|
||||
printlines = printto(sys.stdout)
|
||||
|
||||
class terminator:
|
||||
def __init__(self,method):
|
||||
self.process = method
|
||||
def __ror__(self,input):
|
||||
return self.process(input)
|
||||
|
||||
aslist = terminator(list)
|
||||
asdict = terminator(dict)
|
||||
astuple = terminator(tuple)
|
||||
join = terminator(''.join)
|
||||
enum = terminator(enumerate)
|
||||
|
||||
class sort:
|
||||
def __ror__(self,input):
|
||||
ll = list(input)
|
||||
ll.sort()
|
||||
return ll
|
||||
sort = sort()
|
||||
|
||||
class uniq:
|
||||
def __ror__(self,input):
|
||||
for i in input:
|
||||
try:
|
||||
if i == prev:
|
||||
continue
|
||||
except NameError:
|
||||
pass
|
||||
prev = i
|
||||
yield i
|
||||
uniq = uniq()
|
||||
@@ -1,155 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
|
||||
# PISI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
from variables import glb
|
||||
|
||||
class BinutilsError(pisi.actionsapi.Error):
|
||||
pass
|
||||
|
||||
env = glb.env
|
||||
dirs = glb.dirs
|
||||
|
||||
def curDIR():
|
||||
return os.getcwd()
|
||||
|
||||
def ENV(environ):
|
||||
return os.environ[environ];
|
||||
|
||||
# variables.Env
|
||||
|
||||
def pkgDIR():
|
||||
return env.pkg_dir
|
||||
|
||||
def workDIR():
|
||||
return env.work_dir
|
||||
|
||||
def installDIR():
|
||||
return env.install_dir
|
||||
|
||||
def srcNAME():
|
||||
return env.src_name
|
||||
|
||||
def srcVERSION():
|
||||
return env.src_version
|
||||
|
||||
def srcRELEASE():
|
||||
return env.src_release
|
||||
|
||||
def srcTAG():
|
||||
return env.src_name + '-' + env.src_version + '-' + env.src_release
|
||||
|
||||
def srcDIR():
|
||||
return env.src_name + '-' + env.src_version
|
||||
|
||||
def HOST():
|
||||
return env.host
|
||||
|
||||
def CFLAGS():
|
||||
return env.cflags
|
||||
|
||||
def CXXFLAGS():
|
||||
return env.cxxflags
|
||||
|
||||
def LDFLAGS():
|
||||
return env.ldflags
|
||||
|
||||
# variables.Dirs
|
||||
|
||||
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 exists_binary(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 exists_binary(cross_build_name):
|
||||
if not exists_binary(util):
|
||||
raise BinutilsError('util %s cannot be found' % util)
|
||||
else:
|
||||
ctx.ui.debug('Warning: %s does not exist, using plain name %s' \
|
||||
% (cross_build_name, util))
|
||||
return util
|
||||
else:
|
||||
return cross_build_name
|
||||
|
||||
def AR():
|
||||
return getBinutilsInfo('ar')
|
||||
|
||||
def AS():
|
||||
return getBinutilsInfo('as')
|
||||
|
||||
def CC():
|
||||
return getBinutilsInfo('gcc')
|
||||
|
||||
def CXX():
|
||||
return getBinutilsInfo('g++')
|
||||
|
||||
def LD():
|
||||
return getBinutilsInfo('ld')
|
||||
|
||||
def NM():
|
||||
return getBinutilsInfo('nm')
|
||||
|
||||
def RANLIB():
|
||||
return getBinutilsInfo('ranlib')
|
||||
|
||||
def F77():
|
||||
return getBinutilsInfo('f77')
|
||||
|
||||
def GCJ():
|
||||
return getBinutilsInfo('gcj')
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
# 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
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
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, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
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 \
|
||||
--enable-final \
|
||||
--disable-dependency-tracking \
|
||||
--disable-debug \
|
||||
%s' % (get.kdeDIR(), get.HOST(), get.qtDIR(), get.qtLIBDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise ConfigureError('!!! Configure failed...\n')
|
||||
else:
|
||||
raise ConfigureError('!!! No configure script found...\n')
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters = "all" || "doc" etc.'''
|
||||
if system('make %s' % parameters):
|
||||
raise MakeError('!!! Make failed...\n')
|
||||
|
||||
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...\n')
|
||||
else:
|
||||
raise InstallError('!!! No Makefile found...\n')
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
import pisi.actionsapi.get as get
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
def preplib(sourceDirectory = '/usr/lib'):
|
||||
sourceDirectory = get.installDIR() + sourceDirectory
|
||||
if can_access_directory(sourceDirectory):
|
||||
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
|
||||
raise RunTimeError('!!! Running ldconfig failed...')
|
||||
|
||||
def preplib_so(sourceDirectory):
|
||||
pass
|
||||
|
||||
def gnuconfig_update():
|
||||
''' copy newest config.* onto source\'s '''
|
||||
for root, dirs, files in os.walk(os.getcwd()):
|
||||
for file in files:
|
||||
if file in ['config.sub', 'config.guess']:
|
||||
copy('/usr/share/gnuconfig/%s' % file, os.path.join(root, file))
|
||||
ctx.ui.info('GNU Config Update Finished.')
|
||||
|
||||
def libtoolize(parameters = ''):
|
||||
if system('/usr/bin/libtoolize %s' % parameters):
|
||||
raise RunTimeError('Running libtoolize failed...')
|
||||
|
||||
def gen_usr_ldscript(dynamicLib):
|
||||
|
||||
makedirs('%s/usr/lib' % get.installDIR())
|
||||
|
||||
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
|
||||
content = '''
|
||||
/* GNU ld script
|
||||
Since Pardus has critical dynamic libraries
|
||||
in /lib, and the static versions in /usr/lib,
|
||||
we need to have a "fake" dynamic lib in /usr/lib,
|
||||
otherwise we run into linking problems.
|
||||
*/
|
||||
GROUP ( /lib/%s )
|
||||
''' % dynamicLib
|
||||
|
||||
destinationFile.write(content)
|
||||
destinationFile.close()
|
||||
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
|
||||
@@ -1,219 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
import fileinput
|
||||
import re
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.pisitoolsfunctions import *
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
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(get.installDIR() + destinationDirectory, sourceFile)
|
||||
|
||||
def dodir(destinationDirectory):
|
||||
'''creates a directory tree'''
|
||||
makedirs(get.installDIR() + destinationDirectory)
|
||||
|
||||
def dodoc(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
|
||||
readable_insinto(get.installDIR() + os.path.join('/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(get.installDIR() + destinationDirectory, sourceFile)
|
||||
|
||||
def dohard(sourceFile, destinationFile):
|
||||
'''creates hard link between sourceFile and destinationFile'''
|
||||
#FIXME: How can i use hard-links in Python?
|
||||
pass
|
||||
|
||||
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 = os.path.join(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' % (os.path.join(root, source), destionationDirectory))
|
||||
|
||||
def doinfo(*sourceFiles):
|
||||
'''inserts the into files in the list of files into /usr/share/info'''
|
||||
readable_insinto(os.path.join(get.installDIR(), get.infoDIR()), *sourceFiles)
|
||||
|
||||
def dojar():
|
||||
'''installs jar files into /usr/share/PACKAGE/lib, and adds to /usr/share/PACKAGE/classpath.env'''
|
||||
pass
|
||||
|
||||
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 = os.path.join(os.getcwd(), sourceFile)
|
||||
destinationDirectory = 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 = os.path.join(os.getcwd(), sourceFile)
|
||||
destinationDirectory = 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 = os.path.join(os.getcwd(), sourceFile)
|
||||
destinationDirectory = 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 = os.path.join(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:
|
||||
ctx.ui.error('\n!!! ActionsAPI [doman]: Wrong man page file...')
|
||||
|
||||
makedirs(manDIR + '/man%s' % pageDirectory)
|
||||
system('install -m0644 %s %s' % (source, 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(get.installDIR() + destination)
|
||||
|
||||
for file in glob.glob(get.installDIR() + sourceFile):
|
||||
if not destinationFile:
|
||||
move(file, get.installDIR() + os.path.join(destination, os.path.basename(file)))
|
||||
else:
|
||||
move(file, get.installDIR() + os.path.join(destination, destinationFile))
|
||||
|
||||
def dopython():
|
||||
'''FIXME: What the hell is this?'''
|
||||
pass
|
||||
|
||||
def dosed(sourceFile, findPattern, replacePattern = ''):
|
||||
'''replaces patterns in sourceFile'''
|
||||
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
|
||||
''' example call: pisitools.dosym("/etc/passwd", "caglar")'''
|
||||
''' example call: pisitools.dosym("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
|
||||
|
||||
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 doesn\'t exists or permission denied...')
|
||||
|
||||
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
|
||||
'''insert a executable file into /sbin or /usr/sbin'''
|
||||
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
|
||||
executable_insinto(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(get.installDIR() + os.path.dirname(destinationFile))
|
||||
|
||||
try:
|
||||
os.symlink(sourceFile, get.installDIR() + destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...')
|
||||
|
||||
def insinto(destinationDirectory, sourceFile, destinationFile = ''):
|
||||
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
|
||||
makedirs(get.installDIR() + destinationDirectory)
|
||||
|
||||
if not destinationFile:
|
||||
for file in glob.glob(sourceFile):
|
||||
if can_access_file(file):
|
||||
copy(file, get.installDIR() + os.path.join(destinationDirectory, os.path.basename(file)))
|
||||
else:
|
||||
copy(sourceFile, get.installDIR() + os.path.join(destinationDirectory, destinationFile))
|
||||
|
||||
def newdoc(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
|
||||
move(sourceFile, destinationFile)
|
||||
readable_insinto(os.path.join(get.installDIR(), 'usr/share/doc', get.srcTAG()), destinationFile)
|
||||
|
||||
def newman(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
|
||||
move(sourceFile, destinationFile)
|
||||
doman(destinationFile)
|
||||
|
||||
def remove(sourceFile):
|
||||
'''removes sourceFile'''
|
||||
for file in glob.glob(get.installDIR() + sourceFile):
|
||||
unlink(file)
|
||||
|
||||
def removeDir(destinationDirectory):
|
||||
'''removes destinationDirectory and its subtrees'''
|
||||
for directory in glob.glob(get.installDIR() + destinationDirectory):
|
||||
unlinkDir(directory)
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Generic functions for common usage of pisitools #
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
|
||||
# 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, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class ArgumentError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
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):
|
||||
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,57 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
# 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, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
|
||||
def compile(parameters = ''):
|
||||
'''compile source with given parameters.'''
|
||||
if system('python setup.py build %s' % (get.installDIR(), parameters)):
|
||||
raise CompileError('!!! Make failed...\n')
|
||||
|
||||
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...\n')
|
||||
|
||||
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...\n' % parameters)
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get
|
||||
|
||||
def can_access_file(sourceFile):
|
||||
'''test the existence of file'''
|
||||
return os.access(sourceFile, os.F_OK)
|
||||
|
||||
def can_access_directory(destinationDirectory):
|
||||
'''test readability, writability and executablility of directory'''
|
||||
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
|
||||
|
||||
def makedirs(destinationDirectory):
|
||||
'''recursive directory creation function'''
|
||||
try:
|
||||
os.makedirs(destinationDirectory)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def echo(destionationFile, content):
|
||||
try:
|
||||
f = open(destionationFile, 'a')
|
||||
f.write("%s\n" % content)
|
||||
f.close()
|
||||
except IOError:
|
||||
ctx.ui.error(' ActionsAPI [echo]: Can\'t append to file...')
|
||||
|
||||
def chmod(sourceFile, mode = 0755):
|
||||
'''change the mode of sourceFile to the mode'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if can_access_file(file):
|
||||
try:
|
||||
os.chmod(file, mode)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [chmod]: Operation not permitted...')
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [chmod]: File doesn\'t exists...')
|
||||
|
||||
def chown(sourceFile, uid = 0, gid = 0):
|
||||
'''change the owner and group id of sourceFile to the numeric uid and gid'''
|
||||
if can_access_file(sourceFile):
|
||||
try:
|
||||
os.chown(sourceFile, uid, gid)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [chown]: Operation not permitted...')
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [chown]: File doesn\'t exists...')
|
||||
|
||||
def sym(sourceFile, destinationFile):
|
||||
'''creates symbolic link'''
|
||||
try:
|
||||
os.symlink(sourceFile, destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [sym]: Permission denied...')
|
||||
|
||||
def unlink(sourceFile):
|
||||
'''remove the file path'''
|
||||
if isFile(sourceFile) or isLink(sourceFile):
|
||||
try:
|
||||
os.unlink(sourceFile)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [unlink]: Permission denied.')
|
||||
elif isDirectory(sourceFile):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [unlink]: File doesn\'t exists.')
|
||||
|
||||
def unlinkDir(sourceDirectory):
|
||||
'''delete an entire directory tree'''
|
||||
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
|
||||
try:
|
||||
shutil.rmtree(sourceDirectory)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [unlinkDir]: Operation not permitted.')
|
||||
elif isFile(sourceDirectory):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [unlinkDir]: Directory doesn\'t exists.')
|
||||
|
||||
def move(sourceFile, destinationFile):
|
||||
'''recursively move a sourceFile or directory to destinationFile'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if isFile(file) or isLink(file) or isDirectory(file):
|
||||
try:
|
||||
shutil.move(file, destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [move]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [move]: File doesn\'t exists.')
|
||||
|
||||
def copy(sourceFile, destinationFile):
|
||||
'''recursively copy a sourceFile or directory to destinationFile'''
|
||||
for file in glob.glob(sourceFile):
|
||||
if isFile(file) or isLink(file):
|
||||
try:
|
||||
shutil.copy(file, destinationFile)
|
||||
except IOError:
|
||||
ctx.ui.error(' ActionsAPI [copy]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [copy]: File doesn\'t exists.')
|
||||
|
||||
def copytree(source, destination, sym = False):
|
||||
'''recursively copy an entire directory tree rooted at source'''
|
||||
if isDirectory(source) or isLink(source):
|
||||
try:
|
||||
shutil.copytree(source, destination, sym)
|
||||
except OSError:
|
||||
ctx.ui.error(' ActionsAPI [copytree]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error(' ActionsAPI [copytree]: Directory doesn\'t exists.')
|
||||
|
||||
def touch(sourceFile):
|
||||
'''changes the access time of the 'sourceFile', or creates it if it is not exist'''
|
||||
if glob.glob(sourceFile):
|
||||
for file in glob.glob(sourceFile):
|
||||
os.utime(file, None)
|
||||
else:
|
||||
try:
|
||||
f = open(sourceFile, 'w')
|
||||
f.close()
|
||||
except IOError:
|
||||
ctx.ui.error(' ActionsAPI [touch]: Permission denied.')
|
||||
|
||||
def cd(directoryName = ''):
|
||||
'''change directory'''
|
||||
current = os.getcwd()
|
||||
if directoryName:
|
||||
os.chdir(directoryName)
|
||||
else:
|
||||
os.chdir(os.path.dirname(current))
|
||||
|
||||
def ls(source):
|
||||
'''listdir'''
|
||||
if os.path.isdir(source):
|
||||
return os.listdir(source)
|
||||
else:
|
||||
return glob.glob(source)
|
||||
|
||||
def export(key, value):
|
||||
'''export environ variable'''
|
||||
os.environ[key] = value
|
||||
|
||||
def isLink(sourceFile):
|
||||
'''return True if sourceFile refers to a symbolic link'''
|
||||
return os.path.islink(sourceFile)
|
||||
|
||||
def isFile(sourceFile):
|
||||
'''return True if sourceFile is an existing regular file'''
|
||||
return os.path.isfile(sourceFile)
|
||||
|
||||
def isDirectory(sourceDirectory):
|
||||
'''Return True if sourceFile is an existing directory'''
|
||||
return os.path.isdir(sourceDirectory)
|
||||
|
||||
def realPath(sourceFile):
|
||||
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
|
||||
return os.path.realpath(sourceFile)
|
||||
|
||||
def baseName(sourceFile):
|
||||
'''return the base name of pathname sourceFile'''
|
||||
return os.path.basename(sourceFile)
|
||||
|
||||
def dirName(sourceFile):
|
||||
'''return the directory name of pathname path'''
|
||||
return os.path.dirname(sourceFile)
|
||||
|
||||
def system(command):
|
||||
#FIXME: String formatting
|
||||
command = command.replace(" ", " ")
|
||||
ctx.ui.debug('executing %s' % command)
|
||||
p = os.popen(command)
|
||||
while 1:
|
||||
line = p.readline()
|
||||
if not line:
|
||||
break
|
||||
ctx.ui.debug(line[0:len(line)-1])
|
||||
|
||||
return p.close()
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
from os import getenv, environ
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# Set individual information, that are generally needed for ActionsAPI
|
||||
|
||||
def exportFlags():
|
||||
'''General flags used in actions API.'''
|
||||
|
||||
# Build systems depend on these environment variables. That is why
|
||||
# we export them instead of using as (instance) variables.
|
||||
values = ctx.config.values
|
||||
environ['HOST'] = values.build.host
|
||||
environ['CFLAGS'] = values.build.cflags
|
||||
environ['CXXFLAGS'] = values.build.cxxflags
|
||||
environ['LDFLAGS'] = values.build.ldflags
|
||||
|
||||
class Env(object):
|
||||
'''General environment variables used in actions API'''
|
||||
def __init__(self):
|
||||
|
||||
exportFlags()
|
||||
|
||||
self.__vars = {
|
||||
'pkg_dir': 'PKG_DIR',
|
||||
'work_dir': 'WORK_DIR',
|
||||
'install_dir': 'INSTALL_DIR',
|
||||
'src_name': 'SRC_NAME',
|
||||
'src_version': 'SRC_VERSION',
|
||||
'src_release': 'SRC_RELEASE',
|
||||
'host': 'HOST',
|
||||
'cflags': 'CFLAGS',
|
||||
'cxxflags': 'CXXFLAGS',
|
||||
'ldflags': 'LDFLAGS'
|
||||
}
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
||||
# Using environment variables is somewhat tricky. Each time
|
||||
# you need them you need to check for their value.
|
||||
if self.__vars.has_key(attr):
|
||||
return getenv(self.__vars[attr])
|
||||
else:
|
||||
return None
|
||||
|
||||
class Dirs:
|
||||
'''General directories used in actions API.'''
|
||||
# TODO: Eventually we should consider getting these from a/the
|
||||
# configuration file
|
||||
doc = 'usr/share/doc'
|
||||
sbin = 'usr/sbin'
|
||||
man = 'usr/share/man'
|
||||
info = 'usr/share/info'
|
||||
data = 'usr/share'
|
||||
conf = 'etc'
|
||||
localstate = 'var/lib'
|
||||
defaultprefix = 'usr'
|
||||
|
||||
#FIXME: Get these from config or somewhere else!
|
||||
kde = '/usr/kde/3.4'
|
||||
qt = '/usr/qt/3'
|
||||
|
||||
def initVariables():
|
||||
ctx.env = Env()
|
||||
ctx.dirs = Dirs()
|
||||
return ctx
|
||||
|
||||
glb = initVariables()
|
||||
@@ -1,589 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
"""Top level PISI interfaces. a facade to the entire PISI system"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
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
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
def init(database = True, options = None, ui = None ):
|
||||
"""Initialize PiSi subsystem"""
|
||||
|
||||
import pisi.config
|
||||
ctx.config = pisi.config.Config(options)
|
||||
|
||||
if ui is None:
|
||||
if options:
|
||||
pisi.context.ui = pisi.cli.CLI(options.debug)
|
||||
else:
|
||||
pisi.context.ui = pisi.cli.CLI()
|
||||
else:
|
||||
pisi.context.ui = ui
|
||||
|
||||
# initialize repository databases
|
||||
if database:
|
||||
ctx.repodb = pisi.repodb.init()
|
||||
ctx.installdb = pisi.installdb.init()
|
||||
|
||||
# TODO: bunun da ctx'de olmasi gerek, global hesabi
|
||||
packagedb.init()
|
||||
# import pisi.sourcedb
|
||||
# pisi.sourcedb.init()
|
||||
|
||||
def install(packages):
|
||||
"""install a list of packages (either files/urls, or names)"""
|
||||
|
||||
# FIXME: this function name "install" makes impossible to import
|
||||
# and use install module directly.
|
||||
from install import InstallError
|
||||
|
||||
try:
|
||||
# determine if this is a list of files/urls or names
|
||||
if packages[0].endswith(ctx.const.package_prefix): # they all have to!
|
||||
return install_pkg_files(packages)
|
||||
else:
|
||||
return install_pkg_names(packages)
|
||||
|
||||
#FIXME: As Gurer warns, something's fishy with this exception proc.
|
||||
except InstallError, e:
|
||||
ctx.ui.error("InstallError:%s" % e)
|
||||
|
||||
except packagedb.Error, e:
|
||||
ctx.ui.error("PackageDBError: (%s)" % e)
|
||||
ctx.ui.error("Package is not installable.")
|
||||
|
||||
#except Exception, e:
|
||||
# print e
|
||||
# ctx.ui.error("Error: %s" % e)
|
||||
|
||||
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_prefix):
|
||||
ctx.ui.error('Mixing file names and package names not supported YET.\n')
|
||||
return False
|
||||
|
||||
# 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):
|
||||
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.runtimeDeps
|
||||
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 and install_pkg_names(extra_packages)) or \
|
||||
(not extra_packages):
|
||||
|
||||
class PackageDB:
|
||||
def __init__(self):
|
||||
self.d = d_t
|
||||
|
||||
def get_package(self, key):
|
||||
return d_t[str(key)]
|
||||
|
||||
packagedb = PackageDB()
|
||||
|
||||
A = d_t.keys()
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to install.')
|
||||
return True
|
||||
|
||||
# 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
|
||||
print A
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
print pkg
|
||||
for dep in pkg.runtimeDeps:
|
||||
print 'checking ', dep
|
||||
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
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
print order
|
||||
|
||||
for x in order:
|
||||
operations.install_single_file(dfn[x])
|
||||
else:
|
||||
raise Error('External dependencies not satisfied')
|
||||
|
||||
return True # everything went OK.
|
||||
|
||||
def install_pkg_names(A):
|
||||
"""This is the real thing. It installs packages from
|
||||
the repository, trying to perform a minimum number of
|
||||
installs"""
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to install.')
|
||||
return True
|
||||
|
||||
# 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
|
||||
print A
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
print pkg
|
||||
for dep in pkg.runtimeDeps:
|
||||
print 'checking ', 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
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
print order
|
||||
for x in order:
|
||||
operations.install_single_name(x)
|
||||
|
||||
return True # everything went OK :)
|
||||
|
||||
def package_graph(A):
|
||||
"""Construct a package relations graph, containing
|
||||
all dependencies of packages A"""
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
print pkg
|
||||
for dep in pkg.runtimeDeps:
|
||||
# we don't deal with already *satisfied* dependencies
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
# 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
|
||||
return G_f
|
||||
|
||||
def upgrade(A):
|
||||
upgrade_pkg_names(A)
|
||||
|
||||
|
||||
def upgrade_pkg_names(A):
|
||||
"""Re-installs packages from the repository, trying to perform
|
||||
a maximum number of upgrades."""
|
||||
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
|
||||
# filter packages that are not upgradable
|
||||
Ap = []
|
||||
for x in A:
|
||||
if not ctx.installdb.is_installed(x):
|
||||
ctx.ui.info('Package %s is not installed.' % x)
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
pkg = packagedb.get_package(x)
|
||||
if ignore_build or (not build):
|
||||
if release < pkg.release:
|
||||
Ap.append(x)
|
||||
elif build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
|
||||
ctx.ui.info('Package %s is already at its latest version %s,\
|
||||
release %s, build %s.'
|
||||
% (x, pkg.version, pkg.release, pkg.build))
|
||||
A = Ap
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to upgrade.')
|
||||
return True
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
print pkg
|
||||
for dep in pkg.runtimeDeps:
|
||||
print 'checking ', dep
|
||||
# add packages that can be upgraded
|
||||
if dependency.repo_satisfies_dep(dep):
|
||||
if ctx.installdb.is_installed(dep.package):
|
||||
(v,r,b) = ctx.installdb.get_version(dep.package)
|
||||
rep_pkg = packagedb.get_package(dep.package)
|
||||
(vp,rp,bp) = (rep_pkg.version, rep_pkg.release,
|
||||
rep_pkg.build)
|
||||
if ignore_build or (not b) or (not bp):
|
||||
# if we can't look at build
|
||||
if r >= rp: # installed already new
|
||||
continue
|
||||
elif b and bp and b >= bp:
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
print order
|
||||
for x in order:
|
||||
operations.install_single_name(x, True)
|
||||
|
||||
return True # everything went OK :)
|
||||
|
||||
def list_upgradable():
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
|
||||
A = ctx.installdb.list_installed()
|
||||
# filter packages that are not upgradable
|
||||
Ap = []
|
||||
for x in A:
|
||||
if not ctx.installdb.is_installed(x):
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
pkg = packagedb.get_package(x)
|
||||
if ignore_build or (not build):
|
||||
if release < pkg.release:
|
||||
Ap.append(x)
|
||||
elif build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
pass
|
||||
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
|
||||
return Ap
|
||||
|
||||
def remove(A):
|
||||
"""remove set A of packages from system (A is a list of package names)"""
|
||||
|
||||
# filter packages that are not installed
|
||||
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 = Ap
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to remove.')
|
||||
return True
|
||||
|
||||
# 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
|
||||
print A
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
print 'processing', pkg.name
|
||||
rev_deps = packagedb.get_rev_deps(x)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
print 'checking ', rev_dep
|
||||
# we don't deal with unsatisfied dependencies
|
||||
if 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
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
print order
|
||||
for x in order:
|
||||
if ctx.installdb.is_installed(x):
|
||||
operations.remove_single(x)
|
||||
else:
|
||||
ctx.ui.info('Package %s is not installed. Cannot remove.' % x)
|
||||
|
||||
return True # everything went OK :)
|
||||
|
||||
def configure_pending():
|
||||
# TODO: not coded yet
|
||||
# start with pending packages
|
||||
# configure them in reverse topological order of configuration dependency
|
||||
pass
|
||||
|
||||
def info(package):
|
||||
if package.endswith(ctx.const.package_prefix):
|
||||
return info_file(package)
|
||||
else:
|
||||
return info_name(package)
|
||||
|
||||
def info_file(package):
|
||||
from package import Package
|
||||
|
||||
if not os.path.exists(package):
|
||||
raise Error ('File %s not found' % package)
|
||||
|
||||
package = Package(package)
|
||||
package.read()
|
||||
return package.metadata, package.files
|
||||
|
||||
def info_name(package_name):
|
||||
"""fetch package information for a package"""
|
||||
if packagedb.has_package(package_name):
|
||||
package = packagedb.get_package(package_name)
|
||||
from pisi.metadata import MetaData
|
||||
metadata = MetaData()
|
||||
metadata.package = package
|
||||
#FIXME: get it from sourcedb
|
||||
metadata.source = None
|
||||
#TODO: fetch the files from server if possible
|
||||
if ctx.installdb.is_installed(package.name):
|
||||
files = ctx.installdb.files(package.name)
|
||||
else:
|
||||
files = None
|
||||
return metadata, files
|
||||
else:
|
||||
raise Error('Package %s not found' % package_name)
|
||||
|
||||
def index(repo_dir = '.'):
|
||||
|
||||
ctx.ui.info('* Building index of PISI files under %s' % repo_dir)
|
||||
index = Index()
|
||||
index.index(repo_dir)
|
||||
index.write(ctx.const.pisi_index)
|
||||
ctx.ui.info('* Index file written')
|
||||
|
||||
|
||||
def add_repo(name, indexuri):
|
||||
repo = pisi.repodb.Repo(URI(indexuri))
|
||||
ctx.repodb.add_repo(name, repo)
|
||||
|
||||
def remove_repo(name):
|
||||
if ctx.repodb.has_repo(name):
|
||||
ctx.repodb.remove_repo(name)
|
||||
else:
|
||||
ctx.ui.error('* Repository %s does not exist. Cannot remove.'
|
||||
% name)
|
||||
|
||||
def update_repo(repo):
|
||||
|
||||
ctx.ui.info('* Updating repository: %s' % repo)
|
||||
index = Index()
|
||||
index.read(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo)
|
||||
index.update_db(repo)
|
||||
ctx.ui.info('* Package database updated.')
|
||||
|
||||
|
||||
# build functions...
|
||||
def prepare_for_build(pspecfile, authInfo=None):
|
||||
|
||||
# FIXME: there is a function named "build" in this module which
|
||||
# makes it impossible to use build module directly.
|
||||
from build import PisiBuild
|
||||
|
||||
url = URI(pspecfile)
|
||||
if url.is_remote_file():
|
||||
from sourcefetcher import SourceFetcher
|
||||
fs = SourceFetcher(url, authInfo)
|
||||
url.uri = fs.fetch_all()
|
||||
|
||||
pb = PisiBuild(url.uri)
|
||||
|
||||
# find out the build dependencies that are not satisfied...
|
||||
dep_unsatis = []
|
||||
for dep in pb.spec.source.buildDeps:
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
dep_unsatis.append(dep)
|
||||
|
||||
# FIXME: take care of the required buildDeps...
|
||||
# For now just report an error!
|
||||
if dep_unsatis:
|
||||
ctx.ui.error("Unsatisfied Build Dependencies:")
|
||||
for dep in dep_unsatis:
|
||||
ctx.ui.error(dep.package)
|
||||
# FIXME: Don't exit for now! It's annoying to test on a system that
|
||||
# doesn't has all packages made with pisi.
|
||||
# Will be enabled on the full-pisi system.
|
||||
# sys.exit(1)
|
||||
|
||||
return pb
|
||||
|
||||
def build(pspecfile, authInfo=None):
|
||||
pb = prepare_for_build(pspecfile, authInfo)
|
||||
pb.build()
|
||||
|
||||
|
||||
order = {"none": 0,
|
||||
"unpack": 1,
|
||||
"setupaction": 2,
|
||||
"buildaction": 3,
|
||||
"installaction": 4,
|
||||
"buildpackages": 5}
|
||||
|
||||
def __buildState_unpack(pb):
|
||||
# unpack is the first state to run.
|
||||
pb.fetch_source_archive()
|
||||
pb.unpack_source_archive()
|
||||
pb.apply_patches()
|
||||
|
||||
def __buildState_setupaction(pb, last):
|
||||
|
||||
if order[last] < order["unpack"]:
|
||||
__buildState_unpack(pb)
|
||||
pb.run_setup_action()
|
||||
|
||||
def __buildState_buildaction(pb, last):
|
||||
|
||||
if order[last] < order["setupaction"]:
|
||||
__buildState_setupaction(pb, last)
|
||||
pb.run_build_action()
|
||||
|
||||
def __buildState_installaction(pb, last):
|
||||
|
||||
if order[last] < order["buildaction"]:
|
||||
__buildState_buildaction(pb, last)
|
||||
pb.run_install_action()
|
||||
|
||||
def __buildState_buildpackages(pb, last):
|
||||
|
||||
if order[last] < order["installaction"]:
|
||||
__buildState_installaction(pb, last)
|
||||
pb.build_packages()
|
||||
|
||||
def build_until(pspecfile, state, authInfo=None):
|
||||
pb = prepare_for_build(pspecfile, authInfo)
|
||||
pb.compile_action_script()
|
||||
|
||||
last = pb.get_state()
|
||||
ctx.ui.info("Last state was %s"%last)
|
||||
|
||||
if not last: last = "none"
|
||||
|
||||
if state == "unpack":
|
||||
__buildState_unpack(pb)
|
||||
return
|
||||
|
||||
if state == "setupaction":
|
||||
__buildState_setupaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "buildaction":
|
||||
__buildState_buildaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "installaction":
|
||||
__buildState_installaction(pb, last)
|
||||
return
|
||||
|
||||
__buildState_buildpackages(pb, last)
|
||||
@@ -1,213 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Archive module provides access to regular archive file types.
|
||||
# maintainer baris and meren
|
||||
|
||||
# standard library modules
|
||||
import os
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
# PiSi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
class ArchiveError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class ArchiveBase(object):
|
||||
"""Base class for Archive classes."""
|
||||
def __init__(self, file_path, atype):
|
||||
self.file_path = file_path
|
||||
self.type = atype
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
self.target_dir = target_dir
|
||||
# first we check if we need to clean-up our working env.
|
||||
if os.path.exists(self.target_dir) and clean_dir:
|
||||
util.clean_dir(self.target_dir)
|
||||
|
||||
os.makedirs(self.target_dir)
|
||||
|
||||
|
||||
class ArchiveTar(ArchiveBase):
|
||||
"""ArchiveTar handles tar archives depending on the compression
|
||||
type. Provides access to tar, tar.gz and tar.bz2 files.
|
||||
|
||||
This class provides the unpack magic for tar archives."""
|
||||
def __init__(self, file_path, arch_type = "tar"):
|
||||
super(ArchiveTar, self).__init__(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
"""Unpack tar archive to a given target directory(target_dir)."""
|
||||
super(ArchiveTar, self).unpack(target_dir, clean_dir)
|
||||
|
||||
rmode = ""
|
||||
if self.type == 'tar':
|
||||
rmode = 'r:'
|
||||
elif self.type == 'targz':
|
||||
rmode = 'r:gz'
|
||||
elif self.type == 'tarbz2':
|
||||
rmode = 'r:bz2'
|
||||
else:
|
||||
raise ArchiveError("Archive type not recognized")
|
||||
|
||||
tar = tarfile.open(self.file_path, rmode)
|
||||
oldwd = os.getcwd()
|
||||
os.chdir(self.target_dir)
|
||||
for tarinfo in tar:
|
||||
tar.extract(tarinfo)
|
||||
os.chdir(oldwd)
|
||||
tar.close()
|
||||
|
||||
|
||||
class ArchiveZip(ArchiveBase):
|
||||
"""ArchiveZip handles zip archives.
|
||||
|
||||
Being a zip archive PISI packages also use this class
|
||||
extensively. This class provides unpacking and packing magic for
|
||||
zip archives."""
|
||||
|
||||
symmagic = 2716663808 #long of hex val '0xA1ED0000L'
|
||||
|
||||
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
|
||||
super(ArchiveZip, self).__init__(file_path, arch_type)
|
||||
|
||||
self.zip_obj = zipfile.ZipFile(self.file_path, mode)
|
||||
|
||||
def close(self):
|
||||
"""Close the zip archive."""
|
||||
self.zip_obj.close()
|
||||
|
||||
def add_to_archive(self, file_name):
|
||||
"""Add file or directory path to the zip file"""
|
||||
# It's a pity that zipfile can't handle unicode strings. Grrr!
|
||||
file_name = str(file_name)
|
||||
if os.path.isdir(file_name) and not os.path.islink(file_name):
|
||||
self.zip_obj.writestr(file_name + '/', '')
|
||||
for f in os.listdir(file_name):
|
||||
self.add_to_archive(os.path.join(file_name, f))
|
||||
else:
|
||||
if os.path.islink(file_name):
|
||||
dest = os.readlink(file_name)
|
||||
attr = zipfile.ZipInfo()
|
||||
attr.filename = file_name
|
||||
attr.create_system = 3
|
||||
attr.external_attr = self.symmagic
|
||||
self.zip_obj.writestr(attr, dest)
|
||||
else:
|
||||
self.zip_obj.write(file_name, file_name, zipfile.ZIP_DEFLATED)
|
||||
|
||||
def add_basename_to_archive(self, file_name):
|
||||
"""Add only the basepath to the zip file. For example; if the given
|
||||
file_name parameter is /usr/local/bin/somedir, this function
|
||||
will create only the base directory/file somedir in the
|
||||
archive."""
|
||||
cwd = os.getcwd()
|
||||
path_name = os.path.dirname(file_name)
|
||||
file_name = os.path.basename(file_name)
|
||||
if path_name:
|
||||
os.chdir(path_name)
|
||||
self.add_to_archive(file_name)
|
||||
os.chdir(cwd)
|
||||
|
||||
def unpack_file_cond(self, pred, target_dir, archive_root = ''):
|
||||
"""Unpack/Extract a file according to predicate function filename ->
|
||||
bool"""
|
||||
zip_obj = self.zip_obj
|
||||
for info in zip_obj.infolist():
|
||||
if pred(info.filename): # check if condition holds
|
||||
|
||||
# below code removes that, so we find it here
|
||||
is_dir = info.filename.endswith('/')
|
||||
|
||||
# calculate output file name
|
||||
if archive_root == '':
|
||||
outpath = info.filename
|
||||
else:
|
||||
# change archive_root
|
||||
if util.subpath(archive_root, info.filename):
|
||||
outpath = util.removepathprefix(archive_root,
|
||||
info.filename)
|
||||
else:
|
||||
continue # don't extract if not under
|
||||
|
||||
ofile = os.path.join(target_dir, outpath)
|
||||
|
||||
if is_dir: # this is a directory
|
||||
d = os.path.join(target_dir, outpath)
|
||||
if not os.path.isdir(d):
|
||||
os.makedirs(d)
|
||||
continue
|
||||
|
||||
# check that output dir is present
|
||||
util.check_dir(os.path.dirname(ofile))
|
||||
|
||||
# remove output file we might be overwriting
|
||||
if os.path.exists(ofile):
|
||||
os.remove(ofile)
|
||||
|
||||
if info.external_attr == self.symmagic:
|
||||
target = zip_obj.read(info.filename)
|
||||
os.symlink(target, ofile)
|
||||
else:
|
||||
perm = info.external_attr
|
||||
perm &= 0x00FF0000
|
||||
perm >>= 16
|
||||
perm |= 0x00000100
|
||||
buff = open (ofile, 'wb')
|
||||
file_content = zip_obj.read(info.filename)
|
||||
buff.write(file_content)
|
||||
buff.close()
|
||||
os.chmod(ofile, perm)
|
||||
|
||||
def unpack_files(self, paths, target_dir):
|
||||
self.unpack_file_cond(lambda f:f in paths, target_dir)
|
||||
|
||||
def unpack_dir(self, path, target_dir):
|
||||
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir)
|
||||
|
||||
def unpack_dir_flat(self, path, target_dir):
|
||||
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir, path)
|
||||
|
||||
def unpack(self, target_dir, clean_dir=False):
|
||||
super(ArchiveZip, self).unpack(target_dir, clean_dir)
|
||||
|
||||
self.unpack_file_cond(lambda f: True, target_dir)
|
||||
self.close()
|
||||
return
|
||||
|
||||
|
||||
class Archive:
|
||||
"""Archive is the main factory for ArchiveClasses, regarding the
|
||||
Abstract Factory Pattern :)."""
|
||||
|
||||
def __init__(self, file_path, arch_type):
|
||||
"""accepted archive types:
|
||||
targz, tarbz2, zip, tar"""
|
||||
|
||||
handlers = {
|
||||
'targz': ArchiveTar,
|
||||
'tarbz2': ArchiveTar,
|
||||
'tar': ArchiveTar,
|
||||
'zip': ArchiveZip
|
||||
}
|
||||
|
||||
self.archive = handlers.get(arch_type)(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
self.archive.unpack(target_dir, clean_dir)
|
||||
|
||||
def unpack_files(self, files, target_dir):
|
||||
self.archive.unpack_files(files, target_dir)
|
||||
@@ -1,466 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package bulding stuff
|
||||
# maintainer: baris and meren
|
||||
|
||||
# python standard library
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.sourcearchive import SourceArchive
|
||||
from pisi.files import Files, FileInfo
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.package import Package
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
# Helper Functions
|
||||
def get_file_type(path, pinfoList):
|
||||
"""Return the file type of a path according to the given PathInfo
|
||||
list"""
|
||||
# The usage of depth is somewhat confusing. It is used for finding
|
||||
# the best match to paths(in pinfolist). For an example, if paths
|
||||
# contain ['/usr/share','/usr/share/doc'] and path is
|
||||
# /usr/share/doc/filename our iteration over paths should match
|
||||
# the second item.
|
||||
depth = 0
|
||||
ftype = ""
|
||||
path = "/"+path # we need a real path.
|
||||
for pinfo in pinfoList:
|
||||
if util.subpath(pinfo.pathname, path):
|
||||
length = len(pinfo.pathname)
|
||||
if depth < length:
|
||||
depth = length
|
||||
ftype = pinfo.fileType
|
||||
return ftype
|
||||
|
||||
def check_path_collision(package, pkgList):
|
||||
"""This function will check for collision of paths in a package with
|
||||
the paths of packages in pkgList. The return value will be the
|
||||
list containing the paths that collide."""
|
||||
collisions = []
|
||||
for pinfo in package.paths:
|
||||
for pkg in pkgList:
|
||||
if pkg is package:
|
||||
continue
|
||||
for path in pkg.paths:
|
||||
# if pinfo.pathname is a subpath of path.pathname like
|
||||
# the example below. path.patname is marked as a
|
||||
# collide. Exp:
|
||||
# pinfo.pathname: /usr/share
|
||||
# path.pathname: /usr/share/doc
|
||||
if util.subpath(pinfo.pathname, path.pathname):
|
||||
collisions.append(path.pathname)
|
||||
ctx.ui.error(_('Path %s belongs in multiple packages') %
|
||||
path.pathname)
|
||||
return collisions
|
||||
|
||||
# a dynamic build context
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
|
||||
class BuildContext(object):
|
||||
"""Build Context Singleton"""
|
||||
|
||||
def __init__(self, pspecfile):
|
||||
super(BuildContext, self).__init__()
|
||||
self.set_spec_file(pspecfile)
|
||||
|
||||
def set_spec_file(self, pspecfile):
|
||||
self.pspecfile = pspecfile
|
||||
spec = SpecFile()
|
||||
spec.read(pspecfile)
|
||||
# FIXME: following checks the integrity but does nothing when it is wrong
|
||||
# -gurer
|
||||
#spec.verify() # check pspec integrity
|
||||
self.spec = spec
|
||||
|
||||
# directory accessor functions
|
||||
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def pkg_dir(self):
|
||||
"package build directory"
|
||||
packageDir = self.spec.source.name + '-' + \
|
||||
self.spec.source.version + '-' + self.spec.source.release
|
||||
|
||||
return ctx.config.destdir + ctx.config.values.dirs.tmp_dir \
|
||||
+ '/' + packageDir
|
||||
|
||||
def pkg_work_dir(self):
|
||||
return self.pkg_dir() + ctx.const.work_dir_suffix
|
||||
|
||||
def pkg_install_dir(self):
|
||||
return self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
|
||||
class PisiBuild:
|
||||
"""PisiBuild class, provides the package build and creation routines"""
|
||||
def __init__(self, pspec):
|
||||
self.bctx = BuildContext(pspec)
|
||||
self.pspecDir = os.path.dirname(os.path.realpath(self.bctx.pspecfile))
|
||||
self.spec = self.bctx.spec
|
||||
self.sourceArchive = SourceArchive(self.bctx)
|
||||
|
||||
self.set_environment_vars()
|
||||
|
||||
self.actionLocals = None
|
||||
self.actionGlobals = None
|
||||
self.srcDir = None
|
||||
|
||||
def set_state(self, state):
|
||||
stateFile = os.path.join(self.bctx.pkg_work_dir(), "pisiBuildState")
|
||||
open(stateFile, "w").write(state)
|
||||
|
||||
def get_state(self):
|
||||
stateFile = os.path.join(self.bctx.pkg_work_dir(), "pisiBuildState")
|
||||
if not os.path.exists(stateFile): # no state
|
||||
return None
|
||||
return open(stateFile, "r").read()
|
||||
|
||||
def build(self):
|
||||
"""Build the package in one shot."""
|
||||
|
||||
ctx.ui.info(_("Building PISI source package: %s") % self.spec.source.name)
|
||||
util.xterm_title(_("Building PISI source package: %s\n") % self.spec.source.name)
|
||||
|
||||
self.compile_action_script()
|
||||
|
||||
# check if all patch files exists, if there are missing no need to unpack!
|
||||
self.patch_exists()
|
||||
|
||||
self.fetch_source_archive()
|
||||
|
||||
self.unpack_source_archive()
|
||||
|
||||
self.solve_build_dependencies()
|
||||
|
||||
# apply the patches and prepare a source directory for build.
|
||||
self.apply_patches()
|
||||
|
||||
self.run_setup_action()
|
||||
self.run_build_action()
|
||||
self.run_install_action()
|
||||
|
||||
# after all, we are ready to build/prepare the packages
|
||||
self.build_packages()
|
||||
|
||||
def set_environment_vars(self):
|
||||
"""Sets the environment variables for actions API to use"""
|
||||
evn = {
|
||||
"PKG_DIR": self.bctx.pkg_dir(),
|
||||
"WORK_DIR": self.bctx.pkg_work_dir(),
|
||||
"INSTALL_DIR": self.bctx.pkg_install_dir(),
|
||||
"SRC_NAME": self.spec.source.name,
|
||||
"SRC_VERSION": self.spec.source.version,
|
||||
"SRC_RELEASE": self.spec.source.release
|
||||
}
|
||||
os.environ.update(evn)
|
||||
|
||||
def fetch_source_archive(self):
|
||||
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archiveUri)
|
||||
self.sourceArchive.fetch()
|
||||
ctx.ui.info(_("Source archive is stored: %s/%s")
|
||||
%(ctx.config.archives_dir(), self.spec.source.archiveName))
|
||||
|
||||
def unpack_source_archive(self):
|
||||
ctx.ui.info(_("Unpacking archive..."))
|
||||
self.sourceArchive.unpack()
|
||||
ctx.ui.info(_(" unpacked (%s)") % self.bctx.pkg_work_dir())
|
||||
self.set_state("unpack")
|
||||
|
||||
def run_setup_action(self):
|
||||
# Run configure, build and install phase
|
||||
ctx.ui.action(_("Setting up source..."))
|
||||
self.run_action_function(ctx.const.setup_func)
|
||||
self.set_state("setupaction")
|
||||
|
||||
def run_build_action(self):
|
||||
ctx.ui.action(_("Building source..."))
|
||||
self.run_action_function(ctx.const.build_func)
|
||||
self.set_state("buildaction")
|
||||
|
||||
def run_install_action(self):
|
||||
ctx.ui.action(_("Installing..."))
|
||||
|
||||
# Before install make sure install_dir is clean
|
||||
if os.path.exists(self.bctx.pkg_install_dir()):
|
||||
util.clean_dir(self.bctx.pkg_install_dir())
|
||||
|
||||
# install function is mandatory!
|
||||
self.run_action_function(ctx.const.install_func, True)
|
||||
self.set_state("installaction")
|
||||
|
||||
def compile_action_script(self):
|
||||
"""Compiles actions.py and sets the actionLocals and actionGlobals"""
|
||||
specdir = os.path.dirname(self.bctx.pspecfile)
|
||||
scriptfile = os.path.join(specdir, ctx.const.actions_file)
|
||||
try:
|
||||
localSymbols = globalSymbols = {}
|
||||
buf = open(scriptfile).read()
|
||||
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
|
||||
except IOError, e:
|
||||
ctx.ui.error(_("Unable to read Action Script (%s): %s") %(scriptfile,e))
|
||||
sys.exit(1)
|
||||
except SyntaxError, e:
|
||||
ctx.ui.error (_("SyntaxError in Action Script (%s): %s") %(scriptfile,e))
|
||||
sys.exit(1)
|
||||
|
||||
self.actionLocals = localSymbols
|
||||
self.actionGlobals = globalSymbols
|
||||
self.srcDir = self.pkg_src_dir()
|
||||
|
||||
def pkg_src_dir(self):
|
||||
"""Returns the real path of WorkDir for an unpacked archive."""
|
||||
try:
|
||||
workdir = self.actionGlobals['WorkDir']
|
||||
except KeyError:
|
||||
workdir = self.spec.source.name + "-" + self.spec.source.version
|
||||
|
||||
return os.path.join(self.bctx.pkg_work_dir(), workdir)
|
||||
|
||||
def run_action_function(self, func, mandatory=False):
|
||||
"""Calls the corresponding function in actions.py.
|
||||
|
||||
If mandatory parameter is True, and function is not present in
|
||||
actionLocals pisi.build.Error will be raised."""
|
||||
# we'll need our working directory after actionscript
|
||||
# finished its work in the archive source directory.
|
||||
curDir = os.getcwd()
|
||||
os.chdir(self.srcDir)
|
||||
|
||||
|
||||
if func in self.actionLocals:
|
||||
self.actionLocals[func]()
|
||||
else:
|
||||
if mandatory:
|
||||
Error, _("unable to call function from actions: %s") %func
|
||||
|
||||
os.chdir(curDir)
|
||||
|
||||
def solve_build_dependencies(self):
|
||||
"""fail if dependencies not satisfied"""
|
||||
#TODO: we'll have to do better than plugging a fxn here
|
||||
pass
|
||||
|
||||
def patch_exists(self):
|
||||
"""check existence of patch files declared in PSPEC"""
|
||||
|
||||
files_dir = os.path.abspath(os.path.join(self.pspecDir,
|
||||
ctx.const.files_dir))
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = os.path.join(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(os.path.join(self.pspecDir,
|
||||
ctx.const.files_dir))
|
||||
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = os.path.join(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 gen_metadata_xml(self, package):
|
||||
"""Generate the metadata.xml file for build source.
|
||||
|
||||
metadata.xml is composed of the information from specfile plus
|
||||
some additional information."""
|
||||
metadata = MetaData()
|
||||
metadata.from_spec(self.spec.source, package)
|
||||
|
||||
metadata.package.distribution = ctx.config.values.general.distribution
|
||||
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
|
||||
metadata.package.architecture = "Any"
|
||||
|
||||
# FIXME: Bu hatalı. installsize'ı almak için tüm
|
||||
# pkg_install_dir()'ın boyutunu hesaplayamayız. Bir source
|
||||
# birden fazla kaynak üretebilir. package.paths ile
|
||||
# karşılaştırarak file listesinden boyutları hesaplatmalıyız.
|
||||
d = self.bctx.pkg_install_dir()
|
||||
size = util.dir_size(d)
|
||||
metadata.package.installedSize = str(size)
|
||||
|
||||
# build no
|
||||
if ctx.config.options.ignore_build_no:
|
||||
metadata.package.build = None # means, build no information n/a
|
||||
ctx.ui.warning('build number is not available.')
|
||||
else:
|
||||
metadata.package.build = self.calc_build_no(metadata.package.name)
|
||||
|
||||
metadata.write(os.path.join(self.bctx.pkg_dir(), ctx.const.metadata_xml))
|
||||
self.metadata = metadata
|
||||
|
||||
def gen_files_xml(self, package):
|
||||
"""Generetes files.xml using the path definitions in specfile and
|
||||
generated files by the build system."""
|
||||
files = Files()
|
||||
install_dir = self.bctx.pkg_install_dir()
|
||||
collisions = check_path_collision(package,
|
||||
self.spec.packages)
|
||||
if collisions:
|
||||
raise Error(_('Path collisions detected'))
|
||||
d = {}
|
||||
for pinfo in package.paths:
|
||||
path = install_dir + pinfo.pathname
|
||||
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
|
||||
frpath = util.removepathprefix(install_dir, fpath) # relative path
|
||||
ftype = get_file_type(frpath, package.paths)
|
||||
try: # broken links can cause problem
|
||||
fsize = str(os.path.getsize(fpath))
|
||||
except OSError:
|
||||
fsize = "0"
|
||||
d[frpath] = FileInfo(frpath, ftype, fsize, fhash)
|
||||
for (p, fileinfo) in d.iteritems():
|
||||
files.append(fileinfo)
|
||||
files.write(os.path.join(self.bctx.pkg_dir(), ctx.const.files_xml))
|
||||
self.files = files
|
||||
|
||||
def calc_build_no(self, package_name):
|
||||
"""Calculate build number"""
|
||||
|
||||
# find previous build in ctx.config.options.output_dir
|
||||
found = []
|
||||
for root, dirs, files in os.walk(ctx.config.options.output_dir):
|
||||
for fn in files:
|
||||
fn = fn.decode('utf-8')
|
||||
if fn.startswith(package_name + '-') and \
|
||||
fn.endswith(ctx.const.package_prefix):
|
||||
old_package_fn = os.path.join(root, fn)
|
||||
ctx.ui.info('(found old version %s)' % old_package_fn)
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(os.path.join(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
old_build = old_pkg.metadata.package.build
|
||||
found.append( (old_package_fn, old_build) )
|
||||
if not found:
|
||||
return 0
|
||||
ctx.ui.warning('(no previous build found, setting build no to 0.)')
|
||||
else:
|
||||
a = filter(lambda (x,y): y != None, found)
|
||||
if a:
|
||||
a.sort(lambda x,y : cmp(x[1],y[1]))
|
||||
old_package_fn = a[0][0]
|
||||
old_build = a[0][1]
|
||||
else:
|
||||
old_build = None
|
||||
|
||||
# compare old files.xml with the new one..
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(os.path.join(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
|
||||
# FIXME: TAKE INTO ACCOUNT MINOR CHANGES IN METADATA
|
||||
changed = False
|
||||
fnew = self.files.list
|
||||
fold = old_pkg.files.list
|
||||
fold.sort(lambda x,y : cmp(x.path,y.path))
|
||||
fnew.sort(lambda x,y : cmp(x.path,y.path))
|
||||
if len(fnew) != len(fold):
|
||||
changed = True
|
||||
else:
|
||||
for i in range(len(fold)):
|
||||
fo = fold.pop(0)
|
||||
fn = fnew.pop(0)
|
||||
if fo.path != fn.path:
|
||||
changed = True
|
||||
break
|
||||
else:
|
||||
if fo.hash != fn.hash:
|
||||
changed = True
|
||||
break
|
||||
|
||||
# set build number
|
||||
if old_build is None:
|
||||
ctx.ui.warning('(old package lacks a build no, setting build no to 0.)')
|
||||
return 0
|
||||
elif changed:
|
||||
return old_build + 1
|
||||
else:
|
||||
return old_build
|
||||
|
||||
def build_packages(self):
|
||||
"""Build each package defined in PSPEC file. After this process there
|
||||
will be .pisi files hanging around, AS INTENDED ;)"""
|
||||
for package in self.spec.packages:
|
||||
|
||||
# store additional files
|
||||
c = os.getcwd()
|
||||
os.chdir(self.pspecDir)
|
||||
install_dir = self.bctx.pkg_dir() + ctx.const.install_dir_suffix
|
||||
for afile in package.additionalFiles:
|
||||
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)
|
||||
|
||||
name = util.package_name(package.name,
|
||||
self.spec.source.version,
|
||||
self.spec.source.release)
|
||||
|
||||
ctx.ui.action(_("** Building package %s") % package.name);
|
||||
|
||||
ctx.ui.action(_("Generating %s...") % ctx.const.files_xml)
|
||||
self.gen_files_xml(package)
|
||||
ctx.ui.info(_(" done."))
|
||||
|
||||
ctx.ui.action(_("Generating %s...") % ctx.const.metadata_xml)
|
||||
self.gen_metadata_xml(package)
|
||||
ctx.ui.info(_(" done."))
|
||||
|
||||
ctx.ui.action(_("Creating PISI package %s") % name)
|
||||
|
||||
pkg = Package(name, 'w')
|
||||
|
||||
# add comar files to package
|
||||
os.chdir(self.pspecDir)
|
||||
for pcomar in package.providesComar:
|
||||
fname = os.path.join(ctx.const.comar_dir,
|
||||
pcomar.script)
|
||||
pkg.add_to_package(fname)
|
||||
|
||||
# add xmls and files
|
||||
os.chdir(self.bctx.pkg_dir())
|
||||
|
||||
pkg.add_to_package(ctx.const.metadata_xml)
|
||||
pkg.add_to_package(ctx.const.files_xml)
|
||||
|
||||
# Now it is time to add files to the packages using newly
|
||||
# created files.xml
|
||||
files = Files()
|
||||
files.read(ctx.const.files_xml)
|
||||
for finfo in files.list:
|
||||
pkg.add_to_package("install/" + finfo.path)
|
||||
|
||||
pkg.close()
|
||||
os.chdir(c)
|
||||
self.set_state("buildpackages")
|
||||
util.xterm_title_reset()
|
||||
@@ -1,23 +0,0 @@
|
||||
Writing commands:
|
||||
-----------------
|
||||
|
||||
TODO: this is a *bit* out of date now.
|
||||
|
||||
Subclass from Command.
|
||||
|
||||
Be careful not to import PiSi modules before Command is run, e.g.
|
||||
do it in run() method. This is necessary to prevent an obscure
|
||||
initialization error.
|
||||
|
||||
We used to import pisi.operations in pisi.cli
|
||||
|
||||
This prevented pisi.operations from having the current global
|
||||
pisi.ui.ui because it's loaded before the UI is set! So, despite
|
||||
what you might understand from the python FAQ 1.2.3
|
||||
http://www.python.org/doc/faq/programming.html#how-do-i-share-global-variables-across-modules
|
||||
you have to be careful about the initialization order when using
|
||||
singleton modules.
|
||||
|
||||
Another way to solve this is to ensure that the global module is
|
||||
always imported again in every function it is called (see
|
||||
ui.CLI.confirm() ) but this is more expensive.
|
||||
@@ -1,85 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# pisi.cli package version
|
||||
__version__ = "1.0_alpha2"
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.ui import UI
|
||||
from pisi.cli.colors import colorize
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CLI(UI):
|
||||
"Command Line Interface"
|
||||
|
||||
def __init__(self, show_debug = False, show_verbose = False):
|
||||
super(CLI, self).__init__(show_debug, show_verbose)
|
||||
|
||||
def output(self, str):
|
||||
sys.stdout.write(str)
|
||||
sys.stdout.flush()
|
||||
|
||||
def info(self, msg, verbose = False):
|
||||
# TODO: need to look at more kinds of info messages
|
||||
# let's cheat from KDE :)
|
||||
if verbose and self.show_verbose:
|
||||
self.output(msg + '\n')
|
||||
elif not verbose:
|
||||
self.output(msg + '\n')
|
||||
|
||||
def warning(self,msg):
|
||||
self.output(colorize('Warning:' + msg + '\n', 'purple'))
|
||||
|
||||
def error(self,msg):
|
||||
self.output(colorize('Error:' + msg + '\n', 'red'))
|
||||
|
||||
def action(self,msg):
|
||||
#TODO: this seems quite redundant?
|
||||
self.output(colorize(msg + '\n', 'green'))
|
||||
|
||||
def choose(self, msg, opts):
|
||||
print msg
|
||||
for i in range(0,len(opts)):
|
||||
print i + 1, opts(i)
|
||||
while True:
|
||||
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
|
||||
try:
|
||||
opt = int(s)
|
||||
if 1 <= opt and opt <= len(opts):
|
||||
return opts(opt-1)
|
||||
except (Exception,e):
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
if ctx.config.options and ctx.config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
s = raw_input(msg + colorize('(yes/no)', 'red'))
|
||||
if s.startswith('y') or s.startswith('Y'):
|
||||
return True
|
||||
if s.startswith('n') or s.startswith('N'):
|
||||
return False
|
||||
|
||||
def display_progress(self, pd):
|
||||
out = '\r%-30.30s %3d%% %12.2f %s' % \
|
||||
(pd['filename'], pd['percent'], pd['rate'], pd['symbol'])
|
||||
self.output(out)
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Colors module provides some color codes for console output.
|
||||
|
||||
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):
|
||||
return colors[color] + msg + colors['default']
|
||||
else:
|
||||
return msg
|
||||
@@ -1,952 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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 pisi
|
||||
import pisi.cli
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
|
||||
|
||||
class Command(object):
|
||||
"""generic help string for any command"""
|
||||
|
||||
# class variables
|
||||
|
||||
cmd = []
|
||||
cmd_dict = {}
|
||||
|
||||
def commands_string():
|
||||
s = ''
|
||||
list = [x.name[0] for x in Command.cmd]
|
||||
list.sort()
|
||||
for x in list:
|
||||
s += x + '\n'
|
||||
return s
|
||||
commands_string = staticmethod(commands_string)
|
||||
|
||||
def get_command(cmd, fail=False):
|
||||
|
||||
if Command.cmd_dict.has_key(cmd):
|
||||
return Command.cmd_dict[cmd]()
|
||||
|
||||
if fail:
|
||||
print "Unrecognized command: ", cmd
|
||||
sys.exit(1)
|
||||
else:
|
||||
return None
|
||||
get_command = staticmethod(get_command)
|
||||
|
||||
# instance variabes
|
||||
|
||||
def __init__(self):
|
||||
# now for the real parser
|
||||
import pisi
|
||||
self.parser = OptionParser(usage=getattr(self, "__doc__"),
|
||||
version="%prog " + pisi.__version__)
|
||||
self.options()
|
||||
self.commonopts()
|
||||
(self.options, self.args) = self.parser.parse_args()
|
||||
self.args.pop(0) # exclude command arg
|
||||
|
||||
self.check_auth_info()
|
||||
|
||||
def commonopts(self):
|
||||
'''common options'''
|
||||
p = self.parser
|
||||
p.add_option("-D", "--destdir", action="store")
|
||||
p.add_option("", "--yes-all", action="store_true",
|
||||
default=False, help = "assume yes in all yes/no queries")
|
||||
p.add_option("-u", "--username", action="store")
|
||||
p.add_option("-p", "--password", action="store")
|
||||
p.add_option("-P", action="store_true", dest="getpass", default=False,
|
||||
help="Get password from the command line")
|
||||
p.add_option("-v", "--verbose", action="store_true",
|
||||
dest="verbose", default=False,
|
||||
help="detailed output")
|
||||
p.add_option("-d", "--debug", action="store_true",
|
||||
default=True, help="show debugging information")
|
||||
p.add_option("-n", "--dry-run", action="store_true", default=False,
|
||||
help = "do not perform any action, just show what\
|
||||
would be done")
|
||||
return p
|
||||
|
||||
def options(self):
|
||||
"""This is a fall back function. If the implementer module provides an
|
||||
options function it will be called"""
|
||||
pass
|
||||
|
||||
def check_auth_info(self):
|
||||
username = self.options.username
|
||||
password = self.options.password
|
||||
|
||||
# TODO: We'll get the username, password pair from a configuration
|
||||
# file from users home directory. Currently we need user to
|
||||
# give it from the user interface.
|
||||
# if not username and not password:
|
||||
# if someauthconfig.username and someauthconfig.password:
|
||||
# self.authInfo = (someauthconfig.username,
|
||||
# someauthconfig.password)
|
||||
# return
|
||||
if username and password:
|
||||
self.authInfo = (username, password)
|
||||
return
|
||||
|
||||
if username and self.options.getpass:
|
||||
from getpass import getpass
|
||||
password = getpass("Password: ")
|
||||
self.authInfo = (username, password)
|
||||
else:
|
||||
self.authInfo = None
|
||||
|
||||
def init(self, database = True):
|
||||
"""initialize PiSi components"""
|
||||
|
||||
# NB: command imports here or in the command class run fxns
|
||||
import pisi.api
|
||||
pisi.api.init(database, self.options)
|
||||
|
||||
def finalize(self):
|
||||
"""do cleanup work for PiSi components"""
|
||||
pass
|
||||
|
||||
def get_name(self):
|
||||
return self.__class__.name
|
||||
|
||||
def format_name(self):
|
||||
(name, shortname) = self.get_name()
|
||||
if shortname:
|
||||
return "%s (%s)" % (name, shortname)
|
||||
else:
|
||||
return name
|
||||
|
||||
def help(self):
|
||||
"""print help for the command"""
|
||||
ctx.ui.info(self.format_name() + ': ')
|
||||
print getattr(self, "__doc__")
|
||||
print self.parser.format_option_help()
|
||||
|
||||
def die(self):
|
||||
"""exit program"""
|
||||
print 'Program terminated abnormally.'
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
class autocommand(type):
|
||||
def __init__(cls, name, bases, dict):
|
||||
super(autocommand, cls).__init__(name, bases, dict)
|
||||
Command.cmd.append(cls)
|
||||
name = getattr(cls, 'name', None)
|
||||
if name is None:
|
||||
raise pisi.cli.Error('command lacks name')
|
||||
longname, shortname = name
|
||||
def add_cmd(cmd):
|
||||
if Command.cmd_dict.has_key(cmd):
|
||||
raise pisi.cli.Error('duplicate command %s' % cmd)
|
||||
else:
|
||||
Command.cmd_dict[cmd] = cls
|
||||
add_cmd(longname)
|
||||
if shortname:
|
||||
add_cmd(shortname)
|
||||
|
||||
|
||||
class Help(Command):
|
||||
"""Prints help for given commands.
|
||||
|
||||
Usage: help [ <command1> <command2> ... <commandn> ]
|
||||
|
||||
If run without parameters, it prints the general help."""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
#TODO? Discard Help's own usage doc in favor of general usage doc
|
||||
#self.__doc__ = usage_text
|
||||
#self.__doc__ += commands_string()
|
||||
super(Help, self).__init__()
|
||||
|
||||
name = ("help", "h")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.parser.set_usage(usage_text)
|
||||
self.parser.print_help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
|
||||
for arg in self.args:
|
||||
obj = Command.get_command(arg, True)
|
||||
obj.help()
|
||||
print
|
||||
|
||||
self.finalize()
|
||||
|
||||
class Clean(Command):
|
||||
"""Clean stale locks."""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Clean, self).__init__()
|
||||
|
||||
name = ("clean", None)
|
||||
|
||||
def run(self):
|
||||
self.init()
|
||||
pisi.util.clean_locks()
|
||||
self.finalize()
|
||||
|
||||
|
||||
class Graph(Command):
|
||||
"""Graph package relations.
|
||||
Usage: graph <package1> <package2> ...
|
||||
|
||||
Write a graph of package relations, tracking dependency and
|
||||
conflicts relations starting from given packages.
|
||||
"""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Graph, self).__init__()
|
||||
|
||||
name = ("graph", None)
|
||||
|
||||
def run(self):
|
||||
self.init()
|
||||
if self.args:
|
||||
g = pisi.api.package_graph(self.args)
|
||||
g.write_graphviz(file('pgraph.dot', 'w'))
|
||||
self.finalize()
|
||||
|
||||
|
||||
def buildno_opts(self):
|
||||
self.parser.add_option("", "--ignore-build-no", action="store_true",
|
||||
default=False,
|
||||
help="do not take build no into account.")
|
||||
|
||||
|
||||
class Build(Command):
|
||||
"""Build a PISI package using a pspec.xml file
|
||||
|
||||
Usage: build <pspec.xml>
|
||||
|
||||
You can give a URI of the pspec.xml file. PISI will
|
||||
fetch all necessary files and build the package for you.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Build, self).__init__()
|
||||
|
||||
name = ("build", "bi")
|
||||
|
||||
def options(self):
|
||||
buildno_opts(self)
|
||||
self.parser.add_option("-O", "--output-dir", action="store", default=".",
|
||||
help="output directory for produced packages")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
ctx.ui.info('Output directory: %s\n' % ctx.config.options.output_dir)
|
||||
for arg in self.args:
|
||||
pisi.api.build(arg, self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class PackageOp(Command):
|
||||
"""Abstract package operation command"""
|
||||
def __init__(self):
|
||||
super(PackageOp, self).__init__()
|
||||
|
||||
def options(self):
|
||||
self.parser.add_option("", "--ignore-comar", action="store_true",
|
||||
default=False, help="bypass comar configuration agent")
|
||||
## self.parser.add_option("", "--ignore-dependency",
|
||||
## action="store_true",
|
||||
## default=False, help="death")
|
||||
|
||||
def init(self):
|
||||
super(PackageOp, self).init(True)
|
||||
import pisi
|
||||
if not self.options.ignore_comar:
|
||||
import comar
|
||||
try:
|
||||
ctx.comard = comar.Link() # context
|
||||
except comar.Error:
|
||||
ctx.ui.error('Comar error encountered\n')
|
||||
self.die()
|
||||
|
||||
def finalize(self):
|
||||
#self.finalize_db()
|
||||
if not self.options.ignore_comar:
|
||||
pass
|
||||
#try:
|
||||
# pisi.comariface.finalize()
|
||||
#except pisi.comariface.ComarError:
|
||||
# ui.error('Comar error encountered\n')
|
||||
|
||||
|
||||
class Install(PackageOp):
|
||||
"""Install PISI packages
|
||||
|
||||
Usage: install <package1> <package2> ... <packagen>
|
||||
|
||||
You may use filenames, URIs or package names for packages. If you have
|
||||
specified a package name, it should exist in a specified repository.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Install, self).__init__()
|
||||
|
||||
name = "install", "it"
|
||||
|
||||
def options(self):
|
||||
super(Install, self).options()
|
||||
buildno_opts(self)
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
pisi.api.install(self.args)
|
||||
self.finalize()
|
||||
|
||||
class Upgrade(PackageOp):
|
||||
"""Upgrade PISI packages
|
||||
|
||||
Usage: Upgrade <package1> <package2> ... <packagen>
|
||||
|
||||
You may use filenames, URIs or package names for packages. If you have
|
||||
specified a package name, it should exist in a specified repository.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Upgrade, self).__init__()
|
||||
|
||||
name = ("upgrade", "up")
|
||||
|
||||
def options(self):
|
||||
super(Upgrade, self).options()
|
||||
buildno_opts(self)
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
pisi.api.upgrade(self.args)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class Remove(PackageOp):
|
||||
"""Remove PISI packages
|
||||
|
||||
Usage: remove <package1> <package2> ... <packagen>
|
||||
|
||||
Remove package(s) from your system. Just give the package names to remove.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Remove, self).__init__()
|
||||
|
||||
name = ("remove", "rm")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
pisi.api.remove(self.args)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class UpgradeAll(PackageOp):
|
||||
"""Upgrade system
|
||||
|
||||
Usage: Upgrade
|
||||
|
||||
Upgrade the entire system.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(UpgradeAll, self).__init__()
|
||||
|
||||
name = ("upgrade-all", None)
|
||||
|
||||
def options(self):
|
||||
super(UpgradeAll, self).options()
|
||||
buildno_opts(self)
|
||||
|
||||
def run(self):
|
||||
if self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
pisi.api.upgrade(ctx.installdb.list_installed())
|
||||
self.finalize()
|
||||
|
||||
|
||||
class ConfigurePending(PackageOp):
|
||||
"""configure pending packages
|
||||
"""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ConfigurePending, self).__init__()
|
||||
|
||||
name = ("configure-pending", "cp")
|
||||
|
||||
def run(self):
|
||||
|
||||
self.init()
|
||||
pisi.api.configure_pending()
|
||||
self.finalize()
|
||||
|
||||
|
||||
class Info(Command):
|
||||
"""Display package information
|
||||
|
||||
Usage: info <package1> <package2> ... <packagen>
|
||||
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Info, self).__init__()
|
||||
|
||||
name = ("info", "i")
|
||||
|
||||
def options(self):
|
||||
self.parser.add_option("-f", "--files", action="store_true",
|
||||
default=False,
|
||||
help="Show a list of package files.")
|
||||
self.parser.add_option("-F", "--files-path", action="store_true",
|
||||
default=False,
|
||||
help="Show only paths.")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init(True)
|
||||
for arg in self.args:
|
||||
self.printinfo(arg)
|
||||
self.finalize()
|
||||
|
||||
def printinfo(self, arg):
|
||||
import os.path
|
||||
|
||||
metadata, files = pisi.api.info(arg)
|
||||
print metadata.package
|
||||
if self.options.files or self.options.files_path:
|
||||
if files:
|
||||
print
|
||||
print 'Files:'
|
||||
for fileinfo in files.list:
|
||||
if self.options.files:
|
||||
print fileinfo
|
||||
else:
|
||||
print fileinfo.path
|
||||
else:
|
||||
print 'File information not available'
|
||||
|
||||
|
||||
class Index(Command):
|
||||
"""Index PISI files in a given directory
|
||||
|
||||
Usage: index <directory>
|
||||
|
||||
This command searches for all PiSi files in a directory, collects PiSi
|
||||
tags from them and accumulates the information in an output XML file,
|
||||
named by default 'pisi-index.xml'. In particular, it indexes both
|
||||
source and binary packages.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(Index, self).__init__()
|
||||
|
||||
name = ("index", "ix")
|
||||
|
||||
def options(self):
|
||||
self.parser.add_option("-a", "--absolute-uris", action="store_true",
|
||||
default=False,
|
||||
help="store absolute links for indexed files.")
|
||||
|
||||
def run(self):
|
||||
|
||||
self.init()
|
||||
from pisi.api import index
|
||||
if len(self.args)==1:
|
||||
index(self.args[0])
|
||||
elif len(self.args)==0:
|
||||
print 'Indexing current directory.'
|
||||
index()
|
||||
else:
|
||||
print 'Indexing only a single directory supported.'
|
||||
return
|
||||
self.finalize()
|
||||
|
||||
|
||||
class ListInstalled(Command):
|
||||
"""Print the list of all installed packages
|
||||
|
||||
Usage: list-installed
|
||||
|
||||
"""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ListInstalled, self).__init__()
|
||||
|
||||
name = ("list-installed", "li")
|
||||
|
||||
def options(self):
|
||||
self.parser.add_option("-l", "--long", action="store_true",
|
||||
default=False, help="show in long format")
|
||||
self.parser.add_option("-i", "--install-info", action="store_true",
|
||||
default=False, help="show detailed install info")
|
||||
|
||||
def run(self):
|
||||
self.init(True)
|
||||
list = ctx.installdb.list_installed()
|
||||
list.sort()
|
||||
if self.options.install_info:
|
||||
print 'Package Name |St| Version| Rel.| Build| Distro| Date'
|
||||
print '========================================================================'
|
||||
for pkg in list:
|
||||
package = pisi.packagedb.inst_packagedb.get_package(pkg)
|
||||
inst_info = ctx.installdb.get_info(pkg)
|
||||
if self.options.long:
|
||||
print package
|
||||
print inst_info
|
||||
elif self.options.install_info:
|
||||
print '%-15s | %s ' % (package.name, inst_info.one_liner())
|
||||
else:
|
||||
print '%15s - %s ' % (package.name, package.summary)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class UpdateRepo(Command):
|
||||
"""Update repository databases
|
||||
|
||||
Usage: update-repo <repo1> <repo2> ... <repon>
|
||||
|
||||
<repoi>: repository name
|
||||
Synchronizes the PiSi databases with the current repository.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(UpdateRepo, self).__init__()
|
||||
|
||||
name = ("update-repo", "ur")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init(True)
|
||||
for repo in self.args:
|
||||
pisi.api.update_repo(repo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class AddRepo(Command):
|
||||
"""Add a repository
|
||||
|
||||
Usage: add-repo <repo> <indexuri>
|
||||
|
||||
<repo>: name of repository to add
|
||||
<indexuri>: URI of index file
|
||||
|
||||
NB: We support only local files (e.g., /a/b/c) and http:// URIs at the moment
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(AddRepo, self).__init__()
|
||||
|
||||
name = ("add-repo", "ar")
|
||||
|
||||
def run(self):
|
||||
|
||||
if len(self.args)>=2:
|
||||
self.init()
|
||||
name = self.args[0]
|
||||
indexuri = self.args[1]
|
||||
pisi.api.add_repo(name, indexuri)
|
||||
self.init()
|
||||
else:
|
||||
self.help()
|
||||
return
|
||||
|
||||
|
||||
class RemoveRepo(Command):
|
||||
"""Remove repositories
|
||||
|
||||
Usage: remove-repo <repo1> <repo2> ... <repon>
|
||||
|
||||
Remove all repository information from the system.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(RemoveRepo, self).__init__()
|
||||
|
||||
name = ("remove-repo", "rr")
|
||||
|
||||
def run(self):
|
||||
|
||||
if len(self.args)>=1:
|
||||
self.init()
|
||||
for repo in self.args:
|
||||
pisi.api.remove_repo(repo)
|
||||
self.finalize()
|
||||
else:
|
||||
self.help()
|
||||
return
|
||||
|
||||
|
||||
class ListRepo(Command):
|
||||
"""List repositories
|
||||
|
||||
Usage: list-repo
|
||||
|
||||
Lists currently tracked repositories.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ListRepo, self).__init__()
|
||||
|
||||
name = ("list-repo", "lr")
|
||||
|
||||
def run(self):
|
||||
|
||||
self.init()
|
||||
for repo in ctx.repodb.list():
|
||||
print repo
|
||||
print ' ', ctx.repodb.get_repo(repo).indexuri.get_uri()
|
||||
self.finalize()
|
||||
|
||||
|
||||
class ListAvailable(Command):
|
||||
"""List available packages in the repositories
|
||||
|
||||
Usage: list-available [ <repo1> <repo2> ... repon ]
|
||||
|
||||
Gives a brief list of PiSi components published in the repository.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ListAvailable, self).__init__()
|
||||
|
||||
name = ("list-available", "la")
|
||||
|
||||
def run(self):
|
||||
|
||||
self.init(True)
|
||||
|
||||
if self.args:
|
||||
for arg in self.args:
|
||||
self.print_packages(arg)
|
||||
else:
|
||||
# print for all repos
|
||||
for repo in ctx.repodb.list():
|
||||
ctx.ui.info("Repository : %s\n" % repo)
|
||||
self.print_packages(repo)
|
||||
self.finalize()
|
||||
|
||||
def print_packages(self, repo):
|
||||
from pisi import packagedb
|
||||
from colors import colorize
|
||||
|
||||
pkg_db = packagedb.get_db(repo)
|
||||
list = pkg_db.list_packages()
|
||||
installed_list = ctx.installdb.list_installed()
|
||||
list.sort()
|
||||
for p in list:
|
||||
if p in installed_list:
|
||||
print colorize(p, "cyan")
|
||||
else:
|
||||
print p
|
||||
|
||||
class ListUpgrades(Command):
|
||||
"""List packages to be upgraded
|
||||
|
||||
Usage: list-upgrades [ <repo1> <repo2> ... repon ]
|
||||
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ListUpgrades, self).__init__()
|
||||
|
||||
name = ("list-upgrades", "lu")
|
||||
|
||||
def options(self):
|
||||
self.parser.add_option("-l", "--long", action="store_true",
|
||||
default=False, help="show in long format")
|
||||
self.parser.add_option("-i", "--install-info", action="store_true",
|
||||
default=False, help="show detailed install info")
|
||||
buildno_opts(self)
|
||||
|
||||
def run(self):
|
||||
self.init(True)
|
||||
list = pisi.api.list_upgradable()
|
||||
list.sort()
|
||||
if self.options.install_info:
|
||||
print 'Package Name |St| Version| Rel.| Build| Distro| Date'
|
||||
print '========================================================================'
|
||||
for pkg in list:
|
||||
package = pisi.packagedb.inst_packagedb.get_package(pkg)
|
||||
inst_info = ctx.installdb.get_info(pkg)
|
||||
if self.options.long:
|
||||
print package
|
||||
print inst_info
|
||||
elif self.options.install_info:
|
||||
print '%-15s | %s ' % (package.name, inst_info.one_liner())
|
||||
else:
|
||||
print '%15s - %s ' % (package.name, package.summary)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class ListPending(Command):
|
||||
"""List pending packages"""
|
||||
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(ListPending, self).__init__()
|
||||
|
||||
name = ("list-pending", "lp")
|
||||
|
||||
def run(self):
|
||||
self.init(True)
|
||||
|
||||
list = ctx.installdb.list_pending()
|
||||
list.sort()
|
||||
for p in list:
|
||||
print p
|
||||
|
||||
self.finalize()
|
||||
|
||||
|
||||
class SearchAvailable(Command):
|
||||
"""Search in available packages
|
||||
|
||||
Usage: search-available <search pattern>
|
||||
|
||||
FIXME: this is bogus
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Partial build commands
|
||||
|
||||
class BuildUntil(Build):
|
||||
"""Run the build process partially
|
||||
|
||||
Usage: -sStateName build-until <pspec file>
|
||||
|
||||
where states are:
|
||||
unpack, setupaction, buildaction, installaction, buildpackages
|
||||
|
||||
You can give an URI of the pspec.xml file. PISI will fetch all
|
||||
necessary files and unpack the source and prepare a source directory
|
||||
for you.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildUntil, self).__init__()
|
||||
|
||||
name = ("build-until", "bu")
|
||||
|
||||
def options(self):
|
||||
super(BuildUntil, self).options()
|
||||
self.parser.add_option("-s", action="store", dest="state")
|
||||
|
||||
def run(self):
|
||||
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
state = self.options.state
|
||||
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, state, self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class BuildUnpack(Build):
|
||||
"""Unpack the source archive
|
||||
|
||||
Usage: build-unpack <pspec file>
|
||||
|
||||
TODO: desc.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildUnpack, self).__init__()
|
||||
|
||||
name = ("build-unpack", "biu")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, "unpack", self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class BuildSetup(Build):
|
||||
"""Setup the source
|
||||
|
||||
Usage: build-setup <pspec file>
|
||||
|
||||
TODO: desc.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildSetup, self).__init__()
|
||||
|
||||
name = ("build-setup", "bis")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, "setupaction",
|
||||
self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class BuildBuild(Command):
|
||||
"""Setup the source
|
||||
|
||||
Usage: build-build <pspec file>
|
||||
|
||||
TODO: desc.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildBuild, self).__init__()
|
||||
|
||||
name = ("build-build", "bib")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, "buildaction", self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class BuildInstall(Build):
|
||||
"""Install to the sandbox
|
||||
|
||||
Usage: build-install <pspec file>
|
||||
|
||||
TODO: desc.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildInstall, self).__init__()
|
||||
|
||||
name = ("build-install", "bii")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, "installaction",
|
||||
self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
|
||||
class BuildPackage(Build):
|
||||
"""Setup the source
|
||||
|
||||
Usage: build-build <pspec file>
|
||||
|
||||
TODO: desc.
|
||||
"""
|
||||
__metaclass__ = autocommand
|
||||
|
||||
def __init__(self):
|
||||
super(BuildPackage, self).__init__()
|
||||
|
||||
name = ("build-package", "bip")
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
self.help()
|
||||
return
|
||||
|
||||
self.init()
|
||||
for arg in self.args:
|
||||
pisi.api.build_until(arg, "buildpackages", self.authInfo)
|
||||
self.finalize()
|
||||
|
||||
usage_text1 = """%prog [options] <command> [arguments]
|
||||
|
||||
where <command> is one of:
|
||||
|
||||
"""
|
||||
|
||||
usage_text2 = """
|
||||
Use \"%prog help <command>\" for help on a specific command.
|
||||
"""
|
||||
|
||||
usage_text = (usage_text1 + Command.commands_string() + usage_text2)
|
||||
@@ -1,96 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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 pisi
|
||||
from pisi.uri import URI
|
||||
from pisi.cli.commands import *
|
||||
|
||||
class ParserError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PreParser(OptionParser):
|
||||
"""consumes any options, and finds arguments from command line"""
|
||||
|
||||
def __init__(self, version):
|
||||
OptionParser.__init__(self, usage=usage_text, version=version)
|
||||
|
||||
def error(self, msg):
|
||||
raise ParserError, msg
|
||||
|
||||
def parse_args(self, args=None):
|
||||
self.rargs = self._get_args(args)
|
||||
self._process_args()
|
||||
return 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')
|
||||
del rargs[0]
|
||||
return
|
||||
# We handle bare "--" explicitly, and bare "-" is handled by the
|
||||
# standard arg handler since the short arg case ensures that the
|
||||
# len of the opt string is greater than 1.
|
||||
if arg == "--":
|
||||
del rargs[0]
|
||||
break
|
||||
elif arg[0:2] == "--":
|
||||
# process a single long option (possibly with value(s))
|
||||
option()
|
||||
elif arg[:1] == "-" and len(arg) > 1:
|
||||
# process a cluster of short options (possibly with
|
||||
# value(s) for the last one only)
|
||||
option()
|
||||
else: # then it must be an argument
|
||||
args.append(arg)
|
||||
del rargs[0]
|
||||
self.args = args
|
||||
|
||||
|
||||
class PisiCLI(object):
|
||||
|
||||
def __init__(self):
|
||||
# first construct a parser for common options
|
||||
# this is really dummy
|
||||
self.parser = PreParser(version="%prog " + pisi.__version__)
|
||||
|
||||
try:
|
||||
args = self.parser.parse_args()
|
||||
if len(args)==0: # more explicit than using IndexError
|
||||
print 'No command given'
|
||||
self.die()
|
||||
cmd_name = args[0]
|
||||
except ParserError:
|
||||
print 'Command line parsing error'
|
||||
self.die()
|
||||
|
||||
self.command = Command.get_command(cmd_name)
|
||||
if not self.command:
|
||||
print "Unrecognized command: ", cmd
|
||||
self.die()
|
||||
|
||||
def die(self):
|
||||
self.parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
def run_command(self):
|
||||
self.command.run()
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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.
|
||||
|
||||
# Authors: Baris Metin <baris@uludag.org.tr
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.configfile import ConfigurationFile
|
||||
|
||||
class Config(object):
|
||||
"""Config Singleton"""
|
||||
|
||||
def __init__(self, options = None):
|
||||
self.options = options
|
||||
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
|
||||
self.destdir = self.values.general.destinationdirectory
|
||||
|
||||
# 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 lib_dir(self):
|
||||
return self.destdir + self.values.dirs.lib_dir
|
||||
|
||||
def db_dir(self):
|
||||
return self.destdir + self.values.dirs.db_dir
|
||||
|
||||
def archives_dir(self):
|
||||
return self.destdir + self.values.dirs.archives_dir
|
||||
|
||||
def packages_dir(self):
|
||||
return self.destdir + self.values.dirs.packages_dir
|
||||
|
||||
def index_dir(self):
|
||||
return self.destdir + self.values.dirs.index_dir
|
||||
|
||||
def tmp_dir(self):
|
||||
return self.destdir + self.values.dirs.tmp_dir
|
||||
|
||||
# bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde
|
||||
# kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye
|
||||
# ihtiyacımız var? (baris)
|
||||
def install_dir(self):
|
||||
return self.tmp_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
#TODO: remove this
|
||||
config = Config()
|
||||
@@ -1,132 +0,0 @@
|
||||
# -*- conding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI Configuration File module, obviously, is used to read from the
|
||||
# configuration file. Module also defines default values for
|
||||
# configuration parameters.
|
||||
#
|
||||
# Configuration file is located in /etc/pisi/pisi.conf by default,
|
||||
# having an INI like format like below.
|
||||
#
|
||||
#[general]
|
||||
#destinationdirectory = /tmp
|
||||
#
|
||||
#[build]
|
||||
#host = i686-pc-linux-gnu
|
||||
#CFLAGS= -mcpu=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#CXXFLAGS= -mcpu=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#LDFLAGS=
|
||||
#
|
||||
#[directories]
|
||||
#lib_dir = /var/lib/pisi
|
||||
#db_dir = /var/db/pisi
|
||||
#archives_dir = /var/cache/pisi/archives
|
||||
#packages_dir = /var/cache/pisi/packages
|
||||
#index_dir = /var/cache/pisi/index
|
||||
#tmp_dir = /var/tmp/pisi
|
||||
#icon_theme_dir = /usr/share/icons/Tulliana-1.0
|
||||
|
||||
import os
|
||||
from ConfigParser import ConfigParser, NoSectionError
|
||||
|
||||
|
||||
class ConfigException(Exception):
|
||||
pass
|
||||
|
||||
class GeneralDefaults:
|
||||
"""Default values for [general] section"""
|
||||
destinationdirectory = os.getcwd() + "/tmp" # FOR ALPHA
|
||||
distribution = "Pardus"
|
||||
distribution_release = "0.1"
|
||||
|
||||
class BuildDefaults:
|
||||
"""Default values for [build] section"""
|
||||
host = "i686-pc-linux-gnu"
|
||||
CFLAGS = "-mcpu=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
CXXFLAGS = "-mcpu=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
LDFLAGS = ""
|
||||
|
||||
class DirsDefaults:
|
||||
"Default values for [directories] section"
|
||||
lib_dir = "/var/lib/pisi"
|
||||
db_dir = "/var/db/pisi"
|
||||
archives_dir = "/var/cache/pisi/archives"
|
||||
packages_dir = "/var/cache/pisi/packages"
|
||||
index_dir = "/var/cache/pisi/index"
|
||||
tmp_dir = "/var/tmp/pisi"
|
||||
icon_theme_dir = "/usr/share/icons/Tulliana-1.0"
|
||||
|
||||
|
||||
class ConfigurationSection(object):
|
||||
"""ConfigurationSection class defines a section in the configuration
|
||||
file, using defaults (above) as a fallback."""
|
||||
def __init__(self, section, items=[]):
|
||||
self.items = items
|
||||
|
||||
if section == "general":
|
||||
self.defaults = GeneralDefaults
|
||||
elif section == "build":
|
||||
self.defaults = BuildDefaults
|
||||
elif section == "directories":
|
||||
self.defaults = DirsDefaults
|
||||
else:
|
||||
e = "No section by name '%s'" % section
|
||||
raise ConfigException, e
|
||||
|
||||
self.section = section
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
||||
# first search for attribute in the items provided in the
|
||||
# configuration file.
|
||||
if self.items:
|
||||
for item in self.items:
|
||||
if item[0] == attr:
|
||||
return item[1]
|
||||
|
||||
# then fall back to defaults
|
||||
if hasattr(self.defaults, attr):
|
||||
return getattr(self.defaults, attr)
|
||||
|
||||
return ""
|
||||
|
||||
# We'll need to access configuration keys by their names as a
|
||||
# string. Like; ["default"]...
|
||||
def __getitem__(self, key):
|
||||
return self.__getattr__(key)
|
||||
|
||||
|
||||
class ConfigurationFile(object):
|
||||
"""Parse and get configuration values from the configuration file"""
|
||||
def __init__(self, filePath):
|
||||
parser = ConfigParser()
|
||||
self.filePath = filePath
|
||||
|
||||
parser.read(self.filePath)
|
||||
|
||||
try:
|
||||
generalitems = parser.items("general")
|
||||
except NoSectionError:
|
||||
generalitems = []
|
||||
self.general = ConfigurationSection("general", generalitems)
|
||||
|
||||
try:
|
||||
builditems = parser.items("build")
|
||||
except NoSectionError:
|
||||
builditems = []
|
||||
self.build = ConfigurationSection("build", builditems)
|
||||
|
||||
try:
|
||||
dirsitems = parser.items("directories")
|
||||
except NoSectionError:
|
||||
dirsitems = []
|
||||
self.dirs = ConfigurationSection("directories", dirsitems)
|
||||
@@ -1,90 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI constants.
|
||||
# If you have a "magic" constant value this is where it should be
|
||||
# defined.
|
||||
|
||||
# Author: Baris Metin <baris@uludag.org.tr
|
||||
|
||||
import pisi
|
||||
|
||||
class _constant:
|
||||
"Constant members implementation"
|
||||
class ConstError(TypeError):
|
||||
pass
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, "Can't rebind constant: %s" % name
|
||||
# Binding an attribute once to a const is available
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, "Can't unbind constant: %s" % name
|
||||
# we don't have an attribute by this name
|
||||
raise NameError, name
|
||||
|
||||
class Constants:
|
||||
"Pisi Constants Singleton"
|
||||
|
||||
__c = _constant()
|
||||
|
||||
def __init__(self):
|
||||
# prefix for package names
|
||||
self.__c.package_prefix = ".pisi"
|
||||
|
||||
# directory suffixes for build
|
||||
self.__c.work_dir_suffix = "/work"
|
||||
self.__c.install_dir_suffix = "/install"
|
||||
|
||||
# directory suffixes for intall. We'll use these directories
|
||||
# for storing files related to the packages. Each package will
|
||||
# have its own directory under lib_dir with has these
|
||||
# sub-directories.
|
||||
self.__c.comar_dir_suffix = "/comar"
|
||||
self.__c.files_dir_suffix = "/files"
|
||||
self.__c.metadata_dir_suffix = "/metadata"
|
||||
|
||||
# file/directory names
|
||||
self.__c.actions_file = "actions.py"
|
||||
self.__c.files_dir = "files"
|
||||
self.__c.comar_dir = "comar"
|
||||
self.__c.files_xml = "files.xml"
|
||||
self.__c.metadata_xml = "metadata.xml"
|
||||
self.__c.pisi_index = "pisi-index.xml"
|
||||
|
||||
# functions in actions_file
|
||||
self.__c.setup_func = "setup"
|
||||
self.__c.build_func = "build"
|
||||
self.__c.install_func = "install"
|
||||
|
||||
# file types
|
||||
self.__c.doc = "doc"
|
||||
self.__c.man = "man"
|
||||
self.__c.info = "info"
|
||||
self.__c.conf = "config"
|
||||
self.__c.header = "header"
|
||||
self.__c.library = "library"
|
||||
self.__c.executable = "executable"
|
||||
self.__c.data = "data"
|
||||
self.__c.localedata = "localedata"
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.__c, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
setattr(self.__c, attr, value)
|
||||
|
||||
def __delattr__(self, attr):
|
||||
delattr(self.__c, attr)
|
||||
@@ -1,36 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Context module.
|
||||
|
||||
# Authors: Baris Metin <baris@uludag.org.tr
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# global variables here
|
||||
|
||||
import pisi.constants
|
||||
|
||||
const = pisi.constants.Constants()
|
||||
|
||||
config = None
|
||||
|
||||
# default UI is CLI
|
||||
ui = None # not now
|
||||
|
||||
installdb = None
|
||||
repodb = None
|
||||
|
||||
comard = None
|
||||
|
||||
#def register(_impl):
|
||||
# """ Register a UI implementation"""
|
||||
# ui = _impl
|
||||
@@ -1,134 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# dependency analyzer
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
#import pisi.db as db
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
from pisi.version import Version
|
||||
from pisi.xmlext import *
|
||||
from pisi.xmlfile import XmlFile
|
||||
from pisi.util import Checks
|
||||
|
||||
class DepInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.package = getNodeText(node).strip()
|
||||
self.versionFrom = getNodeAttribute(node, "versionFrom")
|
||||
self.versionTo = getNodeAttribute(node, "versionTo")
|
||||
self.releaseFrom = getNodeAttribute(node, "releaseFrom")
|
||||
self.releaseTo = getNodeAttribute(node, "releaseTo")
|
||||
else:
|
||||
self.versionFrom = self.versionTo = None
|
||||
self.releaseFrom = self.releaseFrom = None
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Dependency")
|
||||
xml.addText(node, self.package)
|
||||
if self.versionFrom:
|
||||
node.setAttribute("versionFrom", self.versionFrom)
|
||||
if self.versionTo:
|
||||
node.setAttribute("versionTo", self.versionTo)
|
||||
if self.releaseFrom:
|
||||
node.setAttribute("releaseFrom", self.versionFrom)
|
||||
if self.releaseTo:
|
||||
node.setAttribute("releaseTo", self.versionTo)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
if not self.package:
|
||||
return [ "Dependency should have a package string" ]
|
||||
return None
|
||||
|
||||
def satisfies(self, pkg_name, version, release):
|
||||
"""determine if a package ver. satisfies given dependency spec"""
|
||||
ret = True
|
||||
from version import Version
|
||||
if self.versionFrom:
|
||||
ret &= Version(version) >= Version(self.versionFrom)
|
||||
if self.versionTo:
|
||||
ret &= Version(version) <= Version(self.versionTo)
|
||||
if self.releaseFrom:
|
||||
ret &= Version(release) <= Version(self.releaseFrom)
|
||||
if self.releaseTo:
|
||||
ret &= Version(release) <= Version(self.releaseTo)
|
||||
return ret
|
||||
|
||||
def __str__(self):
|
||||
s = self.package
|
||||
if self.versionFrom:
|
||||
s += 'ver >= ' + self.versionFrom
|
||||
if self.versionTo:
|
||||
s += 'ver <= ' + self.versionTo
|
||||
if self.releaseFrom:
|
||||
s += 'rel >= ' + self.releaseFrom
|
||||
if self.releaseTo:
|
||||
s += 'rel <= ' + self.releaseTo
|
||||
return s
|
||||
|
||||
def dict_satisfies_dep(dict, depinfo):
|
||||
"""determine if a package in a dictionary satisfies given dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not dict.has_key(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = dict[pkg_name]
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def installed_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.installdb.is_installed(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = packagedb.inst_packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def repo_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not packagedb.has_package(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
|
||||
for dep in deps:
|
||||
if not sat(dep):
|
||||
ctx.ui.error('Package %s does not satisfy dependency %s' %
|
||||
(pkg,dep))
|
||||
return False
|
||||
return True
|
||||
|
||||
def satisfies_runtime_deps(pkg):
|
||||
deps = packagedb.get_package(pkg).runtimeDeps
|
||||
return satisfies_dependencies(pkg, deps)
|
||||
|
||||
def installable(pkg):
|
||||
"""calculate if pkg is installable currently
|
||||
which means it has to satisfy both install and runtime dependencies"""
|
||||
if not packagedb.has_package(pkg):
|
||||
ctx.ui.info("Package " + pkg + " is not present in the package database");
|
||||
return False
|
||||
elif satisfies_runtime_deps(pkg):
|
||||
return True
|
||||
else:
|
||||
#ctx.ui.info("package " + pkg + " does not satisfy dependencies\n");
|
||||
return False
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as 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 os
|
||||
from base64 import encodestring
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
|
||||
|
||||
class FetchError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
# helper functions
|
||||
def fetch_url(url, dest, progress=None):
|
||||
fetch = Fetcher(url, dest)
|
||||
fetch.progress = progress
|
||||
fetch.fetch()
|
||||
if progress:
|
||||
pass
|
||||
#ctx.ui.info('\n')
|
||||
|
||||
|
||||
class Fetcher:
|
||||
"""Fetcher can fetch a file from various sources using various
|
||||
protocols."""
|
||||
def __init__(self, url, dest):
|
||||
if not isinstance(url, URI):
|
||||
url = URI(url)
|
||||
|
||||
self.url = url
|
||||
self.filedest = dest
|
||||
util.check_dir(self.filedest)
|
||||
self.percent = 0
|
||||
self.rate = 0.0
|
||||
self.progress = None
|
||||
|
||||
def fetch (self):
|
||||
"""Return value: Fetched file's full path.."""
|
||||
|
||||
if not self.url.filename():
|
||||
self.err("filename error")
|
||||
|
||||
if not os.access(self.filedest, os.W_OK):
|
||||
self.err("no perm to write to dest dir")
|
||||
|
||||
if self.url.is_local_file():
|
||||
self.fetchLocalFile()
|
||||
else:
|
||||
self.fetchRemoteFile()
|
||||
|
||||
return os.path.join(self.filedest, self.url.filename())
|
||||
|
||||
def _do_grab(self, fileURI, dest, totalsize):
|
||||
symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s']
|
||||
from time import time
|
||||
tt, oldsize = int(time()), 0
|
||||
bs, size = 1024, 0
|
||||
symbol, depth = "B/s", 0
|
||||
st = time()
|
||||
chunk = fileURI.read(bs)
|
||||
size = size + len(chunk)
|
||||
if self.progress:
|
||||
p = self.progress(totalsize)
|
||||
self.percent = p.update(size)
|
||||
while chunk:
|
||||
dest.write(chunk)
|
||||
chunk = fileURI.read(bs)
|
||||
size = size + len(chunk)
|
||||
ct = time()
|
||||
if int(tt) != int(ct):
|
||||
self.rate = size / (ct - st)
|
||||
while self.rate > 1000 and depth < 3:
|
||||
self.rate /= 1024
|
||||
depth += 1
|
||||
symbol, depth = symbols[depth], 0
|
||||
oldsize, tt = size, time()
|
||||
if self.progress:
|
||||
if p.update(size):
|
||||
self.percent = p.percent
|
||||
retval = {'filename': self.url.filename(),
|
||||
'percent' : self.percent,
|
||||
'rate': self.rate,
|
||||
'symbol': symbol}
|
||||
ctx.ui.display_progress(retval)
|
||||
|
||||
dest.close()
|
||||
|
||||
def fetchLocalFile (self):
|
||||
url = self.url
|
||||
|
||||
if not os.access(url.path(), os.F_OK):
|
||||
self.err("no such file or no perm to read")
|
||||
|
||||
dest = open(os.path.join(self.filedest, url.filename()) , "w")
|
||||
totalsize = os.path.getsize(url.path())
|
||||
fileObj = open(url.path())
|
||||
self._do_grab(fileObj, dest, totalsize)
|
||||
|
||||
def fetchRemoteFile (self):
|
||||
from httplib import HTTPException
|
||||
|
||||
try:
|
||||
fileObj = urllib2.urlopen(self.formatRequest\
|
||||
(urllib2.Request(self.url.uri)))
|
||||
headers = fileObj.info()
|
||||
|
||||
except ValueError, e:
|
||||
self.err('%s' % (e, ))
|
||||
except IOError, e:
|
||||
self.err('%s' % (e, ))
|
||||
except OSError, e:
|
||||
self.err('%s' % (e, ))
|
||||
except HTTPException, e:
|
||||
self.err(('(%s): %s') % (e.__class__.__name__, e))
|
||||
|
||||
try:
|
||||
totalsize = int(headers['Content-Length'])
|
||||
except:
|
||||
totalsize = 0
|
||||
|
||||
dest = open(os.path.join(self.filedest, self.url.filename()) , "w")
|
||||
self._do_grab(fileObj, dest, totalsize)
|
||||
|
||||
def formatRequest(self, request):
|
||||
authinfo = self.url.auth_info()
|
||||
if authinfo:
|
||||
enc = encodestring("%s:%s" % authinfo)
|
||||
request.add_header('Authorization', 'Basic %s' % enc)
|
||||
return request
|
||||
|
||||
def err (self, error):
|
||||
raise FetchError(error)
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Files module provides access to files.xml. files.xml is genarated
|
||||
# during the build process of a package and used in installation.
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
from pisi.xmlext import *
|
||||
from pisi.xmlfile import XmlFile
|
||||
from pisi.util import Checks
|
||||
|
||||
class FileInfo:
|
||||
"""FileInfo holds the information for a File node/tag in files.xml"""
|
||||
def __init__(self, _path = "", _type = "", _size = "", _hash = ""):
|
||||
self.path = _path
|
||||
self.type = _type
|
||||
self.size = _size
|
||||
self.hash = _hash
|
||||
|
||||
def readnew(node):
|
||||
f = FileInfo()
|
||||
f.read(node)
|
||||
return f
|
||||
readnew = staticmethod(readnew)
|
||||
|
||||
def read(self, node):
|
||||
self.path = getNodeText(getNode(node, "Path"))
|
||||
self.type = getNodeText(getNode(node, "Type"))
|
||||
self.size = getNodeText(getNode(node, "Size"))
|
||||
self.hash = getNodeText(getNode(node, "SHA1Sum"))
|
||||
|
||||
def elt(self, dom):
|
||||
## FIXME: looking for a better way to do it
|
||||
## could apparently use helper functions to do this shorter
|
||||
elt = dom.createElement("File")
|
||||
pathElt = dom.createElement("Path")
|
||||
pathElt.appendChild(dom.createTextNode(self.path))
|
||||
typeElt = dom.createElement("Type")
|
||||
typeElt.appendChild(dom.createTextNode(self.type))
|
||||
sizeElt = dom.createElement("Size")
|
||||
sizeElt.appendChild(dom.createTextNode(self.size))
|
||||
hashElt = dom.createElement("SHA1Sum")
|
||||
hashElt.appendChild(dom.createTextNode(self.hash))
|
||||
elt.appendChild(pathElt)
|
||||
elt.appendChild(typeElt)
|
||||
elt.appendChild(sizeElt)
|
||||
elt.appendChild(hashElt)
|
||||
return elt
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
err.has_tag(self.path, "File", "Path")
|
||||
err.has_tag(self.type, "File", "Type")
|
||||
err.has_tag(self.size, "File", "Size")
|
||||
err.has_tag(self.hash, "File", "SHA1Sum")
|
||||
return err.list
|
||||
|
||||
def __str__(self):
|
||||
s = "%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
|
||||
self.size, self.hash)
|
||||
return s
|
||||
|
||||
class Files(XmlFile):
|
||||
|
||||
def __init__(self):
|
||||
XmlFile.__init__(self, "Files")
|
||||
self.list = []
|
||||
|
||||
def append(self, fileinfo):
|
||||
self.list.append(fileinfo)
|
||||
|
||||
def read(self, filename):
|
||||
self.readxml(filename)
|
||||
|
||||
fileElts = self.getAllNodes("File")
|
||||
self.list = [FileInfo.readnew(x) for x in fileElts]
|
||||
|
||||
def write(self, filename):
|
||||
self.newDOM()
|
||||
document = self.dom.documentElement
|
||||
for x in self.list:
|
||||
document.appendChild(x.elt(self.dom))
|
||||
self.writexml(filename)
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
for finfo in self.list:
|
||||
err.join(finfo.has_errors())
|
||||
return err.list
|
||||
@@ -1,159 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# the most simple minded digraph class ever
|
||||
|
||||
|
||||
# for python 2.3 compatibility
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import pisi
|
||||
|
||||
# not an error!
|
||||
|
||||
class CycleException(pisi.Exception):
|
||||
pass
|
||||
|
||||
class Digraph(object):
|
||||
|
||||
def __init__(self):
|
||||
self.__v = set()
|
||||
self.__adj = {}
|
||||
self.__vdata = {}
|
||||
self.__edata = {}
|
||||
|
||||
def vertices(self):
|
||||
"return set of vertex descriptors"
|
||||
return self.__v
|
||||
|
||||
def edges(self):
|
||||
"return a list of edge descriptors"
|
||||
list = []
|
||||
for u in self.__v:
|
||||
for v in self.__u:
|
||||
list.append( (u,v) )
|
||||
return list
|
||||
|
||||
def from_list(self, el):
|
||||
"convert a list of edges (u,v) to graph"
|
||||
for (u,v) in el:
|
||||
self.add_edge(u,v)
|
||||
|
||||
def add_vertex(self, u, data = None):
|
||||
"add vertex u, optionally with data"
|
||||
assert not u in self.__v
|
||||
self.__v.add(u)
|
||||
self.__adj[u] = set()
|
||||
if data:
|
||||
self.__vdata[u] = data
|
||||
self.__edata[u] = {}
|
||||
|
||||
def add_edge(self, u, v, edata = None, udata = None, vdata = None):
|
||||
"add edge u -> v"
|
||||
if not u in self.__v:
|
||||
self.add_vertex(u, udata)
|
||||
if not v in self.__v:
|
||||
self.add_vertex(v, vdata)
|
||||
self.__adj[u].add(v)
|
||||
if edata != None:
|
||||
self.__edata[u][v] = edata
|
||||
|
||||
def add_biedge(self, u, v, edata = None):
|
||||
self.add_edge(u, v, edata)
|
||||
self.add_edge(v, u, edata)
|
||||
|
||||
def set_vertex_data(self, u, data):
|
||||
self.__vdata[u] = data
|
||||
|
||||
def vertex_data(self, u):
|
||||
return self.__vdata[u]
|
||||
|
||||
def edge_data(self, u, v):
|
||||
return self.__edata[u][v]
|
||||
|
||||
def has_vertex(self, u):
|
||||
return u in self.__v
|
||||
|
||||
def has_edge(self, u,v):
|
||||
if u in self.__v:
|
||||
return v in self.__adj[u]
|
||||
else:
|
||||
return False
|
||||
|
||||
def adj(self, u):
|
||||
return self.__adj[u]
|
||||
|
||||
def dfs(self, finish_hook = None):
|
||||
self.color = {}
|
||||
self.p = {}
|
||||
self.d = {}
|
||||
self.f = {}
|
||||
for u in self.__v:
|
||||
self.color[u] = 'w' # mark white (unexplored)
|
||||
self.p[u] = None
|
||||
self.time = 0
|
||||
for u in self.__v:
|
||||
if self.color[u] == 'w':
|
||||
self.dfs_visit(u, finish_hook)
|
||||
|
||||
def dfs_visit(self, u, finish_hook):
|
||||
self.color[u] = 'g' # mark green (discovered)
|
||||
self.d[u] = self.time = self.time + 1
|
||||
for v in self.adj(u):
|
||||
if self.color[v] == 'w': # explore unexplored vertices
|
||||
self.p[v] = u
|
||||
self.dfs_visit(v, finish_hook)
|
||||
elif self.color[v] == 'g': # cycle detected
|
||||
raise CycleException
|
||||
self.color[u] = 'b' # mark black (completed)
|
||||
if finish_hook:
|
||||
finish_hook(u)
|
||||
self.f[u] = self.time = self.time + 1
|
||||
|
||||
def cycle_free(self):
|
||||
try:
|
||||
self.dfs()
|
||||
return True
|
||||
except CycleException:
|
||||
return False
|
||||
|
||||
def topological_sort(self):
|
||||
list = []
|
||||
self.dfs(lambda u: list.append(u))
|
||||
list.reverse()
|
||||
return list
|
||||
|
||||
def id_str(self, u):
|
||||
s = str(u)
|
||||
return s.replace('-', '_')
|
||||
|
||||
def write_graphviz(self, f):
|
||||
f.write('digraph G {\n')
|
||||
for u in self.vertices():
|
||||
f.write(self.id_str(u))
|
||||
self.write_graphviz_vlabel(f, u)
|
||||
f.write(';\n')
|
||||
f.write('\n')
|
||||
for u in self.vertices():
|
||||
for v in self.adj(u):
|
||||
f.write( self.id_str(u) + ' -> ' + self.id_str(v))
|
||||
self.write_graphviz_elabel(f, u, v)
|
||||
f.write(';\n')
|
||||
f.write('\n')
|
||||
f.write('}\n')
|
||||
|
||||
def write_graphviz_vlabel(self, f, u):
|
||||
pass
|
||||
|
||||
def write_graphviz_elabel(self, f, u, v):
|
||||
pass
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI source/package index
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.metadata as metadata
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.package import Package
|
||||
from pisi.xmlfile import XmlFile
|
||||
from pisi.uri import URI
|
||||
|
||||
class Index(XmlFile):
|
||||
|
||||
def __init__(self):
|
||||
XmlFile.__init__(self,"PISI")
|
||||
self.sources = []
|
||||
self.packages = []
|
||||
|
||||
def read(self, filename, repo = None):
|
||||
"""Read PSPEC file"""
|
||||
|
||||
self.filepath = filename
|
||||
url = URI(filename)
|
||||
if url.is_remote_file():
|
||||
from fetcher import fetch_url
|
||||
|
||||
dest = os.path.join(ctx.config.index_dir(), repo)
|
||||
if not os.path.exists(dest):
|
||||
os.makedirs(dest)
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
|
||||
self.filepath = os.path.join(dest, url.filename())
|
||||
|
||||
self.readxml(self.filepath)
|
||||
|
||||
# find all binary packages
|
||||
packageElts = self.getAllNodes("Package")
|
||||
self.packages = [metadata.PackageInfo(p) for p in packageElts]
|
||||
|
||||
self.unlink()
|
||||
|
||||
def write(self, filename):
|
||||
"""Write index file"""
|
||||
self.newDOM()
|
||||
for pkg in self.packages:
|
||||
self.addChild(pkg.elt(self))
|
||||
self.writexml(filename)
|
||||
self.unlink()
|
||||
|
||||
def index(self, repo_uri):
|
||||
self.repo_dir = repo_uri
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn.endswith(ctx.const.package_prefix):
|
||||
ctx.ui.info('Adding ' + fn + ' to package index')
|
||||
self.add_package(os.path.join(root, fn), repo_uri)
|
||||
|
||||
def update_db(self, repo):
|
||||
pkgdb = packagedb.get_db(repo)
|
||||
pkgdb.clear()
|
||||
for pkg in self.packages:
|
||||
pkgdb.add_package(pkg)
|
||||
|
||||
def add_package(self, path, repo_uri):
|
||||
package = Package(path, 'r')
|
||||
# extract control files
|
||||
util.clean_dir(ctx.config.install_dir())
|
||||
package.extract_PISI_files(ctx.config.install_dir())
|
||||
|
||||
md = metadata.MetaData()
|
||||
md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml))
|
||||
if ctx.config.options and ctx.config.options.absolute_uris:
|
||||
md.package.packageURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
# TODO: in the future we'll do all of this with purl/pfile/&helpers
|
||||
# After that, we'll remove the ugly repo_uri parameter from this
|
||||
# function.
|
||||
md.package.packageURI = util.removepathprefix(repo_uri, path)
|
||||
# check package semantics
|
||||
if md.has_errors():
|
||||
ctx.ui.error('Package ' + md.package.name + ': metadata corrupt')
|
||||
else:
|
||||
self.packages.append(md.package)
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Package install operation
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import os
|
||||
|
||||
import pisi
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.dependency as dependency
|
||||
import pisi.operations as operations
|
||||
from pisi.specfile import *
|
||||
from pisi.package import Package
|
||||
from pisi.metadata import MetaData
|
||||
#import conflicts
|
||||
|
||||
class InstallError(pisi.Error):
|
||||
pass
|
||||
|
||||
class Installer:
|
||||
"Installer class, provides install routines for pisi packages"
|
||||
|
||||
def __init__(self, package_fname):
|
||||
"initialize from a file name"
|
||||
self.package = Package(package_fname)
|
||||
self.package.read()
|
||||
self.metadata = self.package.metadata
|
||||
self.files = self.package.files
|
||||
self.pkginfo = self.metadata.package
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
"entry point"
|
||||
ctx.ui.info('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.reinstall()
|
||||
self.extract_install()
|
||||
self.store_pisi_files()
|
||||
self.register_comar_scripts()
|
||||
self.update_databases()
|
||||
|
||||
def check_requirements(self):
|
||||
"""check system requirements"""
|
||||
#TODO: IS THERE ENOUGH SPACE?
|
||||
# what to do if / is split into /usr, /var, etc.
|
||||
pass
|
||||
|
||||
def check_relations(self):
|
||||
# check if package is in database
|
||||
# If it is not, put it into 3rd party packagedb
|
||||
if not packagedb.has_package(self.pkginfo.name):
|
||||
db = packagedb.thirdparty_packagedb
|
||||
db.add_package(self.pkginfo)
|
||||
|
||||
# check conflicts
|
||||
for pkg in self.metadata.package.conflicts:
|
||||
if ctx.installdb.is_installed(self.pkginfo):
|
||||
raise InstallError("Package conflicts " + pkg)
|
||||
|
||||
# check dependencies
|
||||
if not dependency.installable(self.pkginfo.name):
|
||||
ctx.ui.error('Dependencies for ' + self.pkginfo.name +
|
||||
' not satisfied')
|
||||
raise InstallError("Package not installable")
|
||||
|
||||
def reinstall(self):
|
||||
"check reinstall, confirm action, and remove package if reinstall"
|
||||
|
||||
pkg = self.pkginfo
|
||||
|
||||
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
|
||||
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
|
||||
|
||||
# determine if same version
|
||||
same_ver = False
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
if (not ibuild) or (not pkg.build) or ignore_build:
|
||||
# we don't look at builds to compare two package versions
|
||||
if pkg.version == iversion and pkg.release == irelease:
|
||||
same_ver = True
|
||||
else:
|
||||
if pkg.build == ibuild:
|
||||
same_ver = True
|
||||
|
||||
if same_ver:
|
||||
if self.ask_reinstall:
|
||||
if not ctx.ui.confirm('Re-install same version package?'):
|
||||
raise InstallError('Package re-install declined')
|
||||
else:
|
||||
upgrade = False
|
||||
# is this an upgrade?
|
||||
# determine and report the kind of upgrade: version, release, build
|
||||
if pkg.version > iversion:
|
||||
ctx.ui.info('Upgrading to new upstream version')
|
||||
upgrade = True
|
||||
elif pkg.release > irelease:
|
||||
ctx.ui.info('Upgrading to new distribution release')
|
||||
upgrade = True
|
||||
elif ((not ignore_build) and ibuild and pkg.build
|
||||
and pkg.build > ibuild):
|
||||
ctx.ui.info('Upgrading to new distribution build')
|
||||
upgrade = True
|
||||
|
||||
# is this a downgrade? confirm this action.
|
||||
if self.ask_reinstall and (not upgrade):
|
||||
if pkg.version < iversion:
|
||||
x = 'Downgrade to old upstream version?'
|
||||
elif pkg.release < irelease:
|
||||
x = 'Downgrade to old distribution release?'
|
||||
else:
|
||||
x = 'Downgrade to old distribution build?'
|
||||
if not ctx.ui.confirm(x):
|
||||
raise InstallError('Package downgrade declined')
|
||||
|
||||
# remove old package then
|
||||
operations.remove_single(pkg.name)
|
||||
|
||||
def extract_install(self):
|
||||
"unzip package in place"
|
||||
|
||||
ctx.ui.info('Extracting files,')
|
||||
self.package.extract_dir_flat('install', ctx.config.destdir)
|
||||
|
||||
def store_pisi_files(self):
|
||||
"""put files.xml, metadata.xml, actions.py and COMAR scripts
|
||||
somewhere in the file system. We'll need these in future..."""
|
||||
|
||||
ctx.ui.info('Storing %s, ' % ctx.const.files_xml)
|
||||
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
|
||||
|
||||
ctx.ui.info('%s.' % ctx.const.metadata_xml)
|
||||
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
|
||||
# comar prefix is added to the pkg_dir while extracting comar
|
||||
# script file. so we'll use pkg_dir as destination.
|
||||
ctx.ui.info('Storing %s' % fpath)
|
||||
self.package.extract_file(fpath, self.package.pkg_dir())
|
||||
|
||||
def register_comar_scripts(self):
|
||||
"register COMAR scripts"
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script)
|
||||
ctx.ui.info("Registering COMAR script %s" % pcomar.script)
|
||||
# FIXME: We must check the result of the command (possibly
|
||||
# with id?)
|
||||
if comard:
|
||||
comard.register(pcomar.om,
|
||||
self.metadata.package.name,
|
||||
scriptPath)
|
||||
|
||||
|
||||
def update_databases(self):
|
||||
"update databases"
|
||||
|
||||
# installdb
|
||||
ctx.installdb.install(self.metadata.package.name,
|
||||
self.metadata.package.version,
|
||||
self.metadata.package.release,
|
||||
self.metadata.package.build,
|
||||
self.metadata.package.distribution)
|
||||
|
||||
# installed packages
|
||||
packagedb.inst_packagedb.add_package(self.pkginfo)
|
||||
@@ -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.
|
||||
#
|
||||
#
|
||||
# installation database
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# System
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
class InstallDBError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class InstallInfo:
|
||||
# some data is replicated from packagedb.inst_packagedb
|
||||
# we store as an object, hey, we can waste O(1) space.
|
||||
# this is also easier to modify in the future, without
|
||||
# requiring database upgrades! wow!
|
||||
def __init__(self, state, version, release, build, distribution):
|
||||
self.state = state
|
||||
self.version = version
|
||||
self.release = release
|
||||
self.build = build
|
||||
self.distribution = distribution
|
||||
import time
|
||||
self.time = time.localtime()
|
||||
|
||||
def one_liner(self):
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release,
|
||||
self.build, self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
state_map = { 'i': 'installed', 'ip':'installed-pending', 'r:removed'
|
||||
'p': 'purged' }
|
||||
|
||||
def __str__(self):
|
||||
s = "State: %s\nVersion: %s, Release: %s, Build: %s\n" % \
|
||||
(InstallInfo.state_map[self.state], self.version,
|
||||
self.release, self.build)
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s += 'Distribution: %s, Install Time: %s\n' % (self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
|
||||
class InstallDB:
|
||||
|
||||
def __init__(self):
|
||||
from os.path import join
|
||||
self.d = shelve.LockedDBShelf('install')
|
||||
self.dp = shelve.LockedDBShelf('configpending')
|
||||
self.files_dir = os.path.join(ctx.config.db_dir(), 'files')
|
||||
|
||||
def files_name(self, pkg, version, release):
|
||||
from os.path import join
|
||||
pkg_dir = join(ctx.config.lib_dir(), pkg + '-' + version + '-' + release)
|
||||
return join(pkg_dir, ctx.const.files_xml)
|
||||
|
||||
def files(self, pkg):
|
||||
pkg = str(pkg)
|
||||
pkginfo = self.d[pkg]
|
||||
files = Files()
|
||||
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
|
||||
return files
|
||||
|
||||
def is_recorded(self, pkg):
|
||||
pkg = str(pkg)
|
||||
return self.d.has_key(pkg)
|
||||
|
||||
def is_installed(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.is_recorded(pkg):
|
||||
info = self.d[pkg]
|
||||
return info.state=='i' or info.state=='ip'
|
||||
else:
|
||||
return False
|
||||
|
||||
def list_installed(self):
|
||||
list = []
|
||||
for (pkg, info) in self.d.iteritems():
|
||||
if info.state=='i' or info.state=='ip':
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
def list_pending(self):
|
||||
list = []
|
||||
for (pkg, x) in self.dp.iteritems():
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
def get_info(self, pkg):
|
||||
pkg = str(pkg)
|
||||
return self.d[pkg]
|
||||
|
||||
def get_version(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
return (info.version, info.release, info.build)
|
||||
|
||||
def is_removed(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.is_recorded(pkg):
|
||||
info = self.d[pkg]
|
||||
return info.state=='r'
|
||||
else:
|
||||
return False
|
||||
|
||||
def install(self, pkg, version, release, build, distro = ""):
|
||||
"""install package with specific version, release, build"""
|
||||
pkg = str(pkg)
|
||||
if self.is_installed(pkg):
|
||||
raise InstallDBError("already installed")
|
||||
if ctx.config.options and ctx.config.options.ignore_comar:
|
||||
state = 'ip'
|
||||
self.dp[pkg] = True
|
||||
else:
|
||||
state = 'i'
|
||||
|
||||
self.d[pkg] = InstallInfo(state, version, release, build, distro)
|
||||
|
||||
def remove(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
info.state = 'r'
|
||||
self.d[pkg] = info
|
||||
|
||||
def purge(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.d.has_key(pkg):
|
||||
del self.d[pkg]
|
||||
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = InstallDB()
|
||||
return db
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# A simple wrapper to implement locking for bsddb's dbshelf
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import bsddb.db as db
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import pisi
|
||||
import pisi.context
|
||||
|
||||
class LockedDBShelf(shelve.DBShelf):
|
||||
|
||||
def __init__(self, dbname, flags=db.DB_CREATE, mode=0660,
|
||||
filetype=db.DB_HASH, dbenv=None):
|
||||
shelve.DBShelf.__init__(self, dbenv)
|
||||
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"
|
||||
filename = os.path.join( pisi.context.config.db_dir(), dbname + '.bdb')
|
||||
self.open(filename, dbname, filetype, flags, mode)
|
||||
|
||||
def open(self, filename, dbname, filetype, flags, mode):
|
||||
pisi.util.check_dir(pisi.context.config.db_dir())
|
||||
self.lockfile = file(filename + '.lock', 'w')
|
||||
try:
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
import sys
|
||||
pisi.context.ui.error("Another instance of PISI is running. Try later!")
|
||||
sys.exit(1)
|
||||
return self.db.open(filename, dbname, filetype, flags, mode)
|
||||
|
||||
def close(self):
|
||||
self.db.close()
|
||||
self.lockfile.close()
|
||||
@@ -1,150 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# 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.
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
from pisi.xmlfile import *
|
||||
from pisi.util import Checks
|
||||
|
||||
class SourceInfo:
|
||||
|
||||
def __init__(self, node=None):
|
||||
if node:
|
||||
self.name = getNodeText(node, "Name")
|
||||
self.homepage = getNodeText(node, "HomePage")
|
||||
self.packager = specfile.PackagerInfo(getNode(node, "Packager"))
|
||||
else:
|
||||
self.homepage = None
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Source")
|
||||
xml.addTextNodeUnder(node, "Name", self.name)
|
||||
if self.homepage:
|
||||
xml.addTextNodeUnder(node, "Homepage", self.homepage)
|
||||
node.appendChild(self.packager.elt(xml))
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
if not self.name:
|
||||
return [ "SourceInfo should have a Name" ]
|
||||
return None
|
||||
|
||||
|
||||
class PackageInfo(specfile.PackageInfo):
|
||||
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
specfile.PackageInfo.__init__(self, node)
|
||||
self.version = getNodeText(node, "History/Update/Version")
|
||||
self.release = getNodeText(node, "History/Update/Release")
|
||||
build_ = getNodeText(node, "History/Update/Build")
|
||||
if build_ != None:
|
||||
self.build = int(build_)
|
||||
else:
|
||||
self.build = None
|
||||
self.distribution = getNodeText(node, "Distribution")
|
||||
self.distributionRelease = getNodeText(node, "DistributionRelease")
|
||||
self.architecture = getNodeText(node, "Architecture")
|
||||
self.installedSize = int(getNodeText(node, "InstalledSize"))
|
||||
self.packageURI = getNodeText(node, "PackageURI")
|
||||
else:
|
||||
self.packageURI = None
|
||||
|
||||
def elt(self, xml):
|
||||
node = specfile.PackageInfo.elt(self, xml)
|
||||
if self.build != None:
|
||||
xml.addTextNodeUnder(node, "Build", str(self.build))
|
||||
xml.addTextNodeUnder(node, "Distribution", self.distribution)
|
||||
xml.addTextNodeUnder(node, "DistributionRelease", self.distributionRelease)
|
||||
xml.addTextNodeUnder(node, "Architecture", self.architecture)
|
||||
xml.addTextNodeUnder(node, "InstalledSize", str(self.installedSize))
|
||||
if self.packageURI:
|
||||
xml.addTextNodeUnder(node, "PackageURI", str(self.packageURI))
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
# FIXME: there should be real error msgs
|
||||
# and comment the logic here please, it isn't very clear -gurer
|
||||
ret = (specfile.PackageInfo.has_errors(self) == None)
|
||||
ret = ret and self.distribution!=None
|
||||
ret = ret and self.distributionRelease!=None
|
||||
ret = ret and self.architecture!=None and self.installedSize!=None
|
||||
if ret:
|
||||
return None
|
||||
return [ "Some error in package metadata" ]
|
||||
|
||||
def __str__(self):
|
||||
s = specfile.PackageInfo.__str__(self)
|
||||
return s
|
||||
|
||||
class MetaData(XmlFile):
|
||||
"""Package metadata. Metadata is composed of Specfile and various
|
||||
other information. A metadata has two parts, Source and Package."""
|
||||
|
||||
def __init__(self):
|
||||
XmlFile.__init__(self, "PISI")
|
||||
|
||||
def from_spec(self, src, pkg):
|
||||
self.source = SourceInfo()
|
||||
self.source.name = src.name
|
||||
self.source.homepage = src.homepage
|
||||
self.source.packager = src.packager
|
||||
self.package = PackageInfo()
|
||||
self.package.name = pkg.name
|
||||
self.package.summary = pkg.summary
|
||||
self.package.description = pkg.description
|
||||
self.package.icon = pkg.icon
|
||||
self.package.isa = pkg.isa
|
||||
self.package.partof = pkg.partof
|
||||
self.package.license = pkg.license
|
||||
self.package.runtimeDeps = pkg.runtimeDeps
|
||||
self.package.paths = pkg.paths
|
||||
self.package.history = src.history # FIXME
|
||||
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
|
||||
|
||||
def read(self, filename):
|
||||
self.readxml(filename)
|
||||
self.source = SourceInfo(self.getNode("Source"))
|
||||
self.package = PackageInfo(self.getNode("Package"))
|
||||
|
||||
def write(self, filename):
|
||||
self.newDOM()
|
||||
self.addChild(self.source.elt(self))
|
||||
self.addChild(self.package.elt(self))
|
||||
self.writexml(filename)
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
# FIXME: is this an internal error?? -gurer
|
||||
if not hasattr(self, 'source'):
|
||||
err.add("Metadata should have source")
|
||||
err.join(self.source.has_errors())
|
||||
|
||||
if not self.package:
|
||||
err.add("Metadata should have a package")
|
||||
err.join(self.package.has_errors())
|
||||
return err.list
|
||||
@@ -1,105 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
|
||||
"Package Operations: install/remove/upgrade"
|
||||
|
||||
import os
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.util as util
|
||||
import pisi.packagedb as packagedb
|
||||
from pisi.uri import URI
|
||||
|
||||
# single package operations
|
||||
|
||||
def remove_single(package_name):
|
||||
"""Remove a single package"""
|
||||
inst_packagedb = packagedb.inst_packagedb
|
||||
|
||||
#TODO: check dependencies
|
||||
|
||||
ctx.ui.info('Removing package %s' % package_name)
|
||||
if not ctx.installdb.is_installed(package_name):
|
||||
raise Exception('Trying to remove nonexistent package '
|
||||
+ package_name)
|
||||
if ctx.comard:
|
||||
ctx.comard.remove(package_name)
|
||||
ctx.comard.call("System.Package", "preremove")
|
||||
for fileinfo in ctx.installdb.files(package_name).list:
|
||||
fpath = os.path.join(ctx.config.destdir, fileinfo.path)
|
||||
# TODO: We have to store configuration files for futher
|
||||
# usage. Currently we'are doing it like rpm does, saving
|
||||
# with a prefix and leaving the user to edit it. In the future
|
||||
# we'll have a plan for these configuration files.
|
||||
if fileinfo.type == ctx.const.conf:
|
||||
if os.path.isfile(fpath):
|
||||
os.rename(fpath, fpath + ".pisi")
|
||||
else:
|
||||
# check if file is removed manually.
|
||||
# And we don't remove directories!
|
||||
# FIXME: should give a warning if it is...
|
||||
if os.path.isfile(fpath):
|
||||
os.unlink(fpath)
|
||||
|
||||
|
||||
ctx.installdb.remove(package_name)
|
||||
packagedb.remove_package(package_name)
|
||||
|
||||
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"""
|
||||
from install import Installer
|
||||
Installer(pkg_location).install(not upgrade)
|
||||
|
||||
def install_single_name(name, upgrade = False):
|
||||
"""install a single package from ID"""
|
||||
# find package in repository
|
||||
repo = packagedb.which_repo(name)
|
||||
if repo:
|
||||
repo = ctx.repodb.get_repo(repo)
|
||||
pkg = packagedb.get_package(name)
|
||||
|
||||
# FIXME: let pkg.packageURI be stored as URI type rather than string
|
||||
pkg_uri = URI(pkg.packageURI)
|
||||
if pkg_uri.is_absolute_path():
|
||||
pkg_path = str(pkg.packageURI)
|
||||
else:
|
||||
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(pkg_uri.path()))
|
||||
|
||||
ctx.ui.debug("Package URI: %s" % pkg_path)
|
||||
|
||||
# Package will handle remote file for us!
|
||||
install_single_file(pkg_path, upgrade)
|
||||
else:
|
||||
ctx.ui.error("Package %s not found in any active repository." % pkg)
|
||||
|
||||
# deneme, don't remove ulan
|
||||
class AtomicOperation(object):
|
||||
def __init__(self, package, ignore_dep = False):
|
||||
self.package = package
|
||||
self.ignore_dep = ignore_dep
|
||||
|
||||
def run(self, package):
|
||||
"perform an atomic package operation"
|
||||
pass
|
||||
@@ -1,114 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package abstraction
|
||||
# provides methods to add/remove files, extract control files
|
||||
|
||||
# maintainer: baris and meren
|
||||
|
||||
from os.path import join, exists
|
||||
|
||||
import 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
|
||||
|
||||
class PackageError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Package:
|
||||
"""PISI Package Class provides access to a pisi package (.pisi
|
||||
file)."""
|
||||
def __init__(self, packagefn, mode='r'):
|
||||
self.filepath = packagefn
|
||||
url = URI(packagefn)
|
||||
|
||||
if url.is_remote_file():
|
||||
from fetcher import fetch_url
|
||||
dest = ctx.config.packages_dir()
|
||||
self.filepath = join(dest, url.filename())
|
||||
|
||||
# FIXME: exists is not enough, also sha1sum check needed \
|
||||
# when implemented in pisi-index.xml
|
||||
if not exists(self.filepath):
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
else:
|
||||
ctx.ui.info('%s [cached]' % url.filename())
|
||||
|
||||
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
|
||||
|
||||
def add_to_package(self, fn):
|
||||
"""Add a file or directory to package"""
|
||||
self.impl.add_to_archive(fn)
|
||||
|
||||
def close(self):
|
||||
"""Close the package archive"""
|
||||
self.impl.close()
|
||||
|
||||
def extract(self, outdir):
|
||||
"""Extract entire package contents to directory"""
|
||||
self.extract_dir('', outdir) # means package root
|
||||
|
||||
def extract_files(self, paths, outdir):
|
||||
"""Extract paths to outdir"""
|
||||
self.impl.unpack_files(paths, outdir)
|
||||
|
||||
def extract_file(self, path, outdir):
|
||||
"""Extract file with path to outdir"""
|
||||
self.extract_files([path], outdir)
|
||||
|
||||
def extract_dir(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
copies the directory archiveroot/dir to outdir"""
|
||||
self.impl.unpack_dir(dir, outdir)
|
||||
|
||||
def extract_dir_flat(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
unpacks the *contents* of directory archiveroot/dir inside outdir
|
||||
this is the function used by the installer"""
|
||||
self.impl.unpack_dir_flat(dir, outdir)
|
||||
|
||||
def extract_PISI_files(self, outdir):
|
||||
"""Extract PISI control files: metadata.xml, files.xml,
|
||||
action scripts, etc."""
|
||||
self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
|
||||
self.extract_dir('config', outdir)
|
||||
|
||||
def read(self, outdir = None):
|
||||
if not outdir:
|
||||
outdir = ctx.config.tmp_dir()
|
||||
|
||||
# extract control files
|
||||
self.extract_PISI_files(outdir)
|
||||
|
||||
self.metadata = MetaData()
|
||||
self.metadata.read( join(outdir, ctx.const.metadata_xml) )
|
||||
if self.metadata.has_errors():
|
||||
raise PackageError, "MetaData format wrong"
|
||||
|
||||
self.files = Files()
|
||||
self.files.read( join(outdir, ctx.const.files_xml) )
|
||||
if self.files.has_errors():
|
||||
raise PackageError, "invalid %s" % ctx.const.files_xml
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.metadata.package.name + '-' \
|
||||
+ self.metadata.package.version + '-' \
|
||||
+ self.metadata.package.release
|
||||
|
||||
return join( ctx.config.lib_dir(), packageDir)
|
||||
|
||||
def comar_dir(self):
|
||||
return self.pkg_dir() + ctx.const.comar_dir_suffix
|
||||
@@ -1,161 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package database
|
||||
# interface for update/query to local package repository
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
# we basically store everything in PackageInfo class
|
||||
# yes, we are cheap
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import os, fcntl
|
||||
from bsddb import db
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class PackageDB(object):
|
||||
"""PackageDB class provides an interface to the package database with
|
||||
a delegated dbshelve object"""
|
||||
def __init__(self, id):
|
||||
util.check_dir(ctx.config.db_dir())
|
||||
self.fname = os.path.join(ctx.config.db_dir(), 'package-%s.bdb' % id )
|
||||
self.fname2 = os.path.join(ctx.config.db_dir(), 'revdep-%s.bdb' % id )
|
||||
self.lockfile = file(self.fname + '.lock', 'w')
|
||||
try:
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError, e:
|
||||
import sys
|
||||
ctx.ui.error("PackageDB: %s" % e)
|
||||
sys.exit(1)
|
||||
self.d = shelve.open(self.fname)
|
||||
self.dr = shelve.open(self.fname2)
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
#self.d.close()
|
||||
#self.dr.close()
|
||||
#self.lockfile.close()
|
||||
|
||||
def has_package(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key(name)
|
||||
|
||||
def get_package(self, name):
|
||||
name = str(name)
|
||||
return self.d[name]
|
||||
|
||||
def get_rev_deps(self, name):
|
||||
name = str(name)
|
||||
if self.dr.has_key(name):
|
||||
return self.dr[name]
|
||||
else:
|
||||
return []
|
||||
|
||||
def list_packages(self):
|
||||
list = []
|
||||
for (pkg, x) in self.d.iteritems():
|
||||
list.append(pkg)
|
||||
return list
|
||||
|
||||
#TODO: list_upgrades?
|
||||
|
||||
def add_package(self, package_info):
|
||||
name = str(package_info.name)
|
||||
self.d[name] = package_info
|
||||
for dep in package_info.runtimeDeps:
|
||||
dep_name = str(dep.package)
|
||||
if self.dr.has_key(dep_name):
|
||||
self.dr[dep_name].append( (name, dep) )
|
||||
else:
|
||||
self.dr[dep_name] = [ (name, dep) ]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_package(self, name):
|
||||
name = str(name)
|
||||
del self.d[name]
|
||||
|
||||
|
||||
packagedbs = {}
|
||||
|
||||
def add_db(name):
|
||||
packagedbs[name] = PackageDB('repo-' + name)
|
||||
|
||||
def get_db(name):
|
||||
return packagedbs[name]
|
||||
|
||||
def remove_db(name):
|
||||
del packagedbs[name]
|
||||
#erase database file
|
||||
|
||||
def has_package(name):
|
||||
repo = which_repo(name)
|
||||
if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def which_repo(name):
|
||||
import pisi.repodb
|
||||
for repo in pisi.repodb.db.list():
|
||||
if get_db(repo).has_package(name):
|
||||
return repo
|
||||
return None
|
||||
|
||||
def get_package(name):
|
||||
repo = which_repo(name)
|
||||
if repo:
|
||||
return get_db(repo).get_package(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
return thirdparty_packagedb.get_package(name)
|
||||
if inst_packagedb.has_package(name):
|
||||
return inst_packagedb.get_package(name)
|
||||
raise Error('get_package: package %s not found' % name)
|
||||
|
||||
def get_rev_deps(name):
|
||||
repo = which_repo(name)
|
||||
if repo:
|
||||
return get_db(repo).get_rev_deps(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
return thirdparty_packagedb.get_rev_deps(name)
|
||||
if inst_packagedb.has_package(name):
|
||||
return inst_packagedb.get_rev_deps(name)
|
||||
|
||||
return None
|
||||
|
||||
def remove_package(name):
|
||||
# remove the guy from the tracking databases
|
||||
inst_packagedb.remove_package(name)
|
||||
if thirdparty_packagedb.has_package(name):
|
||||
thirdparty_packagedb.remove_package(name)
|
||||
|
||||
# tracking databases for non-repository information
|
||||
|
||||
thirdparty_packagedb = None
|
||||
inst_packagedb = None
|
||||
|
||||
def init():
|
||||
global thirdparty_packagedb
|
||||
global inst_packagedb
|
||||
|
||||
if not thirdparty_packagedb:
|
||||
thirdparty_packagedb = PackageDB('thirdparty')
|
||||
if not inst_packagedb:
|
||||
inst_packagedb = PackageDB('installed')
|
||||
@@ -1,79 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI package relation graph that represents the state of packagedb
|
||||
|
||||
from graph import *
|
||||
|
||||
# Cache the results from packagedb queries in a graph
|
||||
|
||||
class PGraph(Digraph):
|
||||
|
||||
def __init__(self, packagedb):
|
||||
super(PGraph, self).__init__()
|
||||
self.packagedb = packagedb
|
||||
|
||||
def add_package(self, pkg):
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
self.add_vertex(str(pkg), (pkg1.version, pkg1.release))
|
||||
|
||||
def add_plain_dep(self, pkg1name, pkg2name):
|
||||
pkg1data = None
|
||||
if not pkg1name in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg1name)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg2name in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg2name)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_dep(self, pkg, depinfo):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(depinfo.package)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_rev_dep(self, depinfo, pkg):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(depinfo.package)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo),
|
||||
pkg1data, pkg2data )
|
||||
|
||||
def add_conflict(self, pkg, conflinfo):
|
||||
pkg1data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg1 = self.packagedb.get_package(pkg)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(conflinfo.package)
|
||||
pkg2data = (pkg2.version, pkg2.release)
|
||||
|
||||
self.add_biedge(str(pkg), str(conflinfo.package), ('c', conflinfo)
|
||||
, pkg1data, pkg2data )
|
||||
|
||||
def write_graphviz_vlabel(self, f, u):
|
||||
(v, r) = self.vertex_data(u)
|
||||
f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]')
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Author: Eray Ozkural
|
||||
|
||||
from bsddb import db
|
||||
import os, fcntl
|
||||
|
||||
import pisi
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.uri import URI
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Repo:
|
||||
def __init__(self, indexuri):
|
||||
self.indexuri = indexuri
|
||||
|
||||
#class HttpRepo
|
||||
|
||||
#class FtpRepo
|
||||
|
||||
#class RemovableRepo
|
||||
|
||||
|
||||
class RepoDB(object):
|
||||
"""RepoDB maps repo ids to repository information"""
|
||||
|
||||
def __init__(self):
|
||||
self.d = shelve.LockedDBShelf("repo")
|
||||
if not self.d.has_key("order"):
|
||||
self.d["order"] = []
|
||||
|
||||
def init_dbs(self):
|
||||
# initialize package/source dbs
|
||||
for x in self.list():
|
||||
packagedb.add_db(x)
|
||||
|
||||
def __del__(self):
|
||||
self.d.close()
|
||||
|
||||
def repo_name(self, ix):
|
||||
l = self.list()
|
||||
return l[ix]
|
||||
|
||||
def swap(self, x,y):
|
||||
l = d["order"]
|
||||
t = l[x]
|
||||
l[x] = l[y]
|
||||
l[y] = t
|
||||
d["order"] = l
|
||||
|
||||
def has_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key("repo-" + name)
|
||||
|
||||
def get_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d["repo-" + name]
|
||||
|
||||
def add_repo(self, name, repo_info):
|
||||
if self.d.has_key("repo-" + name):
|
||||
raise Error('Repository %s already exists' % name)
|
||||
self.d["repo-" + name] = repo_info
|
||||
order = self.d["order"]
|
||||
order.append(name)
|
||||
self.d["order"] = order
|
||||
packagedb.add_db(name)
|
||||
|
||||
def list(self):
|
||||
return self.d["order"]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_repo(self, name):
|
||||
name = str(name)
|
||||
del self.d["repo-" + name]
|
||||
l = self.d["order"]
|
||||
l.remove(name)
|
||||
self.d["order"] = l
|
||||
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = RepoDB()
|
||||
db.init_dbs()
|
||||
return db
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@uludag.org.tr>
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# python standard library
|
||||
|
||||
from os.path import join
|
||||
from os import access, R_OK
|
||||
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.archive import Archive
|
||||
from pisi.uri import URI
|
||||
from pisi.fetcher import fetch_url
|
||||
|
||||
class SourceArchiveError(pisi.Error):
|
||||
pass
|
||||
|
||||
class SourceArchive:
|
||||
"""source archive. this is a class responsible for fetching
|
||||
and unpacking a source archive"""
|
||||
def __init__(self, bctx):
|
||||
self.url = URI(bctx.spec.source.archiveUri)
|
||||
self.archiveFile = join(ctx.config.archives_dir(), self.url.filename())
|
||||
self.archiveName = bctx.spec.source.archiveName
|
||||
self.archiveType = bctx.spec.source.archiveType
|
||||
self.archiveSHA1 = bctx.spec.source.archiveSHA1
|
||||
self.bctx = bctx
|
||||
|
||||
def fetch(self, interactive=True):
|
||||
if not self.is_cached(interactive):
|
||||
if interactive:
|
||||
progress = ctx.ui.Progress
|
||||
else: progress = None
|
||||
fetch_url(self.url, ctx.config.archives_dir(), progress)
|
||||
|
||||
def is_cached(self, interactive=True):
|
||||
if not access(self.archiveFile, R_OK):
|
||||
return False
|
||||
|
||||
# check hash
|
||||
if util.check_file_hash(self.archiveFile, self.archiveSHA1):
|
||||
if interactive:
|
||||
ctx.ui.info('%s [cached]' % self.archiveName)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def unpack(self, cleanDir=True):
|
||||
|
||||
# check archive file's integrity
|
||||
if not util.check_file_hash(self.archiveFile, self.archiveSHA1):
|
||||
raise SourceArchiveError, "unpack: check_file_hash failed"
|
||||
|
||||
archive = Archive(self.archiveFile, self.archiveType)
|
||||
archive.unpack(self.bctx.pkg_work_dir(), cleanDir)
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# package source database
|
||||
# interface for update/query to local package repository
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# we basically store everything in sourceinfo class
|
||||
# yes, we are cheap
|
||||
|
||||
import bsddb.dbshelve as shelve
|
||||
import os
|
||||
import fcntl
|
||||
from bsddb import db
|
||||
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
|
||||
class SourceDB(object):
|
||||
|
||||
def __init__(self):
|
||||
util.check_dir(ctx.config.db_dir())
|
||||
self.filename = os.path.join(ctx.config.db_dir(), 'source.bdb')
|
||||
self.d = shelve.open(self.filename)
|
||||
self.fdummy = file(self.filename + '.lock', 'w')
|
||||
fcntl.flock(self.fdummy, fcntl.LOCK_EX)
|
||||
|
||||
def __del__(self):
|
||||
#fcntl.flock(self.fdummy, fcntl.LOCK_UN)
|
||||
self.fdummy.close()
|
||||
#os.unlink(self.filename + '.lock')
|
||||
|
||||
def has_source(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key(name)
|
||||
|
||||
def get_source(self, name):
|
||||
name = str(name)
|
||||
return self.d[name]
|
||||
|
||||
def add_source(self, source_info):
|
||||
# FIXME: how can you make a negative assertion -gurer
|
||||
# and yes i'm not very clever :)
|
||||
# assert source_info.has_errors()
|
||||
name = str(source_info.name)
|
||||
self.d[name] = source_info
|
||||
|
||||
def remove_source(self, name):
|
||||
name = str(name)
|
||||
del self.d[name]
|
||||
|
||||
sourcedb = None
|
||||
|
||||
def init():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
return sourcedb
|
||||
|
||||
sourcedb = SourceDB()
|
||||
return sourcedb
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
from os.path import basename, dirname, join
|
||||
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
class SourceFetcher(object):
|
||||
def __init__(self, url, authInfo=None):
|
||||
self.url = url
|
||||
if authInfo:
|
||||
self.url.set_auth_info(authInfo)
|
||||
self.location = dirname(self.url.uri)
|
||||
|
||||
pkgname = basename(dirname(self.url.path()))
|
||||
self.dest = join(ctx.config.tmp_dir(), pkgname)
|
||||
|
||||
def fetch_all(self):
|
||||
# fetch pspec file
|
||||
self.fetch()
|
||||
pspec = join(self.dest, self.url.filename())
|
||||
self.spec = SpecFile()
|
||||
self.spec.read(pspec)
|
||||
|
||||
self.fetch_actionsfile()
|
||||
self.fetch_patches()
|
||||
self.fetch_comarfiles()
|
||||
self.fetch_additionalFiles()
|
||||
|
||||
return pspec
|
||||
|
||||
def fetch_actionsfile(self):
|
||||
actionsuri = join(self.location, ctx.const.actions_file)
|
||||
self.url.uri = actionsuri
|
||||
self.fetch()
|
||||
|
||||
def fetch_patches(self):
|
||||
spec = self.spec
|
||||
for patch in spec.source.patches:
|
||||
patchuri = join(self.location,
|
||||
ctx.const.files_dir, patch.filename)
|
||||
self.url.uri = patchuri
|
||||
self.fetch(ctx.const.files_dir)
|
||||
|
||||
def fetch_comarfiles(self):
|
||||
spec = self.spec
|
||||
for package in spec.packages:
|
||||
for pcomar in package.providesComar:
|
||||
comaruri = join(self.location,
|
||||
ctx.const.comar_dir, pcomar.script)
|
||||
self.url.uri = comaruri
|
||||
self.fetch(ctx.const.comar_dir)
|
||||
|
||||
def fetch_additionalFiles(self):
|
||||
spec = self.spec
|
||||
for pkg in spec.packages:
|
||||
for afile in pkg.additionalFiles:
|
||||
afileuri = join(self.location,
|
||||
ctx.const.files_dir, afile.filename)
|
||||
self.url.uri = afileuri
|
||||
self.fetch(ctx.const.files_dir)
|
||||
|
||||
def fetch(self, appendDest=""):
|
||||
from fetcher import fetch_url
|
||||
|
||||
ctx.ui.info("Fetching %s" % self.url.uri)
|
||||
dest = join(self.dest, appendDest)
|
||||
fetch_url(self.url, dest)
|
||||
|
||||
@@ -1,471 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
# History:
|
||||
# Baris wrote the first version, then Baris and Eray did
|
||||
# several revisions of it. It was rewritten in accordance
|
||||
# with Gurer's observations.
|
||||
|
||||
|
||||
"""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 access to PSPEC files."""
|
||||
|
||||
# standard python modules
|
||||
from os.path import basename
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
from pisi.xmlext import *
|
||||
from pisi.xmlfile import XmlFile
|
||||
from pisi.dependency import DepInfo
|
||||
from pisi.util import Checks
|
||||
|
||||
#class Packager:
|
||||
# __metaclass__ = xmlfile.autoxml
|
||||
|
||||
class PackagerInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.name = getNodeText(getNode(node, "Name"))
|
||||
self.email = getNodeText(getNode(node, "Email"))
|
||||
else:
|
||||
self.name = None
|
||||
self.email = None
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Packager")
|
||||
xml.addTextNodeUnder(node, "Name", self.name)
|
||||
xml.addTextNodeUnder(node, "Email", self.email)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
err.has_tag(self.name, "Packager", "Name")
|
||||
err.has_tag(self.email, "Packager", "Email")
|
||||
return err.list
|
||||
|
||||
def __str__(self):
|
||||
s = "%s <%s>" % (self.name, self.email)
|
||||
return s
|
||||
|
||||
class AdditionalFileInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.filename = getNodeText(node)
|
||||
self.target = getNodeAttribute(node, "target")
|
||||
self.permission = getNodeAttribute(node, "permission")
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("AdditionalFile")
|
||||
xml.addText(node, self.filename)
|
||||
node.setAttribute("target", self.target)
|
||||
if self.permission:
|
||||
node.setAttribute("permission", self.permission)
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
if not self.filename:
|
||||
err.add("AdditionalFile should have file name string")
|
||||
if not self.target:
|
||||
err.add("AdditionalFile should have a target attribute")
|
||||
return err.list
|
||||
|
||||
def __str__(self):
|
||||
s = "->".join(self.filename, self.target)
|
||||
s += s + '(' + self.permission + ')'
|
||||
return s
|
||||
|
||||
class PatchInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.filename = getNodeText(node)
|
||||
self.compressionType = getNodeAttribute(node, "compressionType")
|
||||
self.level = getNodeAttribute(node, "level")
|
||||
self.target = getNodeAttribute(node, "target")
|
||||
else:
|
||||
self.compressionType = None
|
||||
if not self.level:
|
||||
self.level = 0
|
||||
else:
|
||||
self.level = int(self.level)
|
||||
if not self.target:
|
||||
self.target = ''
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Patch")
|
||||
xml.addText(node, self.filename)
|
||||
if self.compressionType:
|
||||
node.setAttribute("compressionType", self.compressionType)
|
||||
if self.level:
|
||||
node.setAttribute("level", str(self.level))
|
||||
if self.target:
|
||||
node.setAttribute("target", self.target)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
if not self.filename:
|
||||
return [ "Patch should have a filename string" ]
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
s = self.filename
|
||||
s += ' (' + self.compressionType + ')'
|
||||
s += ' level:' + self.level
|
||||
return s
|
||||
|
||||
|
||||
class UpdateInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.date = getNodeText(getNode(node, "Date"))
|
||||
self.version = getNodeText(getNode(node, "Version"))
|
||||
self.release = getNodeText(getNode(node, "Release"))
|
||||
self.type = getNodeText(getNode(node, "Type"))
|
||||
else:
|
||||
self.type = None
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Update")
|
||||
xml.addTextNodeUnder(node, "Date", self.date)
|
||||
xml.addTextNodeUnder(node, "Version", self.version)
|
||||
xml.addTextNodeUnder(node, "Release", self.release)
|
||||
if self.type:
|
||||
xml.addTextNodeUnder(node, "Type", self.type)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
err.has_tag(self.date, "Update", "Date")
|
||||
err.has_tag(self.version, "Update", "Version")
|
||||
err.has_tag(self.release, "Update", "Release")
|
||||
return err.list
|
||||
|
||||
def __str__(self):
|
||||
s = self.date
|
||||
s += ", ver=" + self.version
|
||||
s += ", rel=" + self.release
|
||||
s += ", type=" + self.type
|
||||
return s
|
||||
|
||||
class PathInfo:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.pathname = getNodeText(node)
|
||||
self.fileType = getNodeAttribute(node, "fileType")
|
||||
if not self.fileType:
|
||||
self.fileType = "other"
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Path")
|
||||
xml.addText(node, self.pathname)
|
||||
node.setAttribute("fileType", self.fileType)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
if not self.pathname:
|
||||
return [ "Path tag should have a name string" ]
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
s = self.pathname
|
||||
s += ", type=" + self.fileType
|
||||
return s
|
||||
|
||||
class ComarProvide:
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.om = getNodeText(node)
|
||||
self.script=getNodeAttribute(node, "script")
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("COMAR")
|
||||
xml.addText(node, self.om)
|
||||
node.setAttribute("script", self.script)
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
if not self.om or not self.script:
|
||||
return [ "COMAR provide should have something :)" ]
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
s = self.script
|
||||
s += ' (' + self.om + ')'
|
||||
return s
|
||||
|
||||
class SourceInfo:
|
||||
"""A structure to hold source information. Source information is
|
||||
located under <Source> tag in PSPEC file."""
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.name = getNodeText(node, "Name")
|
||||
self.homepage = getNodeText(node, "HomePage")
|
||||
self.packager = PackagerInfo(getNode(node, "Packager"))
|
||||
self.summary = getNodeText(node, "Summary")
|
||||
self.description = getNodeText(node, "Description")
|
||||
self.license = map(getNodeText, getAllNodes(node, "License"))
|
||||
self.icon = getNodeText(node, "Icon")
|
||||
self.isa = map(getNodeText, getAllNodes(node, "IsA"))
|
||||
self.partof = getNodeText(node, "PartOf")
|
||||
archiveNode = getNode(node, "Archive")
|
||||
self.archiveUri = getNodeText(archiveNode).strip()
|
||||
self.archiveName = basename(self.archiveUri)
|
||||
self.archiveType = getNodeAttribute(archiveNode, "type")
|
||||
self.archiveSHA1 = getNodeAttribute(archiveNode,
|
||||
"sha1sum")
|
||||
patchElts = getAllNodes(node, "Patches/Patch")
|
||||
self.patches = [PatchInfo(p) for p in patchElts]
|
||||
buildDepElts = getAllNodes(node,
|
||||
"BuildDependencies/Dependency")
|
||||
self.buildDeps = [DepInfo(d) for d in buildDepElts]
|
||||
historyElts = getAllNodes(node, "History/Update")
|
||||
self.history = [UpdateInfo(x) for x in historyElts]
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Source")
|
||||
xml.addTextNodeUnder(node, "Name", self.name)
|
||||
if self.homepage:
|
||||
xml.addTextNodeUnder(node, "Homepage", self.homepage)
|
||||
node.appendChild(self.packager.elt(xml))
|
||||
xml.addTextNodeUnder(node, "Summary", self.summary)
|
||||
xml.addTextNodeUnder(node, "Description", self.description)
|
||||
if self.icon:
|
||||
xml.addTextNodeUnder(node, "Icon", self.icon)
|
||||
for lic in self.license:
|
||||
xml.addTextNodeUnder(node, "License", lic)
|
||||
for isa in self.isa:
|
||||
xml.addTextNodeUnder(node, "IsA", isa)
|
||||
xml.addTextNodeUnder(node, "PartOf", self.partof)
|
||||
archiveNode = xml.addNodeUnder(node, "Archive")
|
||||
archiveNode.setAttribute("type", self.archiveType)
|
||||
archiveNode.setAttribute("sha1sum", self.archiveSHA1)
|
||||
for patch in self.patches:
|
||||
xml.addNodeUnder(node, "Patches", patch.elt(xml))
|
||||
for dep in self.buildDeps:
|
||||
xml.addNodeUnder(node, "BuildDependencies", dep.elt(xml))
|
||||
for update in self.history:
|
||||
xml.addNodeUnder(node, "History", update.elt(xml))
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
err.has_tag(self.name, "Source", "Name")
|
||||
err.has_tag(self.description, "Source", "Description")
|
||||
err.has_tag(self.summary, "Source", "Summary")
|
||||
err.has_tag(self.packager, "Source", "Packager")
|
||||
err.has_tag(self.license, "Source", "License")
|
||||
if (not self.archiveUri) or (not self.archiveType):
|
||||
err.add("Source archive URI and type should be given")
|
||||
if not self.archiveSHA1:
|
||||
errd.add("Source archive should have a SHA1 sum")
|
||||
if len(self.history) <= 0:
|
||||
err.add("Source needs some education about History :)")
|
||||
|
||||
err.join(self.packager.has_errors())
|
||||
for update in self.history:
|
||||
err.join(update.has_errors())
|
||||
for patch in self.patches:
|
||||
err.join(patch.has_errors())
|
||||
for dep in self.buildDeps:
|
||||
err.join(dep.has_errors())
|
||||
|
||||
return err.list
|
||||
|
||||
|
||||
class PackageInfo:
|
||||
"""A structure to hold package information. Package information is
|
||||
located under <Package> tag in PSPEC file. Opposite to Source each
|
||||
PSPEC file can have more than one Package tag."""
|
||||
def __init__(self, node = None):
|
||||
if node:
|
||||
self.name = getNodeText(node, "Name")
|
||||
self.summary = getNodeText(node, "Summary")
|
||||
self.description = getNodeText(node, "Description")
|
||||
self.isa = map(getNodeText, getAllNodes(node, "IsA"))
|
||||
self.partof = getNodeText(node, "PartOf")
|
||||
self.license = map(getNodeText, getAllNodes(node, "License"))
|
||||
self.icon = getNodeText(node, "Icon")
|
||||
rtDepElts = getAllNodes(node, "RuntimeDependencies/Dependency")
|
||||
self.runtimeDeps = [DepInfo(x) for x in rtDepElts]
|
||||
self.paths = [PathInfo(x) for x in getAllNodes(node, "Files/Path")]
|
||||
historyElts = getAllNodes(node, "History/Update")
|
||||
self.history = [UpdateInfo(x) for x in historyElts]
|
||||
conflElts = getAllNodes(node, "Conflicts/Package")
|
||||
self.conflicts = map(getNodeText, conflElts)
|
||||
provComarElts = getAllNodes(node, "Provides/COMAR")
|
||||
self.providesComar = [ComarProvide(x) for x in provComarElts]
|
||||
reqComarElts = getAllNodes(node, "Requires/COMAR")
|
||||
self.requiresComar = map(getNodeText, reqComarElts)
|
||||
aFilesElts = getAllNodes(node, "AdditionalFiles/AdditionalFile")
|
||||
self.additionalFiles = [AdditionalFileInfo(f) for f in aFilesElts]
|
||||
|
||||
def elt(self, xml):
|
||||
node = xml.newNode("Package")
|
||||
xml.addTextNodeUnder(node, "Name", self.name)
|
||||
xml.addTextNodeUnder(node, "Summary", self.summary)
|
||||
xml.addTextNodeUnder(node, "Description", self.description)
|
||||
if self.icon:
|
||||
xml.addTextNodeUnder(node, "Icon", self.icon)
|
||||
for lic in self.license:
|
||||
xml.addTextNodeUnder(node, "License", lic)
|
||||
for isa in self.isa:
|
||||
xml.addTextNodeUnder(node, "IsA", isa)
|
||||
if self.partof:
|
||||
xml.addTextNodeUnder(node, "PartOf", self.partof)
|
||||
for dep in self.runtimeDeps:
|
||||
xml.addNodeUnder(node, "RuntimeDependencies", dep.elt(xml))
|
||||
for path in self.paths:
|
||||
xml.addNodeUnder(node, "Files", path.elt(xml))
|
||||
for update in self.history:
|
||||
xml.addNodeUnder(node, "History", update.elt(xml))
|
||||
for conflict in self.conflicts:
|
||||
xml.addTextNodeUnder(node, "Conflicts/Package", conflict)
|
||||
for pcomar in self.providesComar:
|
||||
xml.addNodeUnder(node, "Provides", pcomar.elt(xml))
|
||||
for rcomar in self.requiresComar:
|
||||
xml.addTextNodeUnder(node, "Requires/COMAR", rcomar)
|
||||
for afile in self.additionalFiles:
|
||||
xml.addNodeUnder(node, "AdditionalFiles", afile.elt(xml))
|
||||
return node
|
||||
|
||||
def has_errors(self):
|
||||
err = Checks()
|
||||
err.has_tag(self.name, "Package", "Name")
|
||||
err.has_tag(self.summary, "Package", "Summary")
|
||||
err.has_tag(self.description, "Package", "Description")
|
||||
err.has_tag(self.license, "Package", "License")
|
||||
if len(self.paths) <= 0:
|
||||
err.add("Package should have some files")
|
||||
|
||||
for path in self.paths:
|
||||
err.join(path.has_errors())
|
||||
for dep in self.runtimeDeps:
|
||||
err.join(dep.has_errors())
|
||||
for afile in self.additionalFiles:
|
||||
err.join(afile.has_errors())
|
||||
|
||||
return err.list
|
||||
|
||||
def __str__(self):
|
||||
s = 'Name: ' + self.name
|
||||
s += '\nSummary: ' + self.summary
|
||||
s += '\nDescription: ' + self.description
|
||||
return s
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.name + '-' \
|
||||
+ self.version + '-' \
|
||||
+ self.release
|
||||
|
||||
return join( config.lib_dir(), packageDir)
|
||||
|
||||
class SpecFile(XmlFile):
|
||||
"""A class for reading/writing from/to a PSPEC (PISI SPEC) file."""
|
||||
|
||||
def __init__(self):
|
||||
XmlFile.__init__(self,"PISI")
|
||||
|
||||
def read(self, filename):
|
||||
"""Read PSPEC file"""
|
||||
|
||||
self.readxml(filename)
|
||||
|
||||
self.source = SourceInfo(self.getNode("Source"))
|
||||
|
||||
# As we have no Source/Version tag we need to get
|
||||
# the last version and release information
|
||||
# from the first child of History/Update. And it works :)
|
||||
self.source.version = self.source.history[0].version
|
||||
self.source.release = self.source.history[0].release
|
||||
|
||||
# find all binary packages
|
||||
packageElts = self.getAllNodes("Package")
|
||||
self.packages = [PackageInfo(p) for p in packageElts]
|
||||
|
||||
self.merge_tags()
|
||||
self.override_tags()
|
||||
|
||||
self.unlink()
|
||||
|
||||
errs = self.has_errors()
|
||||
if errs:
|
||||
e = ""
|
||||
for x in errs:
|
||||
e += x + "\n"
|
||||
raise XmlError("File '%s' has errors:\n%s" % (filename, e))
|
||||
|
||||
def override_tags(self):
|
||||
"""Override tags from Source in Packages. Some tags in Packages
|
||||
overrides the tags from Source. There is a more detailed
|
||||
description in documents."""
|
||||
|
||||
tmp = []
|
||||
for pkg in self.packages:
|
||||
|
||||
if not pkg.summary:
|
||||
pkg.summary = self.source.summary
|
||||
|
||||
if not pkg.description:
|
||||
pkg.description = self.source.description
|
||||
|
||||
if not pkg.partof:
|
||||
pkg.partof = self.source.partof
|
||||
|
||||
if not pkg.license:
|
||||
pkg.license = self.source.license
|
||||
|
||||
if not pkg.icon:
|
||||
pkg.icon = self.source.icon
|
||||
|
||||
tmp.append(pkg)
|
||||
|
||||
self.packages = tmp
|
||||
|
||||
def merge_tags(self):
|
||||
"""Merge tags from Source in Packages. Some tags in Packages merged
|
||||
with the tags from Source. There is a more detailed
|
||||
description in documents."""
|
||||
|
||||
tmp = []
|
||||
for pkg in self.packages:
|
||||
|
||||
if pkg.isa and self.source.isa:
|
||||
pkg.isa.append(self.source.isa)
|
||||
elif not pkg.isa and self.source.isa:
|
||||
pkg.isa = self.source.isa
|
||||
|
||||
tmp.append(pkg)
|
||||
|
||||
self.packages = tmp
|
||||
|
||||
def has_errors(self):
|
||||
"""Return errors of the PSPEC file if there are any."""
|
||||
#FIXME: has_errors name is misleading for a function that does
|
||||
#not just return a boolean value. check() would be better - exa
|
||||
err = Checks()
|
||||
err.join(self.source.has_errors())
|
||||
if len(self.packages) <= 0:
|
||||
errs.add("There should be at least one Package section")
|
||||
for p in self.packages:
|
||||
err.join(p.has_errors())
|
||||
return err.list
|
||||
|
||||
def write(self, filename):
|
||||
"""Write PSPEC file"""
|
||||
self.newDOM()
|
||||
self.addChild(self.source.elt(self))
|
||||
for pkg in self.packages:
|
||||
self.addChild(pkg.elt(self))
|
||||
self.writexml(filename)
|
||||
@@ -1,149 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
"""
|
||||
Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
|
||||
files are specification files for PISI source packages. This module
|
||||
provides read and write routines for PSPEC files.
|
||||
"""
|
||||
|
||||
# standard python modules
|
||||
from os.path import basename
|
||||
|
||||
# pisi modules
|
||||
from pisi.xmlext import *
|
||||
import pisi.xmlfile as xmlfile
|
||||
from pisi.xmlfile import XmlFile
|
||||
from pisi.ui import ui
|
||||
from pisi.dependency import DepInfo
|
||||
from pisi.util import Checks
|
||||
|
||||
__metaclass__ = xmlfile.autoxml
|
||||
|
||||
|
||||
class Packager:
|
||||
t_Name = [types.StringType, xmlfile.mandatory]
|
||||
t_Email = [types.StringType, xmlfile.mandatory]
|
||||
def __str__(self):
|
||||
s = "%s <%s>" % (self.name, self.email)
|
||||
return s
|
||||
|
||||
|
||||
class AdditionalFileInfo:
|
||||
s_Filename = xmlfile.mandatory
|
||||
a_Target = [types.StringType, xmlfile.mandatory]
|
||||
a_Permission = [types.StringType, xmlfile.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s -> %s " % (self.filename, self.target)
|
||||
if self.permission:
|
||||
s += '(%s)' % self.permission
|
||||
return s
|
||||
|
||||
|
||||
class Patch:
|
||||
s_Filename = xmlfile.mandatory
|
||||
a_compressionType = [types.StringType, xmlfile.optional]
|
||||
a_level = [types.StringType, xmlfile.optional]
|
||||
a_target = [types.StringType, xmlfile.optional]
|
||||
|
||||
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:
|
||||
|
||||
t_Date = [types.StringType, xmlfile.mandatory]
|
||||
t_Version = [types.StringType, xmlfile.mandatory]
|
||||
t_Release = [types.StringType, xmlfile.mandatory]
|
||||
t_Type = [types.StringType, xmlfile.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 = xmlfile.mandatory
|
||||
a_fileType = [types.StringType, xmlfile.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.pathname
|
||||
s += ", type=" + self.fileType
|
||||
return s
|
||||
|
||||
|
||||
class ComarProvide:
|
||||
|
||||
s_om = [types.StringType, xmlfile.mandatory]
|
||||
a_script = [types.StringType, xmlfile.mandatory]
|
||||
|
||||
def __str__(self):
|
||||
# FIXME: descriptive enough?
|
||||
s = self.script
|
||||
s += ' (' + self.om + ')'
|
||||
return s
|
||||
|
||||
|
||||
class Archive:
|
||||
|
||||
s_uri = [ types.StringType, xmlfile.mandatory ]
|
||||
a_type =[ types.StringType, xmlfile.mandatory ]
|
||||
a_sha1sum =[ types.StringType, xmlfile.mandatory ]
|
||||
|
||||
def decode_post(self):
|
||||
self.name = basename(self.uri)
|
||||
|
||||
|
||||
class Source:
|
||||
|
||||
t_Name = [types.StringType, xmlfile.mandatory]
|
||||
t_HomePage = [types.StringType, xmlfile.mandatory]
|
||||
t_Packager = [Packager, xmlfile.mandatory]
|
||||
t_Summary = [types.StringType, xmlfile.mandatory]
|
||||
t_Description = [types.StringType, xmlfile.mandatory]
|
||||
t_License = [ [types.StringType], xmlfile.mandatory]
|
||||
t_IsA = [ [types.StringType], xmlfile.mandatory]
|
||||
t_PartOf = [types.StringType, xmlfile.mandatory]
|
||||
t_Archive = [Archive, xmlfile.mandatory ]
|
||||
t_Patch = [ [Patch], xmlfile.mandatory, "Patches/Patch"]
|
||||
t_BuildDep = [ [Dependency], xmlfile.mandatory, "BuildDependencies/Dependency"]
|
||||
t_History = [ [Update], xmlfile.mandatory, "History/Update"]
|
||||
|
||||
|
||||
class Package:
|
||||
|
||||
t_Name = [ types.StringType, xmlfile.mandatory ]
|
||||
t_Summary = [ types.StringType, xmlfile.mandatory ]
|
||||
t_Description = [ types.StringType, xmlfile.mandatory ]
|
||||
t_IsA = [ [types.StringType], xmlfile.mandatory]
|
||||
t_PartOf = [types.StringType, xmlfile.mandatory]
|
||||
t_History = [ [Update], xmlfile.mandatory, "History/Update"]
|
||||
t_Conflicts = [ [types.StringType], xmlfile.mandatory, "Conflicts/Package"]
|
||||
t_ProvidesComar = [ [ComarProvide], xmlfile.mandatory, "Provides/COMAR"]
|
||||
t_RequriesComar = [ [types.StringType], xmlfile.mandatory, "Requires/COMAR"]
|
||||
t_AdditionalFiles = [ [AdditionalFile], xmlfile.mandatory, "AdditionalFiles/AdditionalFile"]
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# generic user interface
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Murat Eren <meren@uludag.org.tr>
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class UI(object):
|
||||
"Abstract class for UI operations, derive from this."
|
||||
|
||||
class Progress:
|
||||
def __init__(self, totalsize):
|
||||
self.totalsize = totalsize
|
||||
self.percent = 0
|
||||
|
||||
def update(self, size):
|
||||
if not self.totalsize:
|
||||
return 100
|
||||
|
||||
percent = (size * 100) / self.totalsize
|
||||
if percent and self.percent is not percent:
|
||||
self.percent = percent
|
||||
return percent
|
||||
else:
|
||||
return 0
|
||||
|
||||
def __init__(self, debuggy = False, verbose = False):
|
||||
self.show_debug = debuggy
|
||||
self.show_verbose = verbose
|
||||
|
||||
def set_verbose(self, flag):
|
||||
self.show_verbose = flag
|
||||
|
||||
def set_debug(self, flag):
|
||||
self.show_debug = flag
|
||||
|
||||
def info(self, msg, verbose = False):
|
||||
"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
|
||||
|
||||
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
|
||||
@@ -1,102 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Simplifies working with URLs, purl module provides common URL
|
||||
# parsing and processing
|
||||
|
||||
from urlparse import urlparse
|
||||
from os.path import basename
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
class URI(object):
|
||||
"""URI class provides a URL parser and simplifies working with
|
||||
URLs."""
|
||||
|
||||
def __init__(self, uri=None):
|
||||
if uri:
|
||||
self.set_uri(uri)
|
||||
else:
|
||||
self.__scheme = None
|
||||
self.__location = None
|
||||
self.__path = None
|
||||
self.__filename = None
|
||||
self.__params = None
|
||||
self.__query = None
|
||||
self.__fragment = None
|
||||
self.__uri = None
|
||||
|
||||
self.__authinfo = None
|
||||
|
||||
def get_uri(self):
|
||||
if self.__uri:
|
||||
return self.__uri
|
||||
return None
|
||||
|
||||
def set_uri(self, uri):
|
||||
# (scheme, location, path, params, query, fragment)
|
||||
u = urlparse(uri, "file")
|
||||
self.__scheme = u[0]
|
||||
self.__location = u[1]
|
||||
self.__path = u[2]
|
||||
self.__filename = basename(self.__path)
|
||||
self.__params = u[3]
|
||||
self.__query = u[4]
|
||||
self.__fragment = u[5]
|
||||
|
||||
self.__uri = uri
|
||||
|
||||
def is_local_file(self):
|
||||
if self.scheme() == "file":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_remote_file(self):
|
||||
return not self.is_local_file()
|
||||
|
||||
def is_absolute_path(self):
|
||||
return util.absolute_path(self.__path)
|
||||
|
||||
def is_relative_path(self):
|
||||
return not self.is_absolute_path()
|
||||
|
||||
def set_auth_info(self, authTuple):
|
||||
if not isinstance(authTuple, tuple):
|
||||
raise Exception, "setAuthInfo needs a tuple (user, pass)"
|
||||
self.__authinfo = authTuple
|
||||
|
||||
def auth_info(self):
|
||||
return self.__authinfo
|
||||
|
||||
def scheme(self):
|
||||
return self.__scheme
|
||||
|
||||
def location(self):
|
||||
return self.__location
|
||||
|
||||
def path(self):
|
||||
return self.__path
|
||||
|
||||
def filename(self):
|
||||
return self.__filename
|
||||
|
||||
def params(self):
|
||||
return self.__params
|
||||
|
||||
def query(self):
|
||||
return self.__query
|
||||
|
||||
def fragment(self):
|
||||
return self.__fragment
|
||||
|
||||
def __str__(self):
|
||||
return self.get_uri()
|
||||
|
||||
uri = property(get_uri, set_uri)
|
||||
@@ -1,374 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# misc. utility functions, including process and file utils
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
# S. Caglar Onur <caglar@uludag.org.tr>
|
||||
# A. Murat Eren <meren@uludag.org.tr>
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
import sys
|
||||
import sha
|
||||
import shutil
|
||||
import statvfs
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class FileError(pisi.Error):
|
||||
pass
|
||||
|
||||
class UtilError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
#########################
|
||||
# spec validation utility #
|
||||
#########################
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
self.list = None
|
||||
|
||||
def add(self, err):
|
||||
if not self.list:
|
||||
self.list = []
|
||||
self.list.append(err)
|
||||
|
||||
def join(self, list):
|
||||
if list != None:
|
||||
if not self.list:
|
||||
self.list = []
|
||||
self.list.extend(list)
|
||||
|
||||
def has_tag(self, var, section, name):
|
||||
if not var:
|
||||
if not self.list:
|
||||
self.list = []
|
||||
self.list.append("%s section should have a '%s' tag" % (section, name))
|
||||
|
||||
|
||||
#########################
|
||||
# string/list functions #
|
||||
#########################
|
||||
|
||||
def unzip(seq):
|
||||
return zip(*seq)
|
||||
|
||||
def concat(l):
|
||||
'''concatenate a list of lists'''
|
||||
return reduce( lambda x,y: x+y, l )
|
||||
|
||||
def strlist(l):
|
||||
"""concatenate string reps of l's elements"""
|
||||
return "".join(map(lambda x: str(x) + ' ', l))
|
||||
|
||||
def multisplit(str, chars):
|
||||
""" split str with any of chars"""
|
||||
l = [str]
|
||||
for c in chars:
|
||||
l = concat(map(lambda x:x.split(c), l))
|
||||
return l
|
||||
|
||||
def same(l):
|
||||
'''check if all elements of a sequence are equal'''
|
||||
if len(l)==0:
|
||||
return True
|
||||
else:
|
||||
last = l.pop()
|
||||
for x in l:
|
||||
if x!=last:
|
||||
return False
|
||||
return True
|
||||
|
||||
def prefix(a, b):
|
||||
'''check if sequence a is a prefix of sequence b'''
|
||||
if len(a)>len(b):
|
||||
return False
|
||||
for i in range(0,len(a)):
|
||||
if a[i]!=b[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove_prefix(a,b):
|
||||
"remove prefix a from sequence b"
|
||||
assert prefix(a,b)
|
||||
return b[len(a):]
|
||||
|
||||
|
||||
##############################
|
||||
# Process Releated Functions #
|
||||
##############################
|
||||
|
||||
def run_batch(cmd):
|
||||
"""run command non-interactively and report return value and output"""
|
||||
ui.info('running ' + cmd)
|
||||
a = os.popen(cmd)
|
||||
lines = a.readlines()
|
||||
ret = a.close()
|
||||
ui.debug('return value ' + ret)
|
||||
successful = ret == None
|
||||
if not successful:
|
||||
ui.error('ERROR: executing command: ' + cmd + '\n' + strlist(lines))
|
||||
return (successful,lines)
|
||||
|
||||
|
||||
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
|
||||
|
||||
# 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 os.path.join(*tuple(comps))
|
||||
else:
|
||||
return ""
|
||||
|
||||
def absolute_path(path):
|
||||
"determine if given @path is absolute"
|
||||
comps = splitpath(path)
|
||||
return comps[0] == ''
|
||||
|
||||
####################################
|
||||
# File/Directory Related Functions #
|
||||
####################################
|
||||
|
||||
def check_file(file, mode = os.F_OK):
|
||||
"shorthand to check if a file exists"
|
||||
if not os.access(file, mode):
|
||||
raise FileError("File " + file + " not found")
|
||||
return True
|
||||
|
||||
def check_dir(dir):
|
||||
"""check if directory exists, and create if it doesn't.
|
||||
works recursively"""
|
||||
dir = dir.strip().rstrip("/")
|
||||
if not os.access(dir, os.F_OK):
|
||||
os.makedirs(dir)
|
||||
|
||||
def clean_dir(path):
|
||||
"Remove all content of a directory (top)"
|
||||
# don't reimplement the wheel
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
def dir_size(dir):
|
||||
""" calculate the size of files under a dir
|
||||
based on the os module example"""
|
||||
# It's really hard to give an approximate value for package's
|
||||
# installed size. Gettin a sum of all files' sizes if far from
|
||||
# being true. Using 'du' command (like Debian does) can be a
|
||||
# better solution :(.
|
||||
getsize = os.path.getsize
|
||||
join = os.path.join
|
||||
islink = os.path.islink
|
||||
def sizes():
|
||||
for root, dirs, files in os.walk(dir):
|
||||
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
|
||||
return sum( sizes() )
|
||||
|
||||
def copy_file(src,dest):
|
||||
"""copy source file to destination file"""
|
||||
check_file(src)
|
||||
check_dir(os.path.dirname(dest))
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
def get_file_hashes(top, exclude_prefix=None, removePrefix=None):
|
||||
"""Generator function iterates over a toplevel path and returns the
|
||||
(filePath, sha1Hash) tuple 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."""
|
||||
|
||||
# also handle single files
|
||||
if os.path.isfile(top):
|
||||
yield (top, sha1_file(top))
|
||||
return
|
||||
|
||||
def has_excluded_prefix(filename):
|
||||
if exclude_prefix and removePrefix:
|
||||
tempfnam = remove_prefix(removePrefix, filename)
|
||||
for p in exclude_prefix:
|
||||
if tempfnam.startswith(p):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
for root, dirs, files in os.walk(top, topdown=False):
|
||||
#bug 339
|
||||
if os.path.islink(root) and not has_excluded_prefix(root):
|
||||
#yield the symlink..
|
||||
#bug 373
|
||||
yield (root, sha1_data(os.readlink(root)))
|
||||
exclude_prefix.append(remove_prefix(removePrefix, root) + "/")
|
||||
continue
|
||||
|
||||
#bug 397
|
||||
for dir in dirs:
|
||||
d = os.path.join(root, dir)
|
||||
if os.path.islink(d) and not has_excluded_prefix(d):
|
||||
yield (d, sha1_data(os.readlink(d)))
|
||||
exclude_prefix.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_file(parent))
|
||||
|
||||
for fname in files:
|
||||
f = os.path.join(root, fname)
|
||||
if has_excluded_prefix(f):
|
||||
continue
|
||||
#bug 373
|
||||
elif os.path.islink(f):
|
||||
yield (f, sha1_data(os.readlink(f)))
|
||||
else:
|
||||
yield (f, sha1_file(f))
|
||||
|
||||
def copy_dir(src, dest):
|
||||
"""copy source dir to destination dir recursively"""
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def check_file_hash(filename, hash):
|
||||
"""Check the files integrity with a given hash"""
|
||||
if sha1_file(filename) == hash:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def sha1_file(filename):
|
||||
"""calculate sha1 hash of filename"""
|
||||
# Broken links can cause problem!
|
||||
try:
|
||||
m = sha.new()
|
||||
f = file(filename, 'rb')
|
||||
for line in f:
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
except IOError:
|
||||
return "0"
|
||||
|
||||
def sha1_data(data):
|
||||
"""calculate sha1 hash of given data"""
|
||||
try:
|
||||
m = sha.new()
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
except:
|
||||
return "0"
|
||||
|
||||
def uncompress(patchFile, compressType="gz", targetDir=None):
|
||||
"""uncompresses a file and returns the path of the uncompressed
|
||||
file"""
|
||||
if targetDir:
|
||||
filePath = os.path.join(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, target = ''):
|
||||
"""simple function to apply patches.."""
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sourceDir)
|
||||
|
||||
check_file(patchFile)
|
||||
level = int(level)
|
||||
cmd = "patch -p%d %s< %s" % (level, target, patchFile)
|
||||
p = os.popen(cmd)
|
||||
o = p.readlines()
|
||||
retval = p.close()
|
||||
if retval:
|
||||
raise UtilError("ERROR: patch (%s) failed: %s" % (patchFile,
|
||||
strlist (o)))
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
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 = os.path.join(root, fn)
|
||||
ctx.ui.info('Removing lock %s', path)
|
||||
os.unlink(path)
|
||||
|
||||
########################################
|
||||
# Package/Repository Related Functions #
|
||||
########################################
|
||||
|
||||
def package_name(name, version, release):
|
||||
return name + '-' + version + '-' + release + ctx.const.package_prefix
|
||||
@@ -1,161 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# version structure
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import re
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
|
||||
# Basic rule is:
|
||||
# p > (no suffix) > rc > pre > beta > alpha
|
||||
keywords = {"alpha": 0,
|
||||
"beta" : 1,
|
||||
"pre" : 2,
|
||||
"rc" : 3,
|
||||
"NOKEY" : 4,
|
||||
"p" : 5}
|
||||
|
||||
# helper functions
|
||||
def has_keyword(versionitem):
|
||||
if versionitem._keyword != "NOKEY":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class VersionItem:
|
||||
_keyword = "NOKEY"
|
||||
_value = 0
|
||||
|
||||
def __init__(self, itemstring):
|
||||
|
||||
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":
|
||||
self._value = itemstring
|
||||
else:
|
||||
# rest is the version item's value. And each must have
|
||||
# one!
|
||||
self._value = itemstring[len(self._keyword):]
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
class Version:
|
||||
|
||||
def __init__(self, verstring):
|
||||
self.comps = []
|
||||
for i in util.multisplit(verstring,'.-_'):
|
||||
# some version strings can contain ascii chars at the
|
||||
# back. As an example: 2.11a
|
||||
# We split '11a' as two items like '11' and 'a'
|
||||
s = re.compile("[a-z-A-Z]$").search(i)
|
||||
if s:
|
||||
head = i[:s.start()]
|
||||
tail = s.group()
|
||||
self.comps.append(VersionItem(head))
|
||||
self.comps.append(VersionItem(tail))
|
||||
else:
|
||||
self.comps.append(VersionItem(i))
|
||||
|
||||
self.verstring = verstring
|
||||
|
||||
def string(self):
|
||||
return self.verstring
|
||||
|
||||
def pred(self, rhs, pred):
|
||||
|
||||
loop = len(self.comps)
|
||||
if len(rhs.comps) > loop:
|
||||
loop = len(rhs.comps)
|
||||
|
||||
for i in range(0, loop):
|
||||
try:
|
||||
litem = self.comps[i]
|
||||
except IndexError:
|
||||
litem = VersionItem("")
|
||||
|
||||
try:
|
||||
ritem = rhs.comps[i]
|
||||
except IndexError:
|
||||
ritem = VersionItem("")
|
||||
|
||||
if pred(litem, ritem):
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def __lt__(self,rhs):
|
||||
return self.pred(rhs, lambda x,y: x<y)
|
||||
|
||||
def __le__(self,rhs):
|
||||
return self.pred(rhs, lambda x,y: x<=y)
|
||||
|
||||
def __gt__(self,rhs):
|
||||
return self.pred(rhs, lambda x,y: x>y)
|
||||
|
||||
def __ge__(self,rhs):
|
||||
return self.pred(rhs, lambda x,y: x>=y)
|
||||
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
|
||||
"""
|
||||
some helper functions for using minidom
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an old library
|
||||
"""
|
||||
|
||||
import pisi
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if not node.hasAttribute(attrname):
|
||||
return None
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE if x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError("getNodeText: Expected text node, got something else!")
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
# get DOM for top node
|
||||
nodeList = getTagByName(node, tags[0])
|
||||
if len(nodeList) == 0:
|
||||
return None # not found
|
||||
|
||||
node = nodeList[0] # discard other matches
|
||||
for tag in tags[1:]:
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0:
|
||||
return None
|
||||
else:
|
||||
node = nodeList[0]
|
||||
|
||||
return node
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = getTagByName(node, tags[0])
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
for tag in tags[1:]:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(dom, 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.appendChild(dom.createElement(tag))
|
||||
return node
|
||||
|
||||
def addTagPath(dom, node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(dom, node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(dom, node, tagpath, newnode = None):
|
||||
"""add a new node at the end of the tree"""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(dom, node, [], newnode)
|
||||
return node
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
# get DOM for top node
|
||||
nodeList = getTagByName(node, tags[0])
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return addTagPath(dom, node, tags, newnode)
|
||||
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
tags.pop(0)
|
||||
while len(tags)>0:
|
||||
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(dom, node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1]
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(dom, node, tags, newnode)
|
||||
|
||||
return node
|
||||
@@ -1,595 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
|
||||
"""
|
||||
xmlfile is a helper module for accessing XML files using
|
||||
xml.dom.minidom.
|
||||
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
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 xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import codecs
|
||||
import types
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
from pisi.xmlext import *
|
||||
import pisi.context as ctx
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
mandatory, optional = range(2) # poor man's enum
|
||||
|
||||
# basic types
|
||||
|
||||
Text = types.StringType
|
||||
Integer = types.IntType
|
||||
|
||||
class LocalText(object):
|
||||
"""Handles XML tags/attributes with localized text"""
|
||||
|
||||
def __init__():
|
||||
locs = {}
|
||||
|
||||
def decode(nodes, req):
|
||||
# flags, tag name, instance attribute
|
||||
if not nodes:
|
||||
if req == mandatory:
|
||||
pass
|
||||
#errs.append("Tag '%s' should have at least one '%s' tag\n" % (parent.tagName, d[2]))
|
||||
else:
|
||||
for node in nodes:
|
||||
lang = getAttribute(node, "xml:lang")
|
||||
c = getText(node)
|
||||
if not c:
|
||||
#errs.append("Tag '%s' should have some text data\n" % node.tagName)
|
||||
break
|
||||
# FIXME: check for dups and 'en'
|
||||
if not lang:
|
||||
lang = 'en'
|
||||
self.locs[lang] = c
|
||||
# FIXME: return full list too
|
||||
L = language
|
||||
if not locs.has_key(L):
|
||||
L = 'en'
|
||||
if not locs.has_key(L):
|
||||
#errs.append("Tag '%s' should have an English version\n" % d[2])
|
||||
return ""
|
||||
return locs[L]
|
||||
|
||||
|
||||
class autoxml(type):
|
||||
"""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, 'Employee']
|
||||
|
||||
Logically, inside the Company/Employees tag, we will have several
|
||||
Employes tags, which are inserted to the Employees instance variable of
|
||||
Company in order of appearance.
|
||||
|
||||
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
|
||||
|
||||
# add XmlFile as one of the superclasses, we're smart
|
||||
bases = list(bases)
|
||||
if not XmlFile in bases:
|
||||
bases.append(XmlFile)
|
||||
|
||||
# standard initialization
|
||||
super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
#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
|
||||
inits = []
|
||||
decoders = []
|
||||
encoders = []
|
||||
formatters = []
|
||||
for var in dict:
|
||||
if var.startswith('t_') or var.startswith('a_'):
|
||||
name = var[2:]
|
||||
print 'generating member', name
|
||||
if var.startswith('a_'):
|
||||
x = autoxml.gen_attr_member(cls, name)
|
||||
elif var.startswith('t_'):
|
||||
x = autoxml.gen_tag_member(cls, name)
|
||||
(init, decoder, encoder, formatter) = x
|
||||
inits.append(init)
|
||||
decoders.append(decoder)
|
||||
encoders.append(encoder)
|
||||
formatters.append(formatter)
|
||||
|
||||
# generate top-level helper functions
|
||||
cls.initializers = inits
|
||||
def initialize(self):
|
||||
# XmlFile.__init__(self, cls.tag)
|
||||
for init in self.__class__.initializers:
|
||||
init(self)
|
||||
cls.__init__ = initialize
|
||||
|
||||
cls.decoders = decoders
|
||||
def decode(self, node):
|
||||
for decode_member in self.__class__.decoders:
|
||||
decode_member(self, node)
|
||||
cls.decode = decode
|
||||
|
||||
cls.encoders = encoders
|
||||
def encode(self, xml):
|
||||
node = xml.newNode(cls.tag)
|
||||
for encode_member in self.__class__.encoders:
|
||||
encode_member(self, xml, node)
|
||||
xml.dom.documentElement = node
|
||||
return node
|
||||
cls.encode = encode
|
||||
|
||||
cls.formatters = formatters
|
||||
def format(self):
|
||||
string = ''
|
||||
for formatter in self.__class__.formatters:
|
||||
string += formatter(self)
|
||||
return string
|
||||
cls.format = format
|
||||
if not dict.has_key('__str__'):
|
||||
cls.__str__ = format
|
||||
|
||||
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 createnode(xml, attr_name):
|
||||
return xml.newAttribute(attr_name) # create an attribute node
|
||||
def writetext(xml, attr, nil, text):
|
||||
attr.value = text
|
||||
anonfuns = cls.gen_anon_basic(attr, spec, readtext, createnode, writetext)
|
||||
def mergetext(node, attr):
|
||||
node.setAttributeNode(attr)
|
||||
return cls.gen_named_comp(attr, spec, anonfuns, mergetext)
|
||||
|
||||
def gen_tag_member(cls, tag):
|
||||
"""generate helper funs for tag member of class"""
|
||||
spec = getattr(cls, 't_' + tag)
|
||||
anonfuns = cls.gen_tag(tag, spec)
|
||||
def mergetext(node, newnode):
|
||||
print 'mergenode', node, newnode
|
||||
node.appendChild(newnode)
|
||||
return cls.gen_named_comp(tag, spec, anonfuns, mergetext)
|
||||
|
||||
def gen_tag(cls, tag, spec):
|
||||
"""generate readers and writers for the tag"""
|
||||
tag_type = spec[0]
|
||||
if type(tag_type) is types.TypeType:
|
||||
def readtext(node, tagpath):
|
||||
return getNodeText(node, tagpath)
|
||||
def createnode(xml, tag):
|
||||
return xml.newNode(tag)
|
||||
def writetext(xml, node, tagpath, text):
|
||||
xml.addTextNodeUnder(node, tagpath, text)
|
||||
return cls.gen_anon_basic(tag, spec, readtext,
|
||||
createnode, writetext)
|
||||
elif type(tag_type) is types.ListType:
|
||||
return cls.gen_list_tag(tag, spec)
|
||||
elif type(tag_type) is autoxml or type(tag_type) is types.ClassType:
|
||||
return cls.gen_class_tag(tag, spec)
|
||||
|
||||
def gen_named_comp(cls, token, spec, anonfuns, mergetext):
|
||||
"""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, format_a) = anonfuns
|
||||
|
||||
def init(self):
|
||||
"""initialize component"""
|
||||
setattr(self, name, init_a())
|
||||
|
||||
def decode(self, node):
|
||||
"""decode component from DOM node"""
|
||||
setattr(self, name, decode_a(node))
|
||||
|
||||
def encode(self, xml, node):
|
||||
"""encode self inside, possibly new, DOM node using xml"""
|
||||
if hasattr(self, name):
|
||||
value = getattr(self, name)
|
||||
else:
|
||||
value = None
|
||||
newnode = encode_a(xml, value)
|
||||
if newnode:
|
||||
mergetext(node, newnode)
|
||||
|
||||
def format(self):
|
||||
if hasattr(self, name):
|
||||
value = getattr(self,name)
|
||||
return '%s: %s\n' % (token, format_a(value))
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory variable %s not available' % name)
|
||||
return ''
|
||||
|
||||
return (init, decode, encode, format)
|
||||
|
||||
def mixed_case(cls, identifier):
|
||||
"""helper function to turn token name into mixed case"""
|
||||
if identifier is "":
|
||||
return ""
|
||||
else:
|
||||
return identifier[0].lower() + 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
|
||||
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, createnode, 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):
|
||||
"""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 Error:
|
||||
raise Error('Type mismatch: read text cannot be decoded')
|
||||
return value
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory token %s not available' % token)
|
||||
else:
|
||||
return None
|
||||
|
||||
def encode(xml, value):
|
||||
"""encode given value inside DOM node"""
|
||||
if value:
|
||||
node = createnode(xml, token)
|
||||
writetext(xml, node, token, str(value))
|
||||
return node
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory argument not available')
|
||||
|
||||
def format(value):
|
||||
"""format value for pretty printing"""
|
||||
return str(value)
|
||||
|
||||
return initialize, decode, encode, 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():
|
||||
return tag_type.__new__(tag_type)
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node):
|
||||
node = getNode(node, tag)
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node)
|
||||
return obj
|
||||
except Error:
|
||||
raise Error('Type mismatch: DOM cannot be decoded')
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory argument not available')
|
||||
else:
|
||||
return None
|
||||
|
||||
def encode(xml, obj):
|
||||
if obj:
|
||||
try:
|
||||
node = obj.encode(xml)
|
||||
#FIXME: change node's tag?
|
||||
return
|
||||
except Error:
|
||||
raise Error('Object cannot be encoded')
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory argument not available')
|
||||
|
||||
def format(obj):
|
||||
return obj.format()
|
||||
|
||||
return (init, decode, encode, format)
|
||||
|
||||
def gen_list_tag(cls, tag, spec):
|
||||
"""generate a list datatype. stores comps in tag/comp_tag"""
|
||||
name, tag_type, req, comp_tag = cls.parse_spec(tag, spec)
|
||||
#head, last = cls.tagpath_head_last(path)
|
||||
|
||||
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, format_item) = x
|
||||
|
||||
def init():
|
||||
return []
|
||||
|
||||
def decode(node):
|
||||
l = []
|
||||
nodes = getAllNodes(node, tag + '/' + comp_tag)
|
||||
print node, tag + '/' + comp_tag
|
||||
print 'F U', nodes
|
||||
if len(nodes) is 0 and req is mandatory:
|
||||
raise Error('Mandatory list empty')
|
||||
for node in nodes:
|
||||
dummy = node.ownerDocument.createElement("Dummy")
|
||||
dummy.appendChild(node)
|
||||
l.append(decode_item(dummy))
|
||||
return l
|
||||
|
||||
def encode(xml, l):
|
||||
dummy = xml.newNode("Dummy")
|
||||
if len(l) > 0:
|
||||
for item in l:
|
||||
item_node = encode_item(xml, item)
|
||||
xml.addNodeUnder(dummy, comp_tag, item_node)
|
||||
return getNode(dummy, "Dummy")
|
||||
else:
|
||||
if req is mandatory:
|
||||
raise Error('Mandatory list empty')
|
||||
|
||||
def format(l):
|
||||
#print 'format:', name
|
||||
s = ''
|
||||
for ix in range(len(l)):
|
||||
s += str(ix+1) + ': ' + format_item(l[ix])
|
||||
if ix != len(l)-1:
|
||||
s += ', '
|
||||
return s
|
||||
|
||||
return (init, decode, encode, format)
|
||||
|
||||
basic_cons_map = {
|
||||
types.StringType : str,
|
||||
types.IntType : int
|
||||
}
|
||||
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, rootTag):
|
||||
self.rootTag = rootTag
|
||||
self.newDOM()
|
||||
|
||||
def newDOM(self):
|
||||
"""clear DOM"""
|
||||
impl = mdom.getDOMImplementation()
|
||||
self.dom = impl.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
self.dom.unlink()
|
||||
|
||||
def readxml(self, fileName):
|
||||
try:
|
||||
self.dom = mdom.parse(fileName)
|
||||
except ExpatError, inst:
|
||||
raise Error("File '%s' has invalid XML: %s\n" % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def writexml(self, fileName):
|
||||
f = codecs.open(fileName,'w', "utf-8")
|
||||
f.write(self.dom.toprettyxml())
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.dom.documentElement.tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error("Root tagname %s not identical to %s as expected " %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.dom.createElement(tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.dom.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.dom.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.dom.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.dom.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.dom, self.dom.documentElement, tagPath,
|
||||
newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.dom, node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.dom.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
#
|
||||
# Install script for tengis.
|
||||
#
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
PISI_VERSION = "0.1"
|
||||
|
||||
def getRevision():
|
||||
import os
|
||||
try:
|
||||
p = os.popen("svn info 2> /dev/null")
|
||||
for line in p.readlines():
|
||||
line = line.strip()
|
||||
if line.startswith("Revision:"):
|
||||
return line.split(":")[1].strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
# doesn't working in a Subversion directory
|
||||
return None
|
||||
|
||||
def getVersion():
|
||||
rev = getRevision()
|
||||
if rev:
|
||||
return "-r".join([PISI_VERSION, rev])
|
||||
else:
|
||||
return PISI_VERSION
|
||||
|
||||
setup(name="pisi",
|
||||
version= getVersion(),
|
||||
description="PISI (Packages Installed Successfully as Intended)",
|
||||
long_description="PISI is the package management system of Pardus Linux.",
|
||||
license="GNU GPL2",
|
||||
author="Pardus Developers",
|
||||
author_email="pisi@uludag.org.tr",
|
||||
url="http://www.uludag.org.tr/eng/pisi/",
|
||||
package_dir = {'': ''},
|
||||
packages = ['pisi', 'pisi.cli', 'pisi.actionsapi'],
|
||||
scripts = ['pisi-cli']
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
PISI test suite
|
||||
---------------
|
||||
|
||||
|
||||
There are python unit tests and shell scripts in this directory.
|
||||
|
||||
Shell scripts use the CLIs to perform package operations.
|
||||
|
||||
For this there must be a symlink "packages" to the PISI package repository
|
||||
until we can find a few sample packages to put here.
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
|
||||
<A href="http://www.cs.bilkent.edu.tr/~erayo">
|
||||
<Name>Eray Ozkural</Name>
|
||||
<Number>868</Number>
|
||||
<Description>Lazy Tin</Description>
|
||||
<Description xml:lang="tr">Tembel Teneke</Description>
|
||||
<Projects>
|
||||
<Project>pisi</Project>
|
||||
<Project>noatun</Project>
|
||||
<Project>kdevelop</Project>
|
||||
</Projects>
|
||||
<OtherInfo>
|
||||
<BirthDate>18071976</BirthDate>
|
||||
<Interest>AI</Interest>
|
||||
<CodesWith>
|
||||
<Person>Baris</Person>
|
||||
<Person>Gurer</Person>
|
||||
<Person>Caglar</Person>
|
||||
<Person>Meren</Person>
|
||||
</CodesWith>
|
||||
</OtherInfo>
|
||||
</A>
|
||||
@@ -1,51 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
class ActionsAPITestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.f = zipfile.ZipFile("helloworld-0.1-1.pisi", "r")
|
||||
self.filelist = []
|
||||
|
||||
for file in self.f.namelist():
|
||||
self.filelist.append(file)
|
||||
|
||||
def testFileList(self):
|
||||
fileContent = ["files.xml", \
|
||||
"install/bin/helloworld", \
|
||||
"install/opt/PARDUS", \
|
||||
"install/opt/helloworld/helloworld", \
|
||||
"install/opt/uludag", \
|
||||
"install/sbin/helloworld", \
|
||||
"install/sys/PARDUS", \
|
||||
"install/sys/uludag", \
|
||||
"install/usr/bin/goodbye", \
|
||||
"install/usr/bin/helloworld", \
|
||||
"install/usr/lib/helloworld.o", \
|
||||
"install/usr/sbin/goodbye", \
|
||||
"install/usr/sbin/helloworld", \
|
||||
"install/usr/share/doc/helloworld-0.1-1/Makefile.am", \
|
||||
"install/usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp", \
|
||||
"install/usr/share/info/Makefile.am", \
|
||||
"install/usr/share/info/Makefile.cvs", \
|
||||
"install/usr/share/info/Makefile.in", \
|
||||
"install/var/goodbye", \
|
||||
"install/var/hello", \
|
||||
"metadata.xml"]
|
||||
|
||||
'''check number of files in package'''
|
||||
self.assertEqual(fileContent.__len__(), self.filelist.__len__())
|
||||
|
||||
'''check file content'''
|
||||
for file in self.filelist:
|
||||
self.assert_(fileContent.__contains__(file))
|
||||
|
||||
suite = unittest.makeSuite(ActionsAPITestCase)
|
||||
@@ -1,116 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
from os.path import exists as pathexists
|
||||
from os.path import basename, islink, join
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
from pisi import archive
|
||||
from pisi import sourcearchive
|
||||
from pisi import fetcher
|
||||
from pisi import util
|
||||
from pisi.build import BuildContext
|
||||
from pisi.config import config
|
||||
from pisi import uri
|
||||
|
||||
class ArchiveFileTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pisi.api.init()
|
||||
|
||||
def testUnpackTar(self):
|
||||
bctx = BuildContext("tests/popt/pspec.xml")
|
||||
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
|
||||
assert bctx.spec.source.archiveType == "targz"
|
||||
|
||||
# skip fetching and directly unpack the previously fetched (by
|
||||
# fetchertests) archive
|
||||
if not achv.is_cached(interactive=False):
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack()
|
||||
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
# but testing is hard
|
||||
# "var/tmp/pisi/popt-1.7-3/work" (targetDir)
|
||||
assert pathexists(targetDir + "/popt-1.7")
|
||||
|
||||
testfile = targetDir + "/popt-1.7/Makefile.am"
|
||||
assert pathexists(testfile)
|
||||
|
||||
# check file integrity
|
||||
self.assertEqual(util.sha1_file(testfile),
|
||||
"5af9dd7d754f788cf511c57ce0af3d555fed009d")
|
||||
|
||||
def testUnpackZip(self):
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
|
||||
assert bctx.spec.source.archiveType == "zip"
|
||||
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack(cleanDir=True)
|
||||
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
assert pathexists(targetDir + "/pccts")
|
||||
|
||||
testfile = targetDir + "/pccts/history.txt"
|
||||
assert pathexists(testfile)
|
||||
|
||||
# check file integrity
|
||||
self.assertEqual(util.sha1_file(testfile),
|
||||
"f2be0f9783e84e98fe4e2b8201a8f506fcc07a4d")
|
||||
|
||||
# TODO: no link file in pccts package. Need to find a ZIP file
|
||||
# containing a symlink
|
||||
# check for symbolic links
|
||||
# testfile = targetDir + "/sandbox/testdir/link1"
|
||||
# assert islink(testfile)
|
||||
|
||||
def testMakeZip(self):
|
||||
# first unpack our dear sandbox.zip
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
achv = sourcearchive.SourceArchive(bctx)
|
||||
achv.fetch(interactive=False)
|
||||
achv.unpack(cleanDir=True)
|
||||
del achv
|
||||
|
||||
newZip = targetDir + "/new.zip"
|
||||
zip = archive.ArchiveZip(newZip, 'zip', 'w')
|
||||
sourceDir = targetDir + "/pccts"
|
||||
zip.add_to_archive(sourceDir)
|
||||
zip.close()
|
||||
|
||||
#TODO: do some more work to test the integrity of new zip file
|
||||
|
||||
|
||||
def testUnpackZipCond(self):
|
||||
bctx = BuildContext("tests/pccts/pspec.xml")
|
||||
url = uri.URI(bctx.spec.source.archiveUri)
|
||||
targetDir = bctx.pkg_work_dir()
|
||||
filePath = join(config.archives_dir(), url.filename())
|
||||
|
||||
# check cached
|
||||
if util.sha1_file(filePath) != bctx.spec.source.archiveSHA1:
|
||||
fetch = fetcher.Fetcher(bctx.spec.source.archiveUri, targetDir)
|
||||
fetch.fetch()
|
||||
assert bctx.spec.source.archiveType == "zip"
|
||||
|
||||
achv = archive.Archive(filePath, bctx.spec.source.archiveType)
|
||||
achv.unpack_files(["pccts/history.txt"], targetDir)
|
||||
assert pathexists(targetDir + "/pccts")
|
||||
testfile = targetDir + "/pccts/history.txt"
|
||||
assert pathexists(testfile)
|
||||
|
||||
suite = unittest.makeSuite(ArchiveFileTestCase)
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
pwd
|
||||
PATH=$PATH:.
|
||||
set -x
|
||||
pisi-cli --ignore-comar remove unzip
|
||||
set -e
|
||||
pisi-cli --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml
|
||||
pisi-cli index .
|
||||
pisi-cli add-repo repo1 pisi-index.xml
|
||||
pisi-cli list-repo
|
||||
pisi-cli update-repo repo1
|
||||
pisi-cli list-available
|
||||
pisi-cli --ignore-comar install zip
|
||||
pisi-cli list-installed
|
||||
pisi-cli --ignore-comar remove unzip
|
||||
pisi-cli info zip*.pisi
|
||||
pisi-cli --ignore-comar install zip*.pisi
|
||||
pisi-cli list-pending
|
||||
pisi-cli configure-pending
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
export PATH=$PATH:.
|
||||
set -x # xtrace
|
||||
set -e # errexit
|
||||
|
||||
echo "beta functionality test script for testing remote repos"
|
||||
echo "working directory:" `pwd`
|
||||
echo "cleaning destination dir: tmp"
|
||||
rm -rf tmp
|
||||
#echo "*** repository tests"
|
||||
pisi-cli add-repo pardus ftp://ftp.uludag.org.tr/pub/pisi/binary/system/base/pisi-index.xml
|
||||
pisi-cli update-repo pardus
|
||||
pisi-cli list-repo
|
||||
|
||||
#echo "*** package ops"
|
||||
pisi-cli list-available
|
||||
pisi-cli info python
|
||||
pisi-cli install python
|
||||
|
||||
echo "*** database contents"
|
||||
for x in `find tmp -iname '*.bdb'`; do
|
||||
echo "contents of database " $x;
|
||||
tools/cat-db.py $x;
|
||||
done
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
pwd
|
||||
PATH=$PATH:.
|
||||
set -x -e
|
||||
|
||||
pisi-cli --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml
|
||||
pisi-cli --yes-all --ignore-comar install unzip-5.50-1.pisi zip-2.3-1.pisi
|
||||
|
||||
mkdir -p myrepo
|
||||
cd myrepo
|
||||
../pisi-cli --ignore-build-no build ../tests/zip2/pspec.xml ../tests/unzip2/pspec.xml
|
||||
cd ..
|
||||
pisi-cli --absolute-uris index myrepo
|
||||
pisi-cli remove-repo repo1
|
||||
pisi-cli add-repo repo1 pisi-index.xml
|
||||
pisi-cli list-repo
|
||||
pisi-cli update-repo repo1
|
||||
pisi-cli list-available
|
||||
pisi-cli --install-info list-installed
|
||||
pisi-cli list-upgrades
|
||||
pisi-cli --ignore-comar upgrade zip
|
||||
pisi-cli --install-info list-installed
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "beta functionality test script"
|
||||
echo "working directory:" `pwd`
|
||||
echo "cleaning destination dir: tmp"
|
||||
PATH=$PATH:.
|
||||
set -x # xtrace
|
||||
set -e # errexit
|
||||
rm -rf tmp
|
||||
#echo "*** build tests"
|
||||
pisi-cli build https://svn.uludag.org.tr/pisi/trunk/system/base/zip/pspec.xml \
|
||||
https://svn.uludag.org.tr/pisi/trunk/system/base/unzip/pspec.xml
|
||||
|
||||
#partial-builds
|
||||
pisi-cli build-setup https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml
|
||||
pisi-cli build-build https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml
|
||||
pisi-cli build-install https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml
|
||||
pisi-cli build-package https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml
|
||||
|
||||
#echo "*** repository tests"
|
||||
|
||||
pisi-cli index .
|
||||
pisi-cli add-repo repo1 pisi-index.xml
|
||||
pisi-cli update-repo repo1
|
||||
pisi-cli list-repo
|
||||
|
||||
pisi-cli build https://svn.uludag.org.tr/pisi/trunk/system/base/grep/pspec.xml \
|
||||
https://svn.uludag.org.tr/pisi/trunk/system/base/flex/pspec.xml
|
||||
|
||||
#echo "*** package ops"
|
||||
pisi-cli info *.pisi
|
||||
# pisi-cli list-available
|
||||
pisi-cli install --ignore-comar zip
|
||||
pisi-cli list-installed
|
||||
pisi-cli remove --ignore-comar unzip
|
||||
pisi-cli install --ignore-comar zip*.pisi
|
||||
pisi-cli install --ignore-comar hdparm*.pisi flex*.pisi grep*.pisi
|
||||
pisi-cli remove-repo repo1
|
||||
# pisi-cli list-available
|
||||
|
||||
echo "*** database contents"
|
||||
for x in `find tmp -iname '*.bdb'`; do
|
||||
echo "contents of database " $x;
|
||||
tools/cat-db.py $x;
|
||||
done
|
||||
@@ -1,45 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
from pisi.configfile import ConfigurationFile
|
||||
|
||||
class ConfigFileTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cf = ConfigurationFile("tests/pisi.conf")
|
||||
|
||||
def testSections(self):
|
||||
cf = self.cf
|
||||
if not cf.general:
|
||||
self.fail("No 'general' section found in ConfigurationFile")
|
||||
if not cf.build:
|
||||
self.fail("No 'build' section found in ConfigurationFile")
|
||||
if not cf.dirs:
|
||||
self.fail("No 'dirs' section found in ConfigurationFile")
|
||||
|
||||
def testValues(self):
|
||||
cf = self.cf
|
||||
|
||||
# test values from pisi.conf file
|
||||
self.assertEqual(cf.general.destinationdirectory, "/testing")
|
||||
self.assertEqual(cf.dirs.archives_dir, "/disk2/pisi/archives")
|
||||
|
||||
# test default values
|
||||
self.assertEqual(cf.dirs.tmp_dir, "/var/tmp/pisi")
|
||||
|
||||
def testAccessMethods(self):
|
||||
cf = self.cf
|
||||
|
||||
self.assertEqual(cf.build.host, cf.build["host"])
|
||||
self.assertEqual(cf.dirs.index_dir, cf.dirs["index_dir"])
|
||||
|
||||
suite = unittest.makeSuite(ConfigFileTestCase)
|
||||
@@ -1,67 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
import pisi.context as ctx
|
||||
|
||||
class ContextTestCase(unittest.TestCase):
|
||||
|
||||
def testConstness(self):
|
||||
const = ctx.const
|
||||
|
||||
# test if we can get a const attribute?
|
||||
try:
|
||||
test = const.package_prefix
|
||||
self.assertNotEqual(test, "")
|
||||
except AttributeError:
|
||||
self.fail("Couldn't get const attribute")
|
||||
|
||||
# test binding a new constant
|
||||
const.test = "test binding"
|
||||
|
||||
# test re-binding (which is illegal)
|
||||
try:
|
||||
const.test = "test rebinding"
|
||||
# we shouldn't reach here
|
||||
self.fail("Rebinding a constant works. Something is wrong!")
|
||||
except:
|
||||
# we achived our goal with this error. infact, this is a
|
||||
# ConstError but we can't catch it directly here
|
||||
pass
|
||||
|
||||
# test unbinding (which is also illegal)
|
||||
try:
|
||||
del const.test
|
||||
# we shouldn't reach here
|
||||
self.fail("Unbinding a constant works. Something is wrong!")
|
||||
except:
|
||||
# we achived our goal with this error. infact, this is a
|
||||
# ConstError but we can't catch it directly here
|
||||
pass
|
||||
|
||||
def testConstValues(self):
|
||||
const = ctx.const
|
||||
|
||||
constDict = {
|
||||
"actions_file": "actions.py",
|
||||
"setup_func": "setup",
|
||||
"metadata_xml": "metadata.xml"
|
||||
}
|
||||
|
||||
for k in constDict.keys():
|
||||
if hasattr(const, k):
|
||||
value = getattr(const, k)
|
||||
self.assertEqual(value, constDict[k])
|
||||
else:
|
||||
self.fail("Constants does not have an attribute named %s" % k)
|
||||
|
||||
|
||||
suite = unittest.makeSuite(ContextTestCase)
|
||||
@@ -1,38 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
from pisi.specfile import SpecFile
|
||||
from pisi import fetcher
|
||||
from pisi import util
|
||||
from pisi import uri
|
||||
|
||||
class FetcherTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pisi.api.init()
|
||||
|
||||
self.spec = SpecFile()
|
||||
self.spec.read("tests/popt/pspec.xml")
|
||||
self.url = uri.URI(self.spec.source.archiveUri)
|
||||
self.destpath = ctx.config.archives_dir()
|
||||
self.fetch = fetcher.Fetcher(self.url, self.destpath)
|
||||
|
||||
def testFetch(self):
|
||||
self.fetch.fetch()
|
||||
fetchedFile = os.path.join(self.destpath, self.url.filename())
|
||||
if os.access(fetchedFile, os.R_OK):
|
||||
self.assertEqual(util.sha1_file(fetchedFile),
|
||||
self.spec.source.archiveSHA1)
|
||||
|
||||
suite = unittest.makeSuite(FetcherTestCase)
|
||||
@@ -1,34 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import graph
|
||||
from pisi.config import config
|
||||
|
||||
class GraphTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.g0 = graph.Digraph()
|
||||
self.g0.from_list([ (1,2), (1,3), (2,3), (3,4), (4, 5), (4,1)])
|
||||
|
||||
self.g1 = graph.Digraph()
|
||||
self.g1.from_list([ (0,2), (0,3), (3,4), (2,4), (0,5), (5,4) ])
|
||||
|
||||
def testCycle(self):
|
||||
self.assert_(not self.g0.cycle_free())
|
||||
self.assert_(self.g1.cycle_free())
|
||||
|
||||
def testTopologicalSort(self):
|
||||
order = self.g1.topological_sort()
|
||||
self.assertEqual(order[0], 0)
|
||||
self.assertEqual(order[len(order)-1], 4)
|
||||
|
||||
suite = unittest.makeSuite(GraphTestCase)
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
from pisi.actionsapi import pisitools
|
||||
from pisi.actionsapi import shelltools
|
||||
from pisi.actionsapi import libtools
|
||||
from pisi.actionsapi import get
|
||||
|
||||
WorkDir = "helloworld"
|
||||
|
||||
def setup():
|
||||
autotools.configure()
|
||||
|
||||
def build():
|
||||
autotools.make()
|
||||
|
||||
def install():
|
||||
autotools.install()
|
||||
|
||||
'''/opt/helloworld/'''
|
||||
pisitools.dodir("/opt/helloworld")
|
||||
|
||||
'''/usr/share/doc/helloworld-0.1-1/Makefile.am'''
|
||||
pisitools.dodoc("Makefile.am")
|
||||
|
||||
'''/opt/helloworld/helloworld'''
|
||||
pisitools.doexe("src/helloworld", "/opt/helloworld")
|
||||
|
||||
'''/usr/share/info/Makefile.am'''
|
||||
'''/usr/share/info/Makefile.cvs'''
|
||||
'''/usr/share/info/Makefile.in'''
|
||||
pisitools.doinfo("Makefile.*")
|
||||
|
||||
'''/usr/lib/helloworld.o'''
|
||||
pisitools.dolib("src/helloworld.o")
|
||||
|
||||
'''/opt/hello'''
|
||||
pisitools.insinto("/opt/", "src/helloworld", "hello")
|
||||
'''/opt/hi'''
|
||||
pisitools.insinto("/opt/", "src/helloworld", "hi")
|
||||
|
||||
'''/opt/hello -> /var/hello'''
|
||||
pisitools.domove("/opt/hello", "/var/")
|
||||
'''/opt/hi -> /var/goodbye'''
|
||||
pisitools.domove("/opt/hi", "/var/", "goodbye")
|
||||
|
||||
'''/usr/bin/helloworld'''
|
||||
pisitools.dobin("src/helloworld")
|
||||
'''/bin/helloworld'''
|
||||
pisitools.dobin("src/helloworld", "/bin")
|
||||
|
||||
'''/usr/sbin/helloworld'''
|
||||
pisitools.dosbin("src/helloworld")
|
||||
'''/sbin/helloworld'''
|
||||
pisitools.dosbin("src/helloworld", "/sbin")
|
||||
|
||||
'''Hello, world! -> Goodbye, world!'''
|
||||
pisitools.dosed("src/helloworld.cpp", "Hello, world!", "Goodbye, world!")
|
||||
|
||||
'''/usr/sbin/goodbye --> helloworld'''
|
||||
pisitools.dosym("helloworld", "/usr/sbin/goodbye")
|
||||
'''/usr/bin/goodbye --> helloworld'''
|
||||
pisitools.dosym("helloworld", "/usr/bin/goodbye")
|
||||
|
||||
'''/home/pardus/'''
|
||||
pisitools.dodir("/home/pardus")
|
||||
'''delete pardus'''
|
||||
pisitools.removeDir("/home/pardus")
|
||||
'''delete home'''
|
||||
pisitools.removeDir("/home")
|
||||
|
||||
'''src/helloworld.cpp --> /usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp'''
|
||||
pisitools.newdoc("src/helloworld.cpp", "goodbyeworld.cpp")
|
||||
|
||||
'''/opt/pardus'''
|
||||
shelltools.touch("%s/opt/pardus" % get.installDIR())
|
||||
|
||||
'''/opt/pardus --> /opt/uludag'''
|
||||
shelltools.copy("%s/opt/pardus" % get.installDIR(), "%s/opt/uludag" % get.installDIR())
|
||||
'''/opt/pardus --> /opt/Pardus'''
|
||||
shelltools.move("%s/opt/pardus" % get.installDIR(), "%s/opt/PARDUS" % get.installDIR())
|
||||
|
||||
'''/opt/ --> /sys/'''
|
||||
shelltools.copytree("%s/opt/" % get.installDIR(), "%s/sys/" % get.installDIR())
|
||||
|
||||
'''delete /sys/helloworld/helloworld'''
|
||||
shelltools.unlink("%s/sys/helloworld/helloworld" % get.installDIR())
|
||||
'''delete /sys/helloworld'''
|
||||
shelltools.unlinkDir("%s/sys/helloworld" % get.installDIR())
|
||||
|
||||
'''generate /usr/lib/helloworld.o'''
|
||||
libtools.gen_usr_ldscript("helloworld.o")
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
|
||||
<!DOCTYPE PISI SYSTEM "http://www.uludag.org.tr/projeler/pisi/pisi-spec.dtd">
|
||||
|
||||
<PISI>
|
||||
<Source>
|
||||
<Name>helloworld</Name>
|
||||
<Homepage>http://www.uludag.org.tr</Homepage>
|
||||
<Packager>
|
||||
<Name>S.Çağlar Onur</Name>
|
||||
<Email>caglar@uludag.org.tr</Email>
|
||||
</Packager>
|
||||
<License>GPL-2</License>
|
||||
<IsA>category</IsA>
|
||||
<PartOf>component</PartOf>
|
||||
<Summary xml:lang="en">Dummy HelloWorld for testing ActionsAPI</Summary>
|
||||
<Description xml:lang="en">Dummy HelloWorld for testing ActionsAPI</Description>
|
||||
<Archive type="targz" sha1sum="6242681ee0bceb820eb6e8544ad1bb3d0ba3ee78">http://cekirdek.uludag.org.tr/~caglar/helloworld.tar.gz</Archive>
|
||||
<History>
|
||||
<Update>
|
||||
<Date>2005-08-21</Date>
|
||||
<Version>0.1</Version>
|
||||
<Release>1</Release>
|
||||
</Update>
|
||||
</History>
|
||||
</Source>
|
||||
|
||||
<Package>
|
||||
<Name>helloworld</Name>
|
||||
<Files>
|
||||
<Path fileType="binary">/</Path>
|
||||
</Files>
|
||||
</Package>
|
||||
</PISI>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
import pisi.installdb
|
||||
from pisi import util
|
||||
|
||||
class InstallDBTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pisi.api.init()
|
||||
self.installdb = ctx.installdb
|
||||
|
||||
def testRemoveDummy(self):
|
||||
self.installdb.remove('installtest')
|
||||
self.assert_(not self.installdb.is_installed('installtest'))
|
||||
|
||||
def testInstall(self):
|
||||
self.installdb.purge('installtest')
|
||||
self.installdb.install('installtest', '0.1', '2', '3')
|
||||
|
||||
def testRemovePurge(self):
|
||||
self.installdb.install('installtest', '0.1', '2', '3')
|
||||
self.assert_(self.installdb.is_installed('installtest'))
|
||||
self.installdb.remove('installtest')
|
||||
self.assert_(self.installdb.is_removed('installtest'))
|
||||
self.installdb.purge('installtest')
|
||||
self.assert_(not self.installdb.is_recorded('installtest'))
|
||||
|
||||
suite = unittest.makeSuite(InstallDBTestCase)
|
||||
@@ -1,43 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import metadata
|
||||
from pisi import util
|
||||
from pisi.config import config
|
||||
|
||||
class MetaDataTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def testRead(self):
|
||||
md = metadata.MetaData()
|
||||
md.read('tests/popt/metadata.xml')
|
||||
|
||||
self.assertEqual(md.package.license, ["As-Is"])
|
||||
|
||||
self.assertEqual(md.package.version, "1.7")
|
||||
|
||||
self.assertEqual(md.package.installedSize, 149691)
|
||||
return md
|
||||
|
||||
def testWrite(self):
|
||||
md = self.testRead()
|
||||
md.write(os.path.join(config.tmp_dir(),'metadata-test.xml' ))
|
||||
|
||||
def testVerify(self):
|
||||
md = self.testRead()
|
||||
if md.has_errors():
|
||||
self.fail("Couldn't verify!")
|
||||
|
||||
|
||||
suite = unittest.makeSuite(MetaDataTestCase)
|
||||
@@ -1,43 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
from pisi.packagedb import PackageDB
|
||||
from pisi import util
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
class PackageDBTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# setUp will be called for each test individually
|
||||
pisi.api.init()
|
||||
|
||||
self.spec = SpecFile()
|
||||
self.spec.read('tests/popt/pspec.xml')
|
||||
|
||||
self.pdb = PackageDB('testdb')
|
||||
|
||||
def testAdd(self):
|
||||
self.pdb.add_package(self.spec.packages[0])
|
||||
self.assert_(self.pdb.has_package('popt-libs'))
|
||||
# close the database and remove lock
|
||||
del self.pdb
|
||||
|
||||
def testRemove(self):
|
||||
self.pdb.remove_package('popt-libs')
|
||||
self.assert_(not self.pdb.has_package('popt-libs'))
|
||||
del self.pdb
|
||||
|
||||
suite = unittest.makeSuite(PackageDBTestCase)
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import util
|
||||
from pisi.config import config
|
||||
from pisi import package
|
||||
|
||||
|
||||
class PackageTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.pkgName = util.package_name("testing",
|
||||
"5.1",
|
||||
"2")
|
||||
|
||||
def testAddExtract(self):
|
||||
cur = os.getcwd()
|
||||
tmpdir = config.tmp_dir()
|
||||
testdir = os.path.join(cur, "tests/popt")
|
||||
|
||||
pkg_path = os.path.join(tmpdir, self.pkgName)
|
||||
pkg = package.Package(pkg_path, "w")
|
||||
|
||||
os.chdir(testdir)
|
||||
pkg.add_to_package("files.xml")
|
||||
pkg.add_to_package("metadata.xml")
|
||||
os.chdir(cur)
|
||||
|
||||
pkg.close()
|
||||
|
||||
pkg = package.Package(pkg_path)
|
||||
pkg.extract_file("files.xml", cur)
|
||||
if not os.path.exists("files.xml"):
|
||||
self.fail("Package extract error")
|
||||
|
||||
os.remove("files.xml")
|
||||
os.remove(pkg_path)
|
||||
|
||||
suite = unittest.makeSuite(PackageTestCase)
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
# Timu EREN <selamtux@gmail.com>
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
from pisi.actionsapi import get
|
||||
from pisi.actionsapi import pisitools
|
||||
from pisi.actionsapi import shelltools
|
||||
|
||||
WorkDir = "pccts"
|
||||
|
||||
def setup():
|
||||
shelltools.export("COPT", get.CFLAGS())
|
||||
|
||||
def build():
|
||||
# autotools.make("COPT=%s" % (get.CFLAGS()))
|
||||
autotools.make()
|
||||
|
||||
def install():
|
||||
#autotools.install("")
|
||||
|
||||
pisitools.dobin("bin/antlr")
|
||||
pisitools.dobin("bin/dlg")
|
||||
pisitools.dobin("bin/genmk")
|
||||
pisitools.dobin("bin/sor")
|
||||
|
||||
pisitools.insinto("/usr/include/pccts","h/*.h")
|
||||
pisitools.insinto("/usr/include/pccts","h/*.c")
|
||||
pisitools.insinto("/usr/include/pccts","h/*.cpp")
|
||||
|
||||
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.h")
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.c")
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.cpp")
|
||||
|
||||
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.h")
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.c")
|
||||
pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.cpp")
|
||||
|
||||
pisitools.dodoc("CHANGES*", "KNOWN_PROBLEMS*", "README", "RIGHTS", "history.txt", "history.ps")
|
||||
pisitools.dodoc("sorcerer/README", "sorcerer/UPDATES")
|
||||
|
||||
pisitools.doman("dlg/dlg.1", "antlr/antlr.1")
|
||||
@@ -1,69 +0,0 @@
|
||||
--- pccts-1.33.33/support/genmk/genmk.c Fri Aug 3 17:12:51 2001
|
||||
+++ pccts-1.33.33/support/genmk/genmk-gentoo.c Fri Aug 3 17:12:14 2001
|
||||
@@ -7,6 +7,11 @@
|
||||
* U of MN
|
||||
*/
|
||||
|
||||
+/* modified 20010803 by Peter Kadau
|
||||
+ * for better fhs-compliance
|
||||
+ * i.e. from "none" to "hmmm soso" ;-)
|
||||
+ */
|
||||
+
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pcctscfg.h" /* be sensitive to what ANTLR/DLG call the files */
|
||||
@@ -65,7 +70,7 @@
|
||||
static int nondef_comp = 0; /* 1=compiler is non default */
|
||||
static char *compilerCCC="CC";
|
||||
static char *compilerCC="cc";
|
||||
-static char *pccts_path="/usr/local/pccts";
|
||||
+static char *pccts_path="/usr/include/pccts";
|
||||
|
||||
#ifdef __STDC__
|
||||
void help(void);
|
||||
@@ -356,7 +361,7 @@
|
||||
{ "-cfiles",1,pCFiles, "Additional files in C or C++ to compile"},
|
||||
{ "-sor",0,pSor, "Start of sorcerer group"},
|
||||
{ "-pccts_path",1,ppccts_path,
|
||||
- "Path for $PCCTS directory (default is /usr/local/pccts)"},
|
||||
+ "Path for $PCCTS directory (default is /usr/include/pccts)"},
|
||||
{ "-compiler",1,pCompiler,
|
||||
"Default compiler (default is CC/cc)"},
|
||||
{ "*", 0,pFile, "" }, /* anything else is a file */
|
||||
@@ -495,13 +500,13 @@
|
||||
else printf("SCAN = %s%s\n", DIR(), dlg_class);
|
||||
|
||||
printf("PCCTS = %s\n",pccts_path);
|
||||
- printf("ANTLR_H = $(PCCTS)%sh\n", DirectorySymbol);
|
||||
+ printf("ANTLR_H = $(PCCTS)\n");
|
||||
if (num_sors>0) {
|
||||
- printf("SOR_H = $(PCCTS)%ssorcerer%sh\n", DirectorySymbol, DirectorySymbol);
|
||||
+ printf("SOR_H = $(PCCTS)%ssorcerer\n", DirectorySymbol);
|
||||
printf("SOR_LIB = $(PCCTS)%ssorcerer%slib\n",
|
||||
DirectorySymbol, DirectorySymbol);
|
||||
}
|
||||
- printf("BIN = $(PCCTS)%sbin\n", DirectorySymbol);
|
||||
+ printf("BIN = %susr%sbin\n", DirectorySymbol, DirectorySymbol);
|
||||
printf("ANTLR = $(BIN)%santlr\n", DirectorySymbol);
|
||||
printf("DLG = $(BIN)%sdlg\n", DirectorySymbol);
|
||||
if (num_sors>0) printf("SOR = $(BIN)%ssor\n", DirectorySymbol);
|
||||
--- pccts-1.33.33/sorcerer/lib/sorlist.c 1999-06-30 03:15:56.000000000 +0200
|
||||
+++ pccts-1.33.33/sorcerer/lib/sorlist.c.new 2003-05-23 11:11:09.000000000 +0200
|
||||
@@ -28,6 +28,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
+#include "pcctscfg.h"
|
||||
|
||||
#ifdef PCCTS_USE_STDARG
|
||||
#include <stdarg.h>
|
||||
--- pccts-1.33.33/sorcerer/lib/sintstack.c 1999-06-30 15:08:06.000000000 +0200
|
||||
+++ pccts-1.33.33/sorcerer/lib/sintstack.c.new 2003-05-23 12:54:26.000000000 +0200
|
||||
@@ -28,6 +28,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
+#include "pcctscfg.h"
|
||||
|
||||
#ifdef PCCTS_USE_STDARG
|
||||
#include <stdarg.h>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<!DOCTYPE PISI SYSTEM "http://www.uludag.org.tr/projeler/pisi/pisi-spec.dtd">
|
||||
<PISI>
|
||||
<Source>
|
||||
<Name>pccts</Name>
|
||||
<Homepage>http://www.polhode.com</Homepage>
|
||||
<Packager>
|
||||
<Name>Timu EREN</Name>
|
||||
<Email>selamtux@gmail.com</Email>
|
||||
</Packager>
|
||||
<License>BSD</License>
|
||||
<IsA>category</IsA>
|
||||
<PartOf>component</PartOf>
|
||||
<Summary xml:lang="en">Purdue Compiler Construction Tool Set is an embedded C/C++ parser generator</Summary>
|
||||
<Description xml:lang="en">Purdue Compiler Construction Tool Set is an embedded C/C++ parser generator</Description>
|
||||
<Archive type="zip" sha1sum="5b3417efd5f537434b568114bcda853b4975d851">http://www.polhode.com/pccts133mr33.zip</Archive>
|
||||
<Patches>
|
||||
<Patch level="1">pccts-1.33.33-gentoo.diff</Patch>
|
||||
</Patches>
|
||||
<BuildDependencies>
|
||||
<Dependency>unzip</Dependency>
|
||||
<Dependency>patch</Dependency>
|
||||
</BuildDependencies>
|
||||
<History>
|
||||
<Update>
|
||||
<Date>2005-09-16</Date>
|
||||
<Version>1.33.33</Version>
|
||||
<Release>1</Release>
|
||||
</Update>
|
||||
</History>
|
||||
</Source>
|
||||
<Package>
|
||||
<Name>pccts</Name>
|
||||
<Files>
|
||||
<Path fileType="header">/usr/include</Path>
|
||||
<Path fileType="doc">/usr/share/doc</Path>
|
||||
<Path fileType="executable">/usr/bin</Path>
|
||||
<Path fileType="man">/usr/share/man</Path>
|
||||
</Files>
|
||||
</Package>
|
||||
</PISI>
|
||||
@@ -1,6 +0,0 @@
|
||||
[general]
|
||||
destinationdirectory = /testing
|
||||
|
||||
[directories]
|
||||
archives_dir = /disk2/pisi/archives
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
|
||||
WorkDir='popt-1.7'
|
||||
|
||||
def setup():
|
||||
autotools.configure( '--with-nls' )
|
||||
|
||||
def build():
|
||||
autotools.make()
|
||||
|
||||
def install():
|
||||
autotools.install()
|
||||
@@ -1,507 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
<Files>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/es/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1593
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
29481dbd4051e5c6a0f000436664ba00e47572ae
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/de/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/is/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1793
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
edbcbb430b122626216ea9f6bba268d5de8454b6
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/lib/libpopt.la
|
||||
</Path>
|
||||
<Type>
|
||||
sharedLib
|
||||
</Type>
|
||||
<Size>
|
||||
699
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
04bac6fce89f2c84d4bb1c5adced729f7d312980
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/sl/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
514
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
2031bdefffcf57121e46f3f68f0747fe6b7f1fcc
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/fr/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/it/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/eu_ES/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/da/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1599
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
3ae14880a8bc2cddd9dd0a03861a55d663ac8b0d
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/ko/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1916
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
2079233b9e3284c866e5d17e5aca001f678d4c23
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/ja/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/pt/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1870
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
7db5d457fa3cc169fea2f747f25ba0353ed5fb69
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/lib/libpopt.so
|
||||
</Path>
|
||||
<Type>
|
||||
sharedLib
|
||||
</Type>
|
||||
<Size>
|
||||
38532
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
617c2afa4e79faee954cb95715470f875c044e5a
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/no/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1800
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
b33d41a1fbde6b879f0cce06cf2d8395012cc0ec
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/lib/libpopt.a
|
||||
</Path>
|
||||
<Type>
|
||||
sharedLib
|
||||
</Type>
|
||||
<Size>
|
||||
36100
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
f067421ebdb9a68f89c90526feb64a70763c95cf
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/wa/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
517
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
92f1f582ae24dbf5631fee1ace8bfc99cb5c1b66
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/tr/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1601
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
16e3e8e12af497102c75ccbb52edf8d1783545c3
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/zh/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/id/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/ro/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1150
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
914729e4200d68e5c55d114e6a083c74e692707b
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/sr/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/hu/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
496
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
236bf8c1fea44a5a93c6e18e0c88b3dae1762264
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/pt_BR/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/sv/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1844
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
63517df43bd74c84496441d8894f2b67533c6060
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/lib/libpopt.so.0.0.0
|
||||
</Path>
|
||||
<Type>
|
||||
sharedLib
|
||||
</Type>
|
||||
<Size>
|
||||
38532
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
1f2d3c41ba8e4d6b1c4979225cdf2dd97d4a0aea
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/ru/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1955
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
4742785bd5235eb437922f76a9faa30ec314d09b
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/zh_CN.GB2312/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
468
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
5e0a688794619278a452c851a3fc86b50b7f781e
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/lib/libpopt.so.0
|
||||
</Path>
|
||||
<Type>
|
||||
sharedLib
|
||||
</Type>
|
||||
<Size>
|
||||
38532
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
617c2afa4e79faee954cb95715470f875c044e5a
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/include/popt.h
|
||||
</Path>
|
||||
<Type>
|
||||
header
|
||||
</Type>
|
||||
<Size>
|
||||
15982
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
091692391fd1c539b19298f30e8ae074e3210459
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/sk/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
510
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
f7e081a22577eb5429809b7ce999b8b1e2b1f9b3
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/pl/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/uk/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
504
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
f91c695161926679401201fdaf3edcef4664f6ca
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/cs/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1806
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
920f5b99199361f1d169430bf13d2bfac1a02fab
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/man/man3/popt.3
|
||||
</Path>
|
||||
<Type>
|
||||
doc
|
||||
</Type>
|
||||
<Size>
|
||||
31011
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
5c0014442b4ef38d71733c952884573071978328
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/gl/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
1636
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
8486430a6ecbb006b5d374cbcb109e8303c7d5f8
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
<File>
|
||||
<Path>
|
||||
usr/share/locale/fi/LC_MESSAGES/popt.mo
|
||||
</Path>
|
||||
<Type>
|
||||
localedata
|
||||
</Type>
|
||||
<Size>
|
||||
345
|
||||
</Size>
|
||||
<SHA1Sum>
|
||||
6ceb513b02af4f612a20b49e5d615c158fdcee31
|
||||
</SHA1Sum>
|
||||
</File>
|
||||
</Files>
|
||||
Binary file not shown.
@@ -1,108 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
<PISI>
|
||||
<Source>
|
||||
<Name>
|
||||
popt
|
||||
</Name>
|
||||
<Packager>
|
||||
<Name>
|
||||
Kırmızı kafalar
|
||||
</Name>
|
||||
<Email>
|
||||
hotmail@redhat.com
|
||||
</Email>
|
||||
</Packager>
|
||||
</Source>
|
||||
<Package>
|
||||
<Name>
|
||||
popt-libs
|
||||
</Name>
|
||||
<Summary>
|
||||
Command line option parsing library
|
||||
</Summary>
|
||||
<Description>
|
||||
library files for popt
|
||||
</Description>
|
||||
<License>
|
||||
As-Is
|
||||
</License>
|
||||
<IsA>
|
||||
library:util:optparser
|
||||
</IsA>
|
||||
<PartOf>
|
||||
rpm:archive
|
||||
</PartOf>
|
||||
<RuntimeDependencies>
|
||||
<Dependency>
|
||||
gettext
|
||||
</Dependency>
|
||||
</RuntimeDependencies>
|
||||
<Files>
|
||||
<Path fileType="sharedLib">
|
||||
/usr/lib
|
||||
</Path>
|
||||
<Path fileType="doc">
|
||||
/usr/share/doc
|
||||
</Path>
|
||||
<Path fileType="doc">
|
||||
/usr/share/man
|
||||
</Path>
|
||||
<Path fileType="localedata">
|
||||
/usr/share/locale
|
||||
</Path>
|
||||
<Path fileType="header">
|
||||
/usr/include/popt.h
|
||||
</Path>
|
||||
</Files>
|
||||
<History>
|
||||
<Update>
|
||||
<Date>
|
||||
06/14/2005
|
||||
</Date>
|
||||
<Version>
|
||||
1.7
|
||||
</Version>
|
||||
<Release>
|
||||
3
|
||||
</Release>
|
||||
</Update>
|
||||
<Update>
|
||||
<Date>
|
||||
06/10/2005
|
||||
</Date>
|
||||
<Version>
|
||||
1.7
|
||||
</Version>
|
||||
<Release>
|
||||
2
|
||||
</Release>
|
||||
</Update>
|
||||
<Update>
|
||||
<Date>
|
||||
05/05/2005
|
||||
</Date>
|
||||
<Version>
|
||||
1.7
|
||||
</Version>
|
||||
<Release>
|
||||
1
|
||||
</Release>
|
||||
</Update>
|
||||
</History>
|
||||
<Build>
|
||||
0
|
||||
</Build>
|
||||
<Distribution>
|
||||
Pardus
|
||||
</Distribution>
|
||||
<DistributionRelease>
|
||||
0.1
|
||||
</DistributionRelease>
|
||||
<Architecture>
|
||||
Any
|
||||
</Architecture>
|
||||
<InstalledSize>
|
||||
149691
|
||||
</InstalledSize>
|
||||
</Package>
|
||||
</PISI>
|
||||
@@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
|
||||
<!DOCTYPE PSPEC SYSTEM
|
||||
"http://www.uludag.org.tr/projeler/pisi/pisi-spec.dtd">
|
||||
|
||||
<PISI>
|
||||
|
||||
<Source>
|
||||
<Name>popt</Name>
|
||||
<Homepage>http://www.rpm.org/</Homepage>
|
||||
<Packager>
|
||||
<Name>Kırmızı kafalar</Name>
|
||||
<Email>hotmail@redhat.com</Email>
|
||||
</Packager>
|
||||
<License>As-Is</License>
|
||||
<IsA>library:util:optparser</IsA>
|
||||
<PartOf>rpm:archive</PartOf>
|
||||
<Summary>Popt command line option parser</Summary>
|
||||
<Description>Command line option parsing library.
|
||||
While it is similiar to getopt(3), it contains a number of enhancements, including:
|
||||
|
||||
1) popt is fully reentrant
|
||||
2) popt can parse arbitrary argv[] style arrays while
|
||||
getopt(2) makes this quite difficult
|
||||
3) popt allows users to alias command line arguments
|
||||
4) popt provides convience functions for parsing strings
|
||||
into argv[] style arrays
|
||||
</Description>
|
||||
<Archive type="targz" sha1sum="66f3c77b87a160951b180447f4a6dce68ad2f71b">
|
||||
ftp://ftp.rpm.org/pub/rpm/dist/rpm-4.1.x/popt-1.7.tar.gz
|
||||
</Archive>
|
||||
<Patches>
|
||||
<Patch compressionType="gz" level="1">popt-1.7-uclibc.patch.gz</Patch>
|
||||
</Patches>
|
||||
<BuildDependencies>
|
||||
<Dependency versionFrom="1.8"> make </Dependency>
|
||||
</BuildDependencies>
|
||||
<History>
|
||||
<Update>
|
||||
<Date>06/14/2005</Date>
|
||||
<Version>1.7</Version>
|
||||
<Release>3</Release>
|
||||
</Update>
|
||||
<Update>
|
||||
<Date>06/10/2005</Date>
|
||||
<Version>1.7</Version>
|
||||
<Release>2</Release>
|
||||
</Update>
|
||||
<Update>
|
||||
<Date>05/05/2005</Date>
|
||||
<Version>1.7</Version>
|
||||
<Release>1</Release>
|
||||
</Update>
|
||||
</History>
|
||||
</Source>
|
||||
|
||||
<Package>
|
||||
<Name>popt-libs</Name>
|
||||
<Summary xml:lang="en">Command line option parsing library</Summary>
|
||||
<Summary xml:lang="tr">Komut satırı seçenekleri işleme kütüphanesi</Summary>
|
||||
<RuntimeDependencies>
|
||||
<Dependency>gettext</Dependency>
|
||||
</RuntimeDependencies>
|
||||
<Description>library files for popt</Description>
|
||||
<Files>
|
||||
<Path fileType="sharedLib">/usr/lib</Path>
|
||||
<Path fileType="doc">/usr/share/doc</Path>
|
||||
<Path fileType="doc">/usr/share/man</Path>
|
||||
<Path fileType="localedata">/usr/share/locale</Path>
|
||||
<Path fileType="header">/usr/include/popt.h</Path>
|
||||
</Files>
|
||||
<History>
|
||||
<Update>
|
||||
<Date>06/14/2005</Date>
|
||||
<Version>1.7</Version>
|
||||
<Release>2</Release>
|
||||
</Update>
|
||||
</History>
|
||||
</Package>
|
||||
</PISI>
|
||||
@@ -1,71 +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 unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append('.')
|
||||
sys.path.append('..')
|
||||
|
||||
runTestSuite = lambda(x): unittest.TextTestRunner(verbosity=2).run(x)
|
||||
|
||||
def run_all():
|
||||
|
||||
import utiltests
|
||||
import xmlfiletests
|
||||
import specfiletests
|
||||
import metadatatests
|
||||
import constantstests
|
||||
import fetchertests
|
||||
import archivetests
|
||||
import installdbtests
|
||||
import sourcedbtests
|
||||
import packagedbtests
|
||||
import actionsapitests
|
||||
import graphtests
|
||||
import versiontests
|
||||
import configfiletests
|
||||
import packagetests
|
||||
|
||||
alltests = unittest.TestSuite((
|
||||
utiltests.suite,
|
||||
xmlfiletests.suite,
|
||||
specfiletests.suite,
|
||||
metadatatests.suite,
|
||||
constantstests.suite,
|
||||
fetchertests.suite,
|
||||
archivetests.suite,
|
||||
installdbtests.suite,
|
||||
sourcedbtests.suite,
|
||||
packagedbtests.suite,
|
||||
# FIXME: actionsapitests requires tester to run a specific command first.
|
||||
# actionsapitests.suite,
|
||||
graphtests.suite,
|
||||
versiontests.suite,
|
||||
configfiletests.suite,
|
||||
packagetests.suite
|
||||
))
|
||||
|
||||
runTestSuite(alltests)
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv
|
||||
if len(args) > 1: # run modules given from the command line
|
||||
tests = sys.argv[1:]
|
||||
for test in tests:
|
||||
module = __import__(test + 'tests')
|
||||
print "\nRunning tests in '%s'...\n" % (test)
|
||||
runTestSuite(module.suite)
|
||||
else: # run all tests
|
||||
print "\nRunning all tests in order...\n"
|
||||
run_all()
|
||||
@@ -1,38 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.api
|
||||
import pisi.sourcedb
|
||||
from pisi import util
|
||||
from pisi.specfile import SpecFile
|
||||
|
||||
class SourceDBTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pisi.api.init()
|
||||
|
||||
self.sourcedb = pisi.sourcedb.init()
|
||||
self.spec = SpecFile()
|
||||
self.spec.read("tests/popt/pspec.xml")
|
||||
|
||||
def testAdd(self):
|
||||
self.sourcedb.add_source(self.spec.source)
|
||||
self.assert_(self.sourcedb.has_source("popt"))
|
||||
|
||||
def testRemove(self):
|
||||
self.testAdd()
|
||||
self.sourcedb.remove_source("popt")
|
||||
self.assert_(not self.sourcedb.has_source("popt"))
|
||||
|
||||
suite = unittest.makeSuite(SourceDBTestCase)
|
||||
@@ -1,80 +0,0 @@
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pisi import specfile
|
||||
from pisi.config import config
|
||||
import pisi.util as util
|
||||
|
||||
class SpecFileTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.spec = specfile.SpecFile()
|
||||
self.spec.read("tests/popt/pspec.xml")
|
||||
|
||||
def testReadSpec(self):
|
||||
self.assertEqual(self.spec.source.name, "popt")
|
||||
|
||||
self.assertEqual(self.spec.source.version, "1.7")
|
||||
|
||||
self.assertEqual(self.spec.source.release, "3")
|
||||
|
||||
self.assertEqual(self.spec.source.archiveSHA1,
|
||||
"66f3c77b87a160951b180447f4a6dce68ad2f71b")
|
||||
|
||||
patches = self.spec.source.patches
|
||||
self.assertEqual(len(patches), 1)
|
||||
patch = patches[0] #get first and the only patch
|
||||
self.assertEqual(patch.filename, "popt-1.7-uclibc.patch.gz")
|
||||
self.assertEqual(patch.compressionType, "gz")
|
||||
|
||||
packages = self.spec.packages
|
||||
self.assertEqual(len(packages), 1)
|
||||
package = packages[0] # get the first and the only package
|
||||
self.assertEqual(package.name, "popt-libs")
|
||||
|
||||
# search for a path in package.paths
|
||||
pn = "/usr/lib"
|
||||
matched = [p for p in package.paths if p.pathname == pn]
|
||||
if not matched:
|
||||
self.fail("Failed to match pathname: %s" %pn)
|
||||
|
||||
def testIsAPartOf(self):
|
||||
# test existence in Source
|
||||
if not "library:util:optparser" in self.spec.source.isa:
|
||||
self.fail("Failed to match IsA in Source")
|
||||
if not isinstance(self.spec.source.isa, list):
|
||||
self.fail("source.isa is not a list, but it must be...")
|
||||
|
||||
if "rpm:archive" != self.spec.source.partof:
|
||||
self.fail("Failed to match PartOf in Source")
|
||||
|
||||
# test existence in Package
|
||||
pkg = self.spec.packages[0]
|
||||
if not "library:util:optparser" in pkg.isa:
|
||||
self.fail("Failed to match IsA in Package")
|
||||
if not isinstance(pkg.isa, list):
|
||||
self.fail("source.isa is not a list, but it must be...")
|
||||
|
||||
if "rpm:archive" != pkg.partof:
|
||||
self.fail("Failed to match PartOf in Package")
|
||||
|
||||
def testVerify(self):
|
||||
if self.spec.has_errors():
|
||||
self.fail("Failed to verify specfile")
|
||||
|
||||
def testCopy(self):
|
||||
util.check_dir(config.tmp_dir())
|
||||
self.spec.read("tests/popt/pspec.xml")
|
||||
self.spec.write(os.path.join(config.tmp_dir(), 'popt-copy.pspec.xml'))
|
||||
|
||||
|
||||
suite = unittest.makeSuite(SpecFileTestCase)
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
from pisi.actionsapi import get
|
||||
from pisi.actionsapi import pisitools
|
||||
|
||||
def setup():
|
||||
pisitools.dosed("unix/Makefile", "-O3", get.CFLAGS())
|
||||
pisitools.dosed("unix/Makefile", "CC=gcc LD=gcc", "CC=${CC:-gcc} LD=${CC:-gcc}")
|
||||
pisitools.dosed("unix/Makefile", "-O ", get.CFLAGS())
|
||||
pass
|
||||
|
||||
def build():
|
||||
autotools.make("-f unix/Makefile linux")
|
||||
|
||||
def install():
|
||||
pisitools.insinto("/usr/bin/", "unzip")
|
||||
pisitools.insinto("/usr/bin/", "funzip")
|
||||
pisitools.insinto("/usr/bin/", "unzipsfx")
|
||||
pisitools.insinto("/usr/bin/", "unix/zipgrep")
|
||||
|
||||
pisitools.dosym("/usr/bin/unzip", "/usr/bin/zipinfo")
|
||||
pisitools.doman("man/*.1")
|
||||
pisitools.dodoc("BUGS", "History*", "README", "ToDo", "WHERE")
|
||||
@@ -1,85 +0,0 @@
|
||||
Only in unzip-5.50-lhh/: cscope.files
|
||||
Only in unzip-5.50-lhh/: cscope.out
|
||||
diff -ur unzip-5.50/unix/unix.c unzip-5.50-lhh/unix/unix.c
|
||||
--- unzip-5.50/unix/unix.c 2002-01-21 17:54:42.000000000 -0500
|
||||
+++ unzip-5.50-lhh/unix/unix.c 2003-06-11 18:35:38.000000000 -0400
|
||||
@@ -421,7 +421,8 @@
|
||||
*/
|
||||
{
|
||||
char pathcomp[FILNAMSIZ]; /* path-component buffer */
|
||||
- char *pp, *cp=(char *)NULL; /* character pointers */
|
||||
+ char *pp, *cp=(char *)NULL, /* character pointers */
|
||||
+ *dp=(char *)NULL;
|
||||
char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
|
||||
#ifdef ACORN_FTYPE_NFS
|
||||
char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */
|
||||
@@ -429,6 +430,7 @@
|
||||
#endif
|
||||
int quote = FALSE; /* flags */
|
||||
int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */
|
||||
+ int snarf_ddot = FALSE; /* Is set while scanning for "../" */
|
||||
int error = MPN_OK;
|
||||
register unsigned workch; /* hold the character being tested */
|
||||
|
||||
@@ -467,6 +469,9 @@
|
||||
while ((workch = (uch)*cp++) != 0) {
|
||||
|
||||
if (quote) { /* if character quoted, */
|
||||
+ if ((pp == pathcomp) && (workch == '.'))
|
||||
+ /* Oh no you don't... */
|
||||
+ goto ddot_hack;
|
||||
*pp++ = (char)workch; /* include it literally */
|
||||
quote = FALSE;
|
||||
} else
|
||||
@@ -481,15 +486,44 @@
|
||||
break;
|
||||
|
||||
case '.':
|
||||
- if (pp == pathcomp) { /* nothing appended yet... */
|
||||
+ if (pp == pathcomp) {
|
||||
+ddot_hack:
|
||||
+ /* nothing appended yet... */
|
||||
if (*cp == '/') { /* don't bother appending "./" to */
|
||||
++cp; /* the path: skip behind the '/' */
|
||||
break;
|
||||
- } else if (!uO.ddotflag && *cp == '.' && cp[1] == '/') {
|
||||
- /* "../" dir traversal detected */
|
||||
- cp += 2; /* skip over behind the '/' */
|
||||
- killed_ddot = TRUE; /* set "show message" flag */
|
||||
- break;
|
||||
+ } else if (!uO.ddotflag) {
|
||||
+
|
||||
+ /*
|
||||
+ * SECURITY: Skip past control characters if the user
|
||||
+ * didn't OK use of absolute pathnames. lhh - this is
|
||||
+ * a very quick, ugly, inefficient fix.
|
||||
+ */
|
||||
+ dp = cp;
|
||||
+ do {
|
||||
+ workch = (uch)(*dp);
|
||||
+ if (workch == '/' && snarf_ddot) {
|
||||
+ /* "../" dir traversal detected */
|
||||
+ cp = dp + 1; /* skip past the '/' */
|
||||
+ killed_ddot = TRUE; /* set "show msg" flag */
|
||||
+ break;
|
||||
+ } else if (workch == '.' && !snarf_ddot) {
|
||||
+ snarf_ddot = TRUE;
|
||||
+ } else if (isprint(workch) ||
|
||||
+ ((workch > 127) && (workch <= 254))) {
|
||||
+ /*
|
||||
+ * Since we found a printable, non-ctrl char,
|
||||
+ * we can stop looking for '../', the amount
|
||||
+ * in ../!
|
||||
+ */
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ dp++;
|
||||
+ } while (*dp != 0);
|
||||
+
|
||||
+ if (killed_ddot)
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
*pp++ = '.';
|
||||
Only in unzip-5.50-lhh/unix: .unix.c.swp
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user