From 22c29d8651668195f72e2f6a8e059d625eb511c3 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Wed, 20 Jan 2010 18:46:02 +0000 Subject: bitbake: Switch to bitbake-dev version (bitbake master upstream) Signed-off-by: Richard Purdie --- bitbake-dev/AUTHORS | 10 - bitbake-dev/COPYING | 339 ------- bitbake-dev/ChangeLog | 317 ------ bitbake-dev/bin/bitbake | 195 ---- bitbake-dev/bin/bitdoc | 534 ---------- bitbake-dev/lib/bb/COW.py | 318 ------ bitbake-dev/lib/bb/__init__.py | 1134 --------------------- bitbake-dev/lib/bb/build.py | 394 -------- bitbake-dev/lib/bb/cache.py | 533 ---------- bitbake-dev/lib/bb/command.py | 271 ----- bitbake-dev/lib/bb/cooker.py | 978 ------------------ bitbake-dev/lib/bb/daemonize.py | 191 ---- bitbake-dev/lib/bb/data.py | 562 ----------- bitbake-dev/lib/bb/data_smart.py | 289 ------ bitbake-dev/lib/bb/event.py | 275 ----- bitbake-dev/lib/bb/fetch/__init__.py | 640 ------------ bitbake-dev/lib/bb/fetch/bzr.py | 153 --- bitbake-dev/lib/bb/fetch/cvs.py | 182 ---- bitbake-dev/lib/bb/fetch/git.py | 216 ---- bitbake-dev/lib/bb/fetch/hg.py | 178 ---- bitbake-dev/lib/bb/fetch/local.py | 72 -- bitbake-dev/lib/bb/fetch/osc.py | 155 --- bitbake-dev/lib/bb/fetch/perforce.py | 214 ---- bitbake-dev/lib/bb/fetch/ssh.py | 118 --- bitbake-dev/lib/bb/fetch/svk.py | 109 -- bitbake-dev/lib/bb/fetch/svn.py | 206 ---- bitbake-dev/lib/bb/fetch/wget.py | 130 --- bitbake-dev/lib/bb/manifest.py | 144 --- bitbake-dev/lib/bb/methodpool.py | 84 -- bitbake-dev/lib/bb/msg.py | 125 --- bitbake-dev/lib/bb/parse/__init__.py | 84 -- bitbake-dev/lib/bb/parse/parse_py/BBHandler.py | 410 -------- bitbake-dev/lib/bb/parse/parse_py/ConfHandler.py | 241 ----- bitbake-dev/lib/bb/parse/parse_py/__init__.py | 33 - bitbake-dev/lib/bb/persist_data.py | 121 --- bitbake-dev/lib/bb/providers.py | 327 ------ bitbake-dev/lib/bb/runqueue.py | 1174 ---------------------- bitbake-dev/lib/bb/server/__init__.py | 2 - bitbake-dev/lib/bb/server/none.py | 181 ---- bitbake-dev/lib/bb/server/xmlrpc.py | 187 ---- bitbake-dev/lib/bb/shell.py | 824 --------------- bitbake-dev/lib/bb/taskdata.py | 610 ----------- bitbake-dev/lib/bb/ui/__init__.py | 18 - bitbake-dev/lib/bb/ui/crumbs/__init__.py | 18 - bitbake-dev/lib/bb/ui/crumbs/buildmanager.py | 457 --------- bitbake-dev/lib/bb/ui/crumbs/puccho.glade | 606 ----------- bitbake-dev/lib/bb/ui/crumbs/runningbuild.py | 180 ---- bitbake-dev/lib/bb/ui/depexp.py | 272 ----- bitbake-dev/lib/bb/ui/goggle.py | 77 -- bitbake-dev/lib/bb/ui/knotty.py | 162 --- bitbake-dev/lib/bb/ui/ncurses.py | 335 ------ bitbake-dev/lib/bb/ui/puccho.py | 425 -------- bitbake-dev/lib/bb/ui/uievent.py | 125 --- bitbake-dev/lib/bb/ui/uihelper.py | 49 - bitbake-dev/lib/bb/utils.py | 431 -------- bitbake/AUTHORS | 2 +- bitbake/ChangeLog | 217 ++-- bitbake/MANIFEST | 53 - bitbake/bin/bitbake | 97 +- bitbake/bin/bitdoc | 2 + bitbake/contrib/vim/syntax/bitbake.vim | 35 +- bitbake/doc/bitbake.1 | 4 +- bitbake/doc/manual/usermanual.xml | 28 +- bitbake/lib/bb/__init__.py | 3 +- bitbake/lib/bb/build.py | 226 +++-- bitbake/lib/bb/cache.py | 89 +- bitbake/lib/bb/command.py | 271 +++++ bitbake/lib/bb/cooker.py | 761 +++++++++----- bitbake/lib/bb/daemonize.py | 191 ++++ bitbake/lib/bb/data.py | 2 +- bitbake/lib/bb/event.py | 211 ++-- bitbake/lib/bb/fetch/__init__.py | 42 +- bitbake/lib/bb/fetch/cvs.py | 2 +- bitbake/lib/bb/fetch/git.py | 73 +- bitbake/lib/bb/fetch/local.py | 4 +- bitbake/lib/bb/fetch/svk.py | 2 +- bitbake/lib/bb/fetch/wget.py | 2 +- bitbake/lib/bb/msg.py | 26 +- bitbake/lib/bb/parse/parse_py/BBHandler.py | 32 +- bitbake/lib/bb/parse/parse_py/ConfHandler.py | 13 +- bitbake/lib/bb/providers.py | 4 +- bitbake/lib/bb/runqueue.py | 341 +++++-- bitbake/lib/bb/server/__init__.py | 2 + bitbake/lib/bb/server/none.py | 181 ++++ bitbake/lib/bb/server/xmlrpc.py | 187 ++++ bitbake/lib/bb/shell.py | 19 +- bitbake/lib/bb/taskdata.py | 38 +- bitbake/lib/bb/ui/__init__.py | 18 + bitbake/lib/bb/ui/crumbs/__init__.py | 18 + bitbake/lib/bb/ui/crumbs/buildmanager.py | 457 +++++++++ bitbake/lib/bb/ui/crumbs/puccho.glade | 606 +++++++++++ bitbake/lib/bb/ui/crumbs/runningbuild.py | 180 ++++ bitbake/lib/bb/ui/depexp.py | 272 +++++ bitbake/lib/bb/ui/goggle.py | 77 ++ bitbake/lib/bb/ui/knotty.py | 162 +++ bitbake/lib/bb/ui/ncurses.py | 335 ++++++ bitbake/lib/bb/ui/puccho.py | 425 ++++++++ bitbake/lib/bb/ui/uievent.py | 125 +++ bitbake/lib/bb/ui/uihelper.py | 49 + bitbake/lib/bb/utils.py | 20 +- 100 files changed, 4948 insertions(+), 17371 deletions(-) delete mode 100644 bitbake-dev/AUTHORS delete mode 100644 bitbake-dev/COPYING delete mode 100644 bitbake-dev/ChangeLog delete mode 100755 bitbake-dev/bin/bitbake delete mode 100755 bitbake-dev/bin/bitdoc delete mode 100644 bitbake-dev/lib/bb/COW.py delete mode 100644 bitbake-dev/lib/bb/__init__.py delete mode 100644 bitbake-dev/lib/bb/build.py delete mode 100644 bitbake-dev/lib/bb/cache.py delete mode 100644 bitbake-dev/lib/bb/command.py delete mode 100644 bitbake-dev/lib/bb/cooker.py delete mode 100644 bitbake-dev/lib/bb/daemonize.py delete mode 100644 bitbake-dev/lib/bb/data.py delete mode 100644 bitbake-dev/lib/bb/data_smart.py delete mode 100644 bitbake-dev/lib/bb/event.py delete mode 100644 bitbake-dev/lib/bb/fetch/__init__.py delete mode 100644 bitbake-dev/lib/bb/fetch/bzr.py delete mode 100644 bitbake-dev/lib/bb/fetch/cvs.py delete mode 100644 bitbake-dev/lib/bb/fetch/git.py delete mode 100644 bitbake-dev/lib/bb/fetch/hg.py delete mode 100644 bitbake-dev/lib/bb/fetch/local.py delete mode 100644 bitbake-dev/lib/bb/fetch/osc.py delete mode 100644 bitbake-dev/lib/bb/fetch/perforce.py delete mode 100644 bitbake-dev/lib/bb/fetch/ssh.py delete mode 100644 bitbake-dev/lib/bb/fetch/svk.py delete mode 100644 bitbake-dev/lib/bb/fetch/svn.py delete mode 100644 bitbake-dev/lib/bb/fetch/wget.py delete mode 100644 bitbake-dev/lib/bb/manifest.py delete mode 100644 bitbake-dev/lib/bb/methodpool.py delete mode 100644 bitbake-dev/lib/bb/msg.py delete mode 100644 bitbake-dev/lib/bb/parse/__init__.py delete mode 100644 bitbake-dev/lib/bb/parse/parse_py/BBHandler.py delete mode 100644 bitbake-dev/lib/bb/parse/parse_py/ConfHandler.py delete mode 100644 bitbake-dev/lib/bb/parse/parse_py/__init__.py delete mode 100644 bitbake-dev/lib/bb/persist_data.py delete mode 100644 bitbake-dev/lib/bb/providers.py delete mode 100644 bitbake-dev/lib/bb/runqueue.py delete mode 100644 bitbake-dev/lib/bb/server/__init__.py delete mode 100644 bitbake-dev/lib/bb/server/none.py delete mode 100644 bitbake-dev/lib/bb/server/xmlrpc.py delete mode 100644 bitbake-dev/lib/bb/shell.py delete mode 100644 bitbake-dev/lib/bb/taskdata.py delete mode 100644 bitbake-dev/lib/bb/ui/__init__.py delete mode 100644 bitbake-dev/lib/bb/ui/crumbs/__init__.py delete mode 100644 bitbake-dev/lib/bb/ui/crumbs/buildmanager.py delete mode 100644 bitbake-dev/lib/bb/ui/crumbs/puccho.glade delete mode 100644 bitbake-dev/lib/bb/ui/crumbs/runningbuild.py delete mode 100644 bitbake-dev/lib/bb/ui/depexp.py delete mode 100644 bitbake-dev/lib/bb/ui/goggle.py delete mode 100644 bitbake-dev/lib/bb/ui/knotty.py delete mode 100644 bitbake-dev/lib/bb/ui/ncurses.py delete mode 100644 bitbake-dev/lib/bb/ui/puccho.py delete mode 100644 bitbake-dev/lib/bb/ui/uievent.py delete mode 100644 bitbake-dev/lib/bb/ui/uihelper.py delete mode 100644 bitbake-dev/lib/bb/utils.py delete mode 100644 bitbake/MANIFEST create mode 100644 bitbake/lib/bb/command.py create mode 100644 bitbake/lib/bb/daemonize.py create mode 100644 bitbake/lib/bb/server/__init__.py create mode 100644 bitbake/lib/bb/server/none.py create mode 100644 bitbake/lib/bb/server/xmlrpc.py create mode 100644 bitbake/lib/bb/ui/__init__.py create mode 100644 bitbake/lib/bb/ui/crumbs/__init__.py create mode 100644 bitbake/lib/bb/ui/crumbs/buildmanager.py create mode 100644 bitbake/lib/bb/ui/crumbs/puccho.glade create mode 100644 bitbake/lib/bb/ui/crumbs/runningbuild.py create mode 100644 bitbake/lib/bb/ui/depexp.py create mode 100644 bitbake/lib/bb/ui/goggle.py create mode 100644 bitbake/lib/bb/ui/knotty.py create mode 100644 bitbake/lib/bb/ui/ncurses.py create mode 100644 bitbake/lib/bb/ui/puccho.py create mode 100644 bitbake/lib/bb/ui/uievent.py create mode 100644 bitbake/lib/bb/ui/uihelper.py diff --git a/bitbake-dev/AUTHORS b/bitbake-dev/AUTHORS deleted file mode 100644 index a4014b1e3..000000000 --- a/bitbake-dev/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -Tim Ansell -Phil Blundell -Seb Frankengul -Holger Freyther -Marcin Juszkiewicz -Chris Larson -Ulrich Luckas -Mickey Lauer -Richard Purdie -Holger Schurig diff --git a/bitbake-dev/COPYING b/bitbake-dev/COPYING deleted file mode 100644 index d511905c1..000000000 --- a/bitbake-dev/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. - - , 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 Lesser General -Public License instead of this License. diff --git a/bitbake-dev/ChangeLog b/bitbake-dev/ChangeLog deleted file mode 100644 index 22124cb7e..000000000 --- a/bitbake-dev/ChangeLog +++ /dev/null @@ -1,317 +0,0 @@ -Changes in Bitbake 1.9.x: - - Add PE (Package Epoch) support from Philipp Zabel (pH5) - - Treat python functions the same as shell functions for logging - - Use TMPDIR/anonfunc as a __anonfunc temp directory (T) - - Catch truncated cache file errors - - Allow operations other than assignment on flag variables - - Add code to handle inter-task dependencies - - Fix cache errors when generation dotGraphs - - Make sure __inherit_cache is updated before calling include() (from Michael Krelin) - - Fix bug when target was in ASSUME_PROVIDED (#2236) - - Raise ParseError for filenames with multiple underscores instead of infinitely looping (#2062) - - Fix invalid regexp in BBMASK error handling (missing import) (#1124) - - Promote certain warnings from debug to note 2 level - - Update manual - - Correctly redirect stdin when forking - - If parsing errors are found, exit, too many users miss the errors - - Remove supriours PREFERRED_PROVIDER warnings - - svn fetcher: Add _buildsvncommand function - - Improve certain error messages - - Rewrite svn fetcher to make adding extra operations easier - as part of future SRCDATE="now" fixes - (requires new FETCHCMD_svn definition in bitbake.conf) - - Change SVNDIR layout to be more unique (fixes #2644 and #2624) - - Add ConfigParsed Event after configuration parsing is complete - - Add SRCREV support for svn fetcher - - data.emit_var() - only call getVar if we need the variable - - Stop generating the A variable (seems to be legacy code) - - Make sure intertask depends get processed correcting in recursive depends - - Add pn-PN to overrides when evaluating PREFERRED_VERSION - - Improve the progress indicator by skipping tasks that have - already run before starting the build rather than during it - - Add profiling option (-P) - - Add BB_SRCREV_POLICY variable (clear or cache) to control SRCREV cache - - Add SRCREV_FORMAT support - - Fix local fetcher's localpath return values - - Apply OVERRIDES before performing immediate expansions - - Allow the -b -e option combination to take regular expressions - - Fix handling of variables with expansion in the name using _append/_prepend - e.g. RRECOMMENDS_${PN}_append_xyz = "abc" - - Add plain message function to bb.msg - - Sort the list of providers before processing so dependency problems are - reproducible rather than effectively random - - Fix/improve bitbake -s output - - Add locking for fetchers so only one tries to fetch a given file at a given time - - Fix int(0)/None confusion in runqueue.py which causes random gaps in dependency chains - - Expand data in addtasks - - Print the list of missing DEPENDS,RDEPENDS for the "No buildable providers available for required...." - error message. - - Rework add_task to be more efficient (6% speedup, 7% number of function calls reduction) - - Sort digraph output to make builds more reproducible - - Split expandKeys into two for loops to benefit from the expand_cache (12% speedup) - - runqueue.py: Fix idepends handling to avoid dependency errors - - Clear the terminal TOSTOP flag if set (and warn the user) - - Fix regression from r653 and make SRCDATE/CVSDATE work for packages again - - Fix a bug in bb.decodeurl where http://some.where.com/somefile.tgz decoded to host="" (#1530) - - Warn about malformed PREFERRED_PROVIDERS (#1072) - - Add support for BB_NICE_LEVEL option (#1627) - - Psyco is used only on x86 as there is no support for other architectures. - - Sort initial providers list by default preference (#1145, #2024) - - Improve provider sorting so prefered versions have preference over latest versions (#768) - - Detect builds of tasks with overlapping providers and warn (will become a fatal error) (#1359) - - Add MULTI_PROVIDER_WHITELIST variable to allow known safe multiple providers to be listed - - Handle paths in svn fetcher module parameter - - Support the syntax "export VARIABLE" - - Add bzr fetcher - - Add support for cleaning directories before a task in the form: - do_taskname[cleandirs] = "dir" - - bzr fetcher tweaks from Robert Schuster (#2913) - - Add mercurial (hg) fetcher from Robert Schuster (#2913) - - Don't add duplicates to BBPATH - - Fix preferred_version return values (providers.py) - - Fix 'depends' flag splitting - - Fix unexport handling (#3135) - - Add bb.copyfile function similar to bb.movefile (and improve movefile error reporting) - - Allow multiple options for deptask flag - - Use git-fetch instead of git-pull removing any need for merges when - fetching (we don't care about the index). Fixes fetch errors. - - Add BB_GENERATE_MIRROR_TARBALLS option, set to 0 to make git fetches - faster at the expense of not creating mirror tarballs. - - SRCREV handling updates, improvements and fixes from Poky - - Add bb.utils.lockfile() and bb.utils.unlockfile() from Poky - - Add support for task selfstamp and lockfiles flags - - Disable task number acceleration since it can allow the tasks to run - out of sequence - - Improve runqueue code comments - - Add task scheduler abstraction and some example schedulers - - Improve circular dependency chain debugging code and user feedback - - Don't give a stacktrace for invalid tasks, have a user friendly message (#3431) - - Add support for "-e target" (#3432) - - Fix shell showdata command (#3259) - - Fix shell data updating problems (#1880) - - Properly raise errors for invalid source URI protocols - - Change the wget fetcher failure handling to avoid lockfile problems - - Add support for branches in git fetcher (Otavio Salvador, Michael Lauer) - - Make taskdata and runqueue errors more user friendly - - Add norecurse and fullpath options to cvs fetcher - - Fix exit code for build failures in --continue mode - - Fix git branch tags fetching - - Change parseConfigurationFile so it works on real data, not a copy - - Handle 'base' inherit and all other INHERITs from parseConfigurationFile - instead of BBHandler - - Fix getVarFlags bug in data_smart - - Optmise cache handling by more quickly detecting an invalid cache, only - saving the cache when its changed, moving the cache validity check into - the parsing loop and factoring some getVar calls outside a for loop - - Cooker: Remove a debug message from the parsing loop to lower overhead - - Convert build.py exec_task to use getVarFlags - - Update shell to use cooker.buildFile - - Add StampUpdate event - - Convert -b option to use taskdata/runqueue - - Remove digraph and switch to new stamp checking code. exec_task no longer - honours dependencies - - Make fetcher timestamp updating non-fatal when permissions don't allow - updates - - Add BB_SCHEDULER variable/option ("completion" or "speed") controlling - the way bitbake schedules tasks - - Add BB_STAMP_POLICY variable/option ("perfile" or "full") controlling - how extensively stamps are looked at for validity - - When handling build target failures make sure idepends are checked and - failed where needed. Fixes --continue mode crashes. - - Fix -f (force) in conjunction with -b - - Fix problems with recrdeptask handling where some idepends weren't handled - correctly. - - Handle exit codes correctly (from pH5) - - Work around refs/HEAD issues with git over http (#3410) - - Add proxy support to the CVS fetcher (from Cyril Chemparathy) - - Improve runfetchcmd so errors are seen and various GIT variables are exported - - Add ability to fetchers to check URL validity without downloading - - Improve runtime PREFERRED_PROVIDERS warning message - - Add BB_STAMP_WHITELIST option which contains a list of stamps to ignore when - checking stamp dependencies and using a BB_STAMP_POLICY of "whitelist" - - No longer weight providers on the basis of a package being "already staged". This - leads to builds being non-deterministic. - - Flush stdout/stderr before forking to fix duplicate console output - - Make sure recrdeps tasks include all inter-task dependencies of a given fn - - Add bb.runqueue.check_stamp_fn() for use by packaged-staging - - Add PERSISTENT_DIR to store the PersistData in a persistent - directory != the cache dir. - - Add md5 and sha256 checksum generation functions to utils.py - - Correctly handle '-' characters in class names (#2958) - - Make sure expandKeys has been called on the data dictonary before running tasks - - Correctly add a task override in the form task-TASKNAME. - - Revert the '-' character fix in class names since it breaks things - - When a regexp fails to compile for PACKAGES_DYNAMIC, print a more useful error (#4444) - - Allow to checkout CVS by Date and Time. Just add HHmm to the SRCDATE. - - Move prunedir function to utils.py and add explode_dep_versions function - - Raise an exception if SRCREV == 'INVALID' - - Fix hg fetcher username/password handling and fix crash - - Fix PACKAGES_DYNAMIC handling of packages with '++' in the name - - Rename __depends to __base_depends after configuration parsing so we don't - recheck the validity of the config files time after time - - Add better environmental variable handling. By default it will now only pass certain - whitelisted variables into the data store. If BB_PRESERVE_ENV is set bitbake will use - all variable from the environment. If BB_ENV_WHITELIST is set, that whitelist will be - used instead of the internal bitbake one. Alternatively, BB_ENV_EXTRAWHITE can be used - to extend the internal whitelist. - - Perforce fetcher fix to use commandline options instead of being overriden by the environment - - bb.utils.prunedir can cope with symlinks to directoriees without exceptions - - use @rev when doing a svn checkout - - Add osc fetcher (from Joshua Lock in Poky) - - When SRCREV autorevisioning for a recipe is in use, don't cache the recipe - - Add tryaltconfigs option to control whether bitbake trys using alternative providers - to fulfil failed dependencies. It defaults to off, changing the default since this - behaviour confuses many users and isn't often useful. - - Improve lock file function error handling - - Add username handling to the git fetcher (Robert Bragg) - - Add support for HTTP_PROXY and HTTP_PROXY_IGNORE variables to the wget fetcher - - Export more variables to the fetcher commands to allow ssh checkouts and checkouts through - proxies to work better. (from Poky) - - Also allow user and pswd options in SRC_URIs globally (from Poky) - - Improve proxy handling when using mirrors (from Poky) - - Add bb.utils.prune_suffix function - - Fix hg checkouts of specific revisions (from Poky) - - Fix wget fetching of urls with parameters specified (from Poky) - - Add username handling to git fetcher (from Poky) - - Set HOME environmental variable when running fetcher commands (from Poky) - - Make sure allowed variables inherited from the environment are exported again (from Poky) - - When running a stage task in bbshell, run populate_staging, not the stage task (from Poky) - - Fix + character escaping from PACKAGES_DYNAMIC (thanks Otavio Salvador) - - Addition of BBCLASSEXTEND support for allowing one recipe to provide multiple targets (from Poky) - -Changes in Bitbake 1.8.0: - - Release 1.7.x as a stable series - -Changes in BitBake 1.7.x: - - Major updates of the dependency handling and execution - of tasks. Code from bin/bitbake replaced with runqueue.py - and taskdata.py - - New task execution code supports multithreading with a simplistic - threading algorithm controlled by BB_NUMBER_THREADS - - Change of the SVN Fetcher to keep the checkout around - courtsey of Paul Sokolovsky (#1367) - - PATH fix to bbimage (#1108) - - Allow debug domains to be specified on the commandline (-l) - - Allow 'interactive' tasks - - Logging message improvements - - Drop now uneeded BUILD_ALL_DEPS variable - - Add support for wildcards to -b option - - Major overhaul of the fetchers making a large amount of code common - including mirroring code - - Fetchers now touch md5 stamps upon access (to show activity) - - Fix -f force option when used without -b (long standing bug) - - Add expand_cache to data_cache.py, caching expanded data (speedup) - - Allow version field in DEPENDS (ignored for now) - - Add abort flag support to the shell - - Make inherit fail if the class doesn't exist (#1478) - - Fix data.emit_env() to expand keynames as well as values - - Add ssh fetcher - - Add perforce fetcher - - Make PREFERRED_PROVIDER_foobar defaults to foobar if available - - Share the parser's mtime_cache, reducing the number of stat syscalls - - Compile all anonfuncs at once! - *** Anonfuncs must now use common spacing format *** - - Memorise the list of handlers in __BBHANDLERS and tasks in __BBTASKS - This removes 2 million function calls resulting in a 5-10% speedup - - Add manpage - - Update generateDotGraph to use taskData/runQueue improving accuracy - and also adding a task dependency graph - - Fix/standardise on GPLv2 licence - - Move most functionality from bin/bitbake to cooker.py and split into - separate funcitons - - CVS fetcher: Added support for non-default port - - Add BBINCLUDELOGS_LINES, the number of lines to read from any logfile - - Drop shebangs from lib/bb scripts - -Changes in Bitbake 1.6.0: - - Better msg handling - - COW dict implementation from Tim Ansell (mithro) leading - to better performance - - Speed up of -s - -Changes in Bitbake 1.4.4: - - SRCDATE now handling courtsey Justin Patrin - - #1017 fix to work with rm_work - -Changes in BitBake 1.4.2: - - Send logs to oe.pastebin.com instead of pastebin.com - fixes #856 - - Copy the internal bitbake data before building the - dependency graph. This fixes nano not having a - virtual/libc dependency - - Allow multiple TARBALL_STASH entries - - Cache, check if the directory exists before changing - into it - - git speedup cloning by not doing a checkout - - allow to have spaces in filenames (.conf, .bb, .bbclass) - -Changes in BitBake 1.4.0: - - Fix to check both RDEPENDS and RDEPENDS_${PN} - - Fix a RDEPENDS parsing bug in utils:explode_deps() - - Update git fetcher behaviour to match git changes - - ASSUME_PROVIDED allowed to include runtime packages - - git fetcher cleanup and efficency improvements - - Change the format of the cache - - Update usermanual to document the Fetchers - - Major changes to caching with a new strategy - giving a major performance increase when reparsing - with few data changes - -Changes in BitBake 1.3.3: - - Create a new Fetcher module to ease the - development of new Fetchers. - Issue #438 fixed by rpurdie@openedhand.com - - Make the Subversion fetcher honor the SRC Date - (CVSDATE). - Issue #555 fixed by chris@openedhand.com - - Expand PREFERRED_PROVIDER properly - Issue #436 fixed by rprudie@openedhand.com - - Typo fix for Issue #531 by Philipp Zabel for the - BitBake Shell - - Introduce a new special variable SRCDATE as - a generic naming to replace CVSDATE. - - Introduce a new keyword 'required'. In contrast - to 'include' parsing will fail if a to be included - file can not be found. - - Remove hardcoding of the STAMP directory. Patch - courtsey pHilipp Zabel - - Track the RDEPENDS of each package (rpurdie@openedhand.com) - - Introduce BUILD_ALL_DEPS to build all RDEPENDS. E.g - this is used by the OpenEmbedded Meta Packages. - (rpurdie@openedhand.com). - -Changes in BitBake 1.3.2: - - reintegration of make.py into BitBake - - bbread is gone, use bitbake -e - - lots of shell updates and bugfixes - - Introduction of the .= and =. operator - - Sort variables, keys and groups in bitdoc - - Fix regression in the handling of BBCOLLECTIONS - - Update the bitbake usermanual - -Changes in BitBake 1.3.0: - - add bitbake interactive shell (bitbake -i) - - refactor bitbake utility in OO style - - kill default arguments in methods in the bb.data module - - kill default arguments in methods in the bb.fetch module - - the http/https/ftp fetcher will fail if the to be - downloaded file was not found in DL_DIR (this is needed - to avoid unpacking the sourceforge mirror page) - - Switch to a cow like data instance for persistent and non - persisting mode (called data_smart.py) - - Changed the callback of bb.make.collect_bbfiles to carry - additional parameters - - Drastically reduced the amount of needed RAM by not holding - each data instance in memory when using a cache/persistent - storage - -Changes in BitBake 1.2.1: - The 1.2.1 release is meant as a intermediate release to lay the - ground for more radical changes. The most notable changes are: - - - Do not hardcode {}, use bb.data.init() instead if you want to - get a instance of a data class - - bb.data.init() is a factory and the old bb.data methods are delegates - - Do not use deepcopy use bb.data.createCopy() instead. - - Removed default arguments in bb.fetch - diff --git a/bitbake-dev/bin/bitbake b/bitbake-dev/bin/bitbake deleted file mode 100755 index 23c9d73ee..000000000 --- a/bitbake-dev/bin/bitbake +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer -# Copyright (C) 2005 Holger Hans Peter Freyther -# Copyright (C) 2005 ROAD GmbH -# Copyright (C) 2006 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import sys, os, getopt, re, time, optparse, xmlrpclib -sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib')) -import bb -from bb import cooker -from bb import ui - - -__version__ = "1.9.0" - -if sys.hexversion < 0x020500F0: - print "Sorry, python 2.5 or later is required for this version of bitbake" - sys.exit(1) - -#============================================================================# -# BBOptions -#============================================================================# -class BBConfiguration( object ): - """ - Manages build options and configurations for one run - """ - def __init__( self, options ): - for key, val in options.__dict__.items(): - setattr( self, key, val ) - - -def print_exception(exc, value, tb): - """ - Print the exception to stderr, only showing the traceback if bitbake - debugging is enabled. - """ - if not bb.msg.debug_level['default']: - tb = None - - sys.__excepthook__(exc, value, tb) - - -#============================================================================# -# main -#============================================================================# - -def main(): - return_value = 0 - pythonver = sys.version_info - if pythonver[0] < 2 or (pythonver[0] == 2 and pythonver[1] < 5): - print "Sorry, bitbake needs python 2.5 or later." - sys.exit(1) - - parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ), - usage = """%prog [options] [package ...] - -Executes the specified task (default is 'build') for a given set of BitBake files. -It expects that BBFILES is defined, which is a space separated list of files to -be executed. BBFILES does support wildcards. -Default BBFILES are the .bb files in the current directory.""" ) - - parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.", - action = "store", dest = "buildfile", default = None ) - - parser.add_option( "-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.", - action = "store_false", dest = "abort", default = True ) - - parser.add_option( "-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.", - action = "store_true", dest = "tryaltconfigs", default = False ) - - parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status", - action = "store_true", dest = "force", default = False ) - - parser.add_option( "-i", "--interactive", help = "drop into the interactive mode also called the BitBake shell.", - action = "store_true", dest = "interactive", default = False ) - - parser.add_option( "-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks", - action = "store", dest = "cmd" ) - - parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf", - action = "append", dest = "file", default = [] ) - - parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal", - action = "store_true", dest = "verbose", default = False ) - - parser.add_option( "-D", "--debug", help = "Increase the debug level. You can specify this more than once.", - action = "count", dest="debug", default = 0) - - parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions", - action = "store_true", dest = "dry_run", default = False ) - - parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)", - action = "store_true", dest = "parse_only", default = False ) - - parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)", - action = "store_true", dest = "disable_psyco", default = False ) - - parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages", - action = "store_true", dest = "show_versions", default = False ) - - parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)", - action = "store_true", dest = "show_environment", default = False ) - - parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax", - action = "store_true", dest = "dot_graph", default = False ) - - parser.add_option( "-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""", - action = "append", dest = "extra_assume_provided", default = [] ) - - parser.add_option( "-l", "--log-domains", help = """Show debug logging for the specified logging domains""", - action = "append", dest = "debug_domains", default = [] ) - - parser.add_option( "-P", "--profile", help = "profile the command and print a report", - action = "store_true", dest = "profile", default = False ) - - parser.add_option( "-u", "--ui", help = "userinterface to use", - action = "store", dest = "ui") - - parser.add_option( "", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not", - action = "store_true", dest = "revisions_changed", default = False ) - - options, args = parser.parse_args(sys.argv) - - configuration = BBConfiguration(options) - configuration.pkgs_to_build = [] - configuration.pkgs_to_build.extend(args[1:]) - - #server = bb.server.xmlrpc - server = bb.server.none - - # Save a logfile for cooker into the current working directory. When the - # server is daemonized this logfile will be truncated. - cooker_logfile = os.path.join (os.getcwd(), "cooker.log") - - cooker = bb.cooker.BBCooker(configuration, server) - - # Clear away any spurious environment variables. But don't wipe the - # environment totally. This is necessary to ensure the correct operation - # of the UIs (e.g. for DISPLAY, etc.) - bb.utils.clean_environment() - - cooker.parseCommandLine() - - serverinfo = server.BitbakeServerInfo(cooker.server) - - server.BitBakeServerFork(serverinfo, cooker.serve, cooker_logfile) - del cooker - - sys.excepthook = print_exception - - # Setup a connection to the server (cooker) - serverConnection = server.BitBakeServerConnection(serverinfo) - - # Launch the UI - if configuration.ui: - ui = configuration.ui - else: - ui = "knotty" - - try: - # Dynamically load the UI based on the ui name. Although we - # suggest a fixed set this allows you to have flexibility in which - # ones are available. - exec "from bb.ui import " + ui - exec "return_value = " + ui + ".init(serverConnection.connection, serverConnection.events)" - except ImportError: - print "FATAL: Invalid user interface '%s' specified. " % ui - print "Valid interfaces are 'ncurses', 'depexp' or the default, 'knotty'." - except Exception, e: - print "FATAL: Unable to start to '%s' UI: %s." % (configuration.ui, e.message) - finally: - serverConnection.terminate() - return return_value - -if __name__ == "__main__": - ret = main() - sys.exit(ret) diff --git a/bitbake-dev/bin/bitdoc b/bitbake-dev/bin/bitdoc deleted file mode 100755 index 4940f660a..000000000 --- a/bitbake-dev/bin/bitdoc +++ /dev/null @@ -1,534 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2005 Holger Hans Peter Freyther -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import optparse, os, sys - -# bitbake -sys.path.append(os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib')) -import bb -import bb.parse -from string import split, join - -__version__ = "0.0.2" - -class HTMLFormatter: - """ - Simple class to help to generate some sort of HTML files. It is - quite inferior solution compared to docbook, gtkdoc, doxygen but it - should work for now. - We've a global introduction site (index.html) and then one site for - the list of keys (alphabetical sorted) and one for the list of groups, - one site for each key with links to the relations and groups. - - index.html - all_keys.html - all_groups.html - groupNAME.html - keyNAME.html - """ - - def replace(self, text, *pairs): - """ - From pydoc... almost identical at least - """ - while pairs: - (a,b) = pairs[0] - text = join(split(text, a), b) - pairs = pairs[1:] - return text - def escape(self, text): - """ - Escape string to be conform HTML - """ - return self.replace(text, - ('&', '&'), - ('<', '<' ), - ('>', '>' ) ) - def createNavigator(self): - """ - Create the navgiator - """ - return """ - - - - - -""" - - def relatedKeys(self, item): - """ - Create HTML to link to foreign keys - """ - - if len(item.related()) == 0: - return "" - - txt = "

See also:
" - txts = [] - for it in item.related(): - txts.append("""%(it)s""" % vars() ) - - return txt + ",".join(txts) - - def groups(self,item): - """ - Create HTML to link to related groups - """ - - if len(item.groups()) == 0: - return "" - - - txt = "

See also:
" - txts = [] - for group in item.groups(): - txts.append( """%s """ % (group,group) ) - - return txt + ",".join(txts) - - - def createKeySite(self,item): - """ - Create a site for a key. It contains the header/navigator, a heading, - the description, links to related keys and to the groups. - """ - - return """ -Key %s - - -%s -

%s

- -
-

Synopsis

-

-%s -

-
- -
-

Related Keys

-

-%s -

-
- -
-

Groups

-

-%s -

-
- - - -""" % (item.name(), self.createNavigator(), item.name(), - self.escape(item.description()), self.relatedKeys(item), self.groups(item)) - - def createGroupsSite(self, doc): - """ - Create the Group Overview site - """ - - groups = "" - sorted_groups = doc.groups() - sorted_groups.sort() - for group in sorted_groups: - groups += """%s
""" % (group, group) - - return """ -Group overview - - -%s -

Available Groups

-%s - -""" % (self.createNavigator(), groups) - - def createIndex(self): - """ - Create the index file - """ - - return """ -Bitbake Documentation - - -%s -

Documentation Entrance

-All available groups
-All available keys
- -""" % self.createNavigator() - - def createKeysSite(self, doc): - """ - Create Overview of all avilable keys - """ - keys = "" - sorted_keys = doc.doc_keys() - sorted_keys.sort() - for key in sorted_keys: - keys += """%s
""" % (key, key) - - return """ -Key overview - - -%s -

Available Keys

-%s - -""" % (self.createNavigator(), keys) - - def createGroupSite(self, gr, items, _description = None): - """ - Create a site for a group: - Group the name of the group, items contain the name of the keys - inside this group - """ - groups = "" - description = "" - - # create a section with the group descriptions - if _description: - description += "

" % gr - description += _description - - items.sort(lambda x,y:cmp(x.name(),y.name())) - for group in items: - groups += """%s
""" % (group.name(), group.name()) - - return """ -Group %s - - -%s -%s -
-

Keys in Group %s

-
-%s
-
-
- -""" % (gr, self.createNavigator(), description, gr, groups) - - - - def createCSS(self): - """ - Create the CSS file - """ - return """.synopsis, .classsynopsis -{ - background: #eeeeee; - border: solid 1px #aaaaaa; - padding: 0.5em; -} -.programlisting -{ - background: #eeeeff; - border: solid 1px #aaaaff; - padding: 0.5em; -} -.variablelist -{ - padding: 4px; - margin-left: 3em; -} -.variablelist td:first-child -{ - vertical-align: top; -} -table.navigation -{ - background: #ffeeee; - border: solid 1px #ffaaaa; - margin-top: 0.5em; - margin-bottom: 0.5em; -} -.navigation a -{ - color: #770000; -} -.navigation a:visited -{ - color: #550000; -} -.navigation .title -{ - font-size: 200%; -} -div.refnamediv -{ - margin-top: 2em; -} -div.gallery-float -{ - float: left; - padding: 10px; -} -div.gallery-float img -{ - border-style: none; -} -div.gallery-spacer -{ - clear: both; -} -a -{ - text-decoration: none; -} -a:hover -{ - text-decoration: underline; - color: #FF0000; -} -""" - - - -class DocumentationItem: - """ - A class to hold information about a configuration - item. It contains the key name, description, a list of related names, - and the group this item is contained in. - """ - - def __init__(self): - self._groups = [] - self._related = [] - self._name = "" - self._desc = "" - - def groups(self): - return self._groups - - def name(self): - return self._name - - def description(self): - return self._desc - - def related(self): - return self._related - - def setName(self, name): - self._name = name - - def setDescription(self, desc): - self._desc = desc - - def addGroup(self, group): - self._groups.append(group) - - def addRelation(self,relation): - self._related.append(relation) - - def sort(self): - self._related.sort() - self._groups.sort() - - -class Documentation: - """ - Holds the documentation... with mappings from key to items... - """ - - def __init__(self): - self.__keys = {} - self.__groups = {} - - def insert_doc_item(self, item): - """ - Insert the Doc Item into the internal list - of representation - """ - item.sort() - self.__keys[item.name()] = item - - for group in item.groups(): - if not group in self.__groups: - self.__groups[group] = [] - self.__groups[group].append(item) - self.__groups[group].sort() - - - def doc_item(self, key): - """ - Return the DocumentationInstance describing the key - """ - try: - return self.__keys[key] - except KeyError: - return None - - def doc_keys(self): - """ - Return the documented KEYS (names) - """ - return self.__keys.keys() - - def groups(self): - """ - Return the names of available groups - """ - return self.__groups.keys() - - def group_content(self,group_name): - """ - Return a list of keys/names that are in a specefic - group or the empty list - """ - try: - return self.__groups[group_name] - except KeyError: - return [] - - -def parse_cmdline(args): - """ - Parse the CMD line and return the result as a n-tuple - """ - - parser = optparse.OptionParser( version = "Bitbake Documentation Tool Core version %s, %%prog version %s" % (bb.__version__,__version__)) - usage = """%prog [options] - -Create a set of html pages (documentation) for a bitbake.conf.... -""" - - # Add the needed options - parser.add_option( "-c", "--config", help = "Use the specified configuration file as source", - action = "store", dest = "config", default = os.path.join("conf", "documentation.conf") ) - - parser.add_option( "-o", "--output", help = "Output directory for html files", - action = "store", dest = "output", default = "html/" ) - - parser.add_option( "-D", "--debug", help = "Increase the debug level", - action = "count", dest = "debug", default = 0 ) - - parser.add_option( "-v","--verbose", help = "output more chit-char to the terminal", - action = "store_true", dest = "verbose", default = False ) - - options, args = parser.parse_args( sys.argv ) - - if options.debug: - bb.msg.set_debug_level(options.debug) - - return options.config, options.output - -def main(): - """ - The main Method - """ - - (config_file,output_dir) = parse_cmdline( sys.argv ) - - # right to let us load the file now - try: - documentation = bb.parse.handle( config_file, bb.data.init() ) - except IOError: - bb.fatal( "Unable to open %s" % config_file ) - except bb.parse.ParseError: - bb.fatal( "Unable to parse %s" % config_file ) - - if isinstance(documentation, dict): - documentation = documentation[""] - - # Assuming we've the file loaded now, we will initialize the 'tree' - doc = Documentation() - - # defined states - state_begin = 0 - state_see = 1 - state_group = 2 - - for key in bb.data.keys(documentation): - data = bb.data.getVarFlag(key, "doc", documentation) - if not data: - continue - - # The Documentation now starts - doc_ins = DocumentationItem() - doc_ins.setName(key) - - - tokens = data.split(' ') - state = state_begin - string= "" - for token in tokens: - token = token.strip(',') - - if not state == state_see and token == "@see": - state = state_see - continue - elif not state == state_group and token == "@group": - state = state_group - continue - - if state == state_begin: - string += " %s" % token - elif state == state_see: - doc_ins.addRelation(token) - elif state == state_group: - doc_ins.addGroup(token) - - # set the description - doc_ins.setDescription(string) - doc.insert_doc_item(doc_ins) - - # let us create the HTML now - bb.mkdirhier(output_dir) - os.chdir(output_dir) - - # Let us create the sites now. We do it in the following order - # Start with the index.html. It will point to sites explaining all - # keys and groups - html_slave = HTMLFormatter() - - f = file('style.css', 'w') - print >> f, html_slave.createCSS() - - f = file('index.html', 'w') - print >> f, html_slave.createIndex() - - f = file('all_groups.html', 'w') - print >> f, html_slave.createGroupsSite(doc) - - f = file('all_keys.html', 'w') - print >> f, html_slave.createKeysSite(doc) - - # now for each group create the site - for group in doc.groups(): - f = file('group%s.html' % group, 'w') - print >> f, html_slave.createGroupSite(group, doc.group_content(group)) - - # now for the keys - for key in doc.doc_keys(): - f = file('key%s.html' % doc.doc_item(key).name(), 'w') - print >> f, html_slave.createKeySite(doc.doc_item(key)) - - -if __name__ == "__main__": - main() diff --git a/bitbake-dev/lib/bb/COW.py b/bitbake-dev/lib/bb/COW.py deleted file mode 100644 index ca206cf4b..000000000 --- a/bitbake-dev/lib/bb/COW.py +++ /dev/null @@ -1,318 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# This is a copy on write dictionary and set which abuses classes to try and be nice and fast. -# -# Copyright (C) 2006 Tim Amsell -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -#Please Note: -# Be careful when using mutable types (ie Dict and Lists) - operations involving these are SLOW. -# Assign a file to __warn__ to get warnings about slow operations. -# - -import copy -import types -types.ImmutableTypes = tuple([ \ - types.BooleanType, \ - types.ComplexType, \ - types.FloatType, \ - types.IntType, \ - types.LongType, \ - types.NoneType, \ - types.TupleType, \ - frozenset] + \ - list(types.StringTypes)) - -MUTABLE = "__mutable__" - -class COWMeta(type): - pass - -class COWDictMeta(COWMeta): - __warn__ = False - __hasmutable__ = False - __marker__ = tuple() - - def __str__(cls): - # FIXME: I have magic numbers! - return "" % (cls.__count__, len(cls.__dict__) - 3) - __repr__ = __str__ - - def cow(cls): - class C(cls): - __count__ = cls.__count__ + 1 - return C - copy = cow - __call__ = cow - - def __setitem__(cls, key, value): - if not isinstance(value, types.ImmutableTypes): - if not isinstance(value, COWMeta): - cls.__hasmutable__ = True - key += MUTABLE - setattr(cls, key, value) - - def __getmutable__(cls, key, readonly=False): - nkey = key + MUTABLE - try: - return cls.__dict__[nkey] - except KeyError: - pass - - value = getattr(cls, nkey) - if readonly: - return value - - if not cls.__warn__ is False and not isinstance(value, COWMeta): - print >> cls.__warn__, "Warning: Doing a copy because %s is a mutable type." % key - try: - value = value.copy() - except AttributeError, e: - value = copy.copy(value) - setattr(cls, nkey, value) - return value - - __getmarker__ = [] - def __getreadonly__(cls, key, default=__getmarker__): - """\ - Get a value (even if mutable) which you promise not to change. - """ - return cls.__getitem__(key, default, True) - - def __getitem__(cls, key, default=__getmarker__, readonly=False): - try: - try: - value = getattr(cls, key) - except AttributeError: - value = cls.__getmutable__(key, readonly) - - # This is for values which have been deleted - if value is cls.__marker__: - raise AttributeError("key %s does not exist." % key) - - return value - except AttributeError, e: - if not default is cls.__getmarker__: - return default - - raise KeyError(str(e)) - - def __delitem__(cls, key): - cls.__setitem__(key, cls.__marker__) - - def __revertitem__(cls, key): - if not cls.__dict__.has_key(key): - key += MUTABLE - delattr(cls, key) - - def has_key(cls, key): - value = cls.__getreadonly__(key, cls.__marker__) - if value is cls.__marker__: - return False - return True - - def iter(cls, type, readonly=False): - for key in dir(cls): - if key.startswith("__"): - continue - - if key.endswith(MUTABLE): - key = key[:-len(MUTABLE)] - - if type == "keys": - yield key - - try: - if readonly: - value = cls.__getreadonly__(key) - else: - value = cls[key] - except KeyError: - continue - - if type == "values": - yield value - if type == "items": - yield (key, value) - raise StopIteration() - - def iterkeys(cls): - return cls.iter("keys") - def itervalues(cls, readonly=False): - if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False: - print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True." - return cls.iter("values", readonly) - def iteritems(cls, readonly=False): - if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False: - print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True." - return cls.iter("items", readonly) - -class COWSetMeta(COWDictMeta): - def __str__(cls): - # FIXME: I have magic numbers! - return "" % (cls.__count__, len(cls.__dict__) -3) - __repr__ = __str__ - - def cow(cls): - class C(cls): - __count__ = cls.__count__ + 1 - return C - - def add(cls, value): - COWDictMeta.__setitem__(cls, repr(hash(value)), value) - - def remove(cls, value): - COWDictMeta.__delitem__(cls, repr(hash(value))) - - def __in__(cls, value): - return COWDictMeta.has_key(repr(hash(value))) - - def iterkeys(cls): - raise TypeError("sets don't have keys") - - def iteritems(cls): - raise TypeError("sets don't have 'items'") - -# These are the actual classes you use! -class COWDictBase(object): - __metaclass__ = COWDictMeta - __count__ = 0 - -class COWSetBase(object): - __metaclass__ = COWSetMeta - __count__ = 0 - -if __name__ == "__main__": - import sys - COWDictBase.__warn__ = sys.stderr - a = COWDictBase() - print "a", a - - a['a'] = 'a' - a['b'] = 'b' - a['dict'] = {} - - b = a.copy() - print "b", b - b['c'] = 'b' - - print - - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(): - print x - print - - b['dict']['a'] = 'b' - b['a'] = 'c' - - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(): - print x - print - - try: - b['dict2'] - except KeyError, e: - print "Okay!" - - a['set'] = COWSetBase() - a['set'].add("o1") - a['set'].add("o1") - a['set'].add("o2") - - print "a", a - for x in a['set'].itervalues(): - print x - print "--" - print "b", b - for x in b['set'].itervalues(): - print x - print - - b['set'].add('o3') - - print "a", a - for x in a['set'].itervalues(): - print x - print "--" - print "b", b - for x in b['set'].itervalues(): - print x - print - - a['set2'] = set() - a['set2'].add("o1") - a['set2'].add("o1") - a['set2'].add("o2") - - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(readonly=True): - print x - print - - del b['b'] - try: - print b['b'] - except KeyError: - print "Yay! deleted key raises error" - - if b.has_key('b'): - print "Boo!" - else: - print "Yay - has_key with delete works!" - - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(readonly=True): - print x - print - - b.__revertitem__('b') - - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(readonly=True): - print x - print - - b.__revertitem__('dict') - print "a", a - for x in a.iteritems(): - print x - print "--" - print "b", b - for x in b.iteritems(readonly=True): - print x - print diff --git a/bitbake-dev/lib/bb/__init__.py b/bitbake-dev/lib/bb/__init__.py deleted file mode 100644 index f2f8f656d..000000000 --- a/bitbake-dev/lib/bb/__init__.py +++ /dev/null @@ -1,1134 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# BitBake Build System Python Library -# -# Copyright (C) 2003 Holger Schurig -# Copyright (C) 2003, 2004 Chris Larson -# -# Based on Gentoo's portage.py. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -__version__ = "1.9.0" - -__all__ = [ - - "debug", - "note", - "error", - "fatal", - - "mkdirhier", - "movefile", - - "tokenize", - "evaluate", - "flatten", - "relparse", - "ververify", - "isjustname", - "isspecific", - "pkgsplit", - "catpkgsplit", - "vercmp", - "pkgcmp", - "dep_parenreduce", - "dep_opconvert", - -# fetch - "decodeurl", - "encodeurl", - -# modules - "parse", - "data", - "command", - "event", - "build", - "fetch", - "manifest", - "methodpool", - "cache", - "runqueue", - "taskdata", - "providers", - ] - -whitespace = '\t\n\x0b\x0c\r ' -lowercase = 'abcdefghijklmnopqrstuvwxyz' - -import sys, os, types, re, string, bb -from bb import msg - -#projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) -projectdir = os.getcwd() - -if "BBDEBUG" in os.environ: - level = int(os.environ["BBDEBUG"]) - if level: - bb.msg.set_debug_level(level) - -class VarExpandError(Exception): - pass - -class MalformedUrl(Exception): - """Exception raised when encountering an invalid url""" - - -####################################################################### -####################################################################### -# -# SECTION: Debug -# -# PURPOSE: little functions to make yourself known -# -####################################################################### -####################################################################### - -def plain(*args): - bb.msg.warn(''.join(args)) - -def debug(lvl, *args): - bb.msg.debug(lvl, None, ''.join(args)) - -def note(*args): - bb.msg.note(1, None, ''.join(args)) - -def warn(*args): - bb.msg.warn(1, None, ''.join(args)) - -def error(*args): - bb.msg.error(None, ''.join(args)) - -def fatal(*args): - bb.msg.fatal(None, ''.join(args)) - - -####################################################################### -####################################################################### -# -# SECTION: File -# -# PURPOSE: Basic file and directory tree related functions -# -####################################################################### -####################################################################### - -def mkdirhier(dir): - """Create a directory like 'mkdir -p', but does not complain if - directory already exists like os.makedirs - """ - - debug(3, "mkdirhier(%s)" % dir) - try: - os.makedirs(dir) - debug(2, "created " + dir) - except OSError, e: - if e.errno != 17: raise e - - -####################################################################### - -import stat - -def movefile(src,dest,newmtime=None,sstat=None): - """Moves a file from src to dest, preserving all permissions and - attributes; mtime will be preserved even when moving across - filesystems. Returns true on success and false on failure. Move is - atomic. - """ - - #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")" - try: - if not sstat: - sstat=os.lstat(src) - except Exception, e: - print "movefile: Stating source file failed...", e - return None - - destexists=1 - try: - dstat=os.lstat(dest) - except: - dstat=os.lstat(os.path.dirname(dest)) - destexists=0 - - if destexists: - if stat.S_ISLNK(dstat[stat.ST_MODE]): - try: - os.unlink(dest) - destexists=0 - except Exception, e: - pass - - if stat.S_ISLNK(sstat[stat.ST_MODE]): - try: - target=os.readlink(src) - if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): - os.unlink(dest) - os.symlink(target,dest) - #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) - os.unlink(src) - return os.lstat(dest) - except Exception, e: - print "movefile: failed to properly create symlink:", dest, "->", target, e - return None - - renamefailed=1 - if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]: - try: - ret=os.rename(src,dest) - renamefailed=0 - except Exception, e: - import errno - if e[0]!=errno.EXDEV: - # Some random error. - print "movefile: Failed to move", src, "to", dest, e - return None - # Invalid cross-device-link 'bind' mounted or actually Cross-Device - - if renamefailed: - didcopy=0 - if stat.S_ISREG(sstat[stat.ST_MODE]): - try: # For safety copy then move it over. - shutil.copyfile(src,dest+"#new") - os.rename(dest+"#new",dest) - didcopy=1 - except Exception, e: - print 'movefile: copy', src, '->', dest, 'failed.', e - return None - else: - #we don't yet handle special, so we need to fall back to /bin/mv - a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'") - if a[0]!=0: - print "movefile: Failed to move special file:" + src + "' to '" + dest + "'", a - return None # failure - try: - if didcopy: - missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) - os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown - os.unlink(src) - except Exception, e: - print "movefile: Failed to chown/chmod/unlink", dest, e - return None - - if newmtime: - os.utime(dest,(newmtime,newmtime)) - else: - os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) - newmtime=sstat[stat.ST_MTIME] - return newmtime - -def copyfile(src,dest,newmtime=None,sstat=None): - """ - Copies a file from src to dest, preserving all permissions and - attributes; mtime will be preserved even when moving across - filesystems. Returns true on success and false on failure. - """ - import os, stat, shutil - - #print "copyfile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")" - try: - if not sstat: - sstat=os.lstat(src) - except Exception, e: - print "copyfile: Stating source file failed...", e - return False - - destexists=1 - try: - dstat=os.lstat(dest) - except: - dstat=os.lstat(os.path.dirname(dest)) - destexists=0 - - if destexists: - if stat.S_ISLNK(dstat[stat.ST_MODE]): - try: - os.unlink(dest) - destexists=0 - except Exception, e: - pass - - if stat.S_ISLNK(sstat[stat.ST_MODE]): - try: - target=os.readlink(src) - if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): - os.unlink(dest) - os.symlink(target,dest) - #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) - return os.lstat(dest) - except Exception, e: - print "copyfile: failed to properly create symlink:", dest, "->", target, e - return False - - if stat.S_ISREG(sstat[stat.ST_MODE]): - try: # For safety copy then move it over. - shutil.copyfile(src,dest+"#new") - os.rename(dest+"#new",dest) - except Exception, e: - print 'copyfile: copy', src, '->', dest, 'failed.', e - return False - else: - #we don't yet handle special, so we need to fall back to /bin/mv - a=getstatusoutput("/bin/cp -f "+"'"+src+"' '"+dest+"'") - if a[0]!=0: - print "copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a - return False # failure - try: - os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) - os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown - except Exception, e: - print "copyfile: Failed to chown/chmod/unlink", dest, e - return False - - if newmtime: - os.utime(dest,(newmtime,newmtime)) - else: - os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) - newmtime=sstat[stat.ST_MTIME] - return newmtime - -####################################################################### -####################################################################### -# -# SECTION: Download -# -# PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures -# and mirrors -# -####################################################################### -####################################################################### - -def decodeurl(url): - """Decodes an URL into the tokens (scheme, network location, path, - user, password, parameters). - - >>> decodeurl("http://www.google.com/index.html") - ('http', 'www.google.com', '/index.html', '', '', {}) - - CVS url with username, host and cvsroot. The cvs module to check out is in the - parameters: - - >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg") - ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}) - - Dito, but this time the username has a password part. And we also request a special tag - to check out. - - >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81") - ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}) - """ - - m = re.compile('(?P[^:]*)://((?P.+)@)?(?P[^;]+)(;(?P.*))?').match(url) - if not m: - raise MalformedUrl(url) - - type = m.group('type') - location = m.group('location') - if not location: - raise MalformedUrl(url) - user = m.group('user') - parm = m.group('parm') - - locidx = location.find('/') - if locidx != -1: - host = location[:locidx] - path = location[locidx:] - else: - host = "" - path = location - if user: - m = re.compile('(?P[^:]+)(:?(?P.*))').match(user) - if m: - user = m.group('user') - pswd = m.group('pswd') - else: - user = '' - pswd = '' - - p = {} - if parm: - for s in parm.split(';'): - s1,s2 = s.split('=') - p[s1] = s2 - - return (type, host, path, user, pswd, p) - -####################################################################### - -def encodeurl(decoded): - """Encodes a URL from tokens (scheme, network location, path, - user, password, parameters). - - >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}]) - 'http://www.google.com/index.html' - - CVS with username, host and cvsroot. The cvs module to check out is in the - parameters: - - >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}]) - 'cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg' - - Dito, but this time the username has a password part. And we also request a special tag - to check out. - - >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}]) - 'cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg' - """ - - (type, host, path, user, pswd, p) = decoded - - if not type or not path: - fatal("invalid or missing parameters for url encoding") - url = '%s://' % type - if user: - url += "%s" % user - if pswd: - url += ":%s" % pswd - url += "@" - if host: - url += "%s" % host - url += "%s" % path - if p: - for parm in p.keys(): - url += ";%s=%s" % (parm, p[parm]) - - return url - -####################################################################### - -def which(path, item, direction = 0): - """ - Locate a file in a PATH - """ - - paths = (path or "").split(':') - if direction != 0: - paths.reverse() - - for p in (path or "").split(':'): - next = os.path.join(p, item) - if os.path.exists(next): - return next - - return "" - -####################################################################### - - - - -####################################################################### -####################################################################### -# -# SECTION: Dependency -# -# PURPOSE: Compare build & run dependencies -# -####################################################################### -####################################################################### - -def tokenize(mystring): - """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists: - - >>> tokenize("x") - ['x'] - >>> tokenize("x y") - ['x', 'y'] - >>> tokenize("(x y)") - [['x', 'y']] - >>> tokenize("(x y) b c") - [['x', 'y'], 'b', 'c'] - >>> tokenize("foo? (bar) oni? (blah (blah))") - ['foo?', ['bar'], 'oni?', ['blah', ['blah']]] - >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)") - ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']] - """ - - newtokens = [] - curlist = newtokens - prevlists = [] - level = 0 - accum = "" - for x in mystring: - if x=="(": - if accum: - curlist.append(accum) - accum="" - prevlists.append(curlist) - curlist=[] - level=level+1 - elif x==")": - if accum: - curlist.append(accum) - accum="" - if level==0: - print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'" - return None - newlist=curlist - curlist=prevlists.pop() - curlist.append(newlist) - level=level-1 - elif x in whitespace: - if accum: - curlist.append(accum) - accum="" - else: - accum=accum+x - if accum: - curlist.append(accum) - if (level!=0): - print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'" - return None - return newtokens - - -####################################################################### - -def evaluate(tokens,mydefines,allon=0): - """Removes tokens based on whether conditional definitions exist or not. - Recognizes ! - - >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}) - ['sys-apps/linux-headers'] - - Negate the flag: - - >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {}) - ['sys-apps/linux-headers', ['sys-devel/gettext']] - - Define 'nls': - - >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1}) - ['sys-apps/linux-headers', ['sys-devel/gettext']] - - Turn allon on: - - >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True) - ['sys-apps/linux-headers', ['sys-devel/gettext']] - """ - - if tokens == None: - return None - mytokens = tokens + [] # this copies the list - pos = 0 - while pos < len(mytokens): - if type(mytokens[pos]) == types.ListType: - evaluate(mytokens[pos], mydefines) - if not len(mytokens[pos]): - del mytokens[pos] - continue - elif mytokens[pos][-1] == "?": - cur = mytokens[pos][:-1] - del mytokens[pos] - if allon: - if cur[0] == "!": - del mytokens[pos] - else: - if cur[0] == "!": - if (cur[1:] in mydefines) and (pos < len(mytokens)): - del mytokens[pos] - continue - elif (cur not in mydefines) and (pos < len(mytokens)): - del mytokens[pos] - continue - pos = pos + 1 - return mytokens - - -####################################################################### - -def flatten(mytokens): - """Converts nested arrays into a flat arrays: - - >>> flatten([1,[2,3]]) - [1, 2, 3] - >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']]) - ['sys-apps/linux-headers', 'sys-devel/gettext'] - """ - - newlist=[] - for x in mytokens: - if type(x)==types.ListType: - newlist.extend(flatten(x)) - else: - newlist.append(x) - return newlist - - -####################################################################### - -_package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered -_package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list - -def relparse(myver): - """Parses the last elements of a version number into a triplet, that can - later be compared: - - >>> relparse('1.2_pre3') - [1.2, -2, 3.0] - >>> relparse('1.2b') - [1.2, 98, 0] - >>> relparse('1.2') - [1.2, 0, 0] - """ - - number = 0 - p1 = 0 - p2 = 0 - mynewver = myver.split('_') - if len(mynewver)==2: - # an _package_weights_ - number = float(mynewver[0]) - match = 0 - for x in _package_ends_: - elen = len(x) - if mynewver[1][:elen] == x: - match = 1 - p1 = _package_weights_[x] - try: - p2 = float(mynewver[1][elen:]) - except: - p2 = 0 - break - if not match: - # normal number or number with letter at end - divider = len(myver)-1 - if myver[divider:] not in "1234567890": - # letter at end - p1 = ord(myver[divider:]) - number = float(myver[0:divider]) - else: - number = float(myver) - else: - # normal number or number with letter at end - divider = len(myver)-1 - if myver[divider:] not in "1234567890": - #letter at end - p1 = ord(myver[divider:]) - number = float(myver[0:divider]) - else: - number = float(myver) - return [number,p1,p2] - - -####################################################################### - -__ververify_cache__ = {} - -def ververify(myorigval,silent=1): - """Returns 1 if given a valid version string, els 0. Valid versions are in the format - - ....[a-z,_{_package_weights_}[vy]] - - >>> ververify('2.4.20') - 1 - >>> ververify('2.4..20') # two dots - 0 - >>> ververify('2.x.20') # 'x' is not numeric - 0 - >>> ververify('2.4.20a') - 1 - >>> ververify('2.4.20cvs') # only one trailing letter - 0 - >>> ververify('1a') - 1 - >>> ververify('test_a') # no version at all - 0 - >>> ververify('2.4.20_beta1') - 1 - >>> ververify('2.4.20_beta') - 1 - >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer - 0 - """ - - # Lookup the cache first - try: - return __ververify_cache__[myorigval] - except KeyError: - pass - - if len(myorigval) == 0: - if not silent: - error("package version is empty") - __ververify_cache__[myorigval] = 0 - return 0 - myval = myorigval.split('.') - if len(myval)==0: - if not silent: - error("package name has empty version string") - __ververify_cache__[myorigval] = 0 - return 0 - # all but the last version must be a numeric - for x in myval[:-1]: - if not len(x): - if not silent: - error("package version has two points in a row") - __ververify_cache__[myorigval] = 0 - return 0 - try: - foo = int(x) - except: - if not silent: - error("package version contains non-numeric '"+x+"'") - __ververify_cache__[myorigval] = 0 - return 0 - if not len(myval[-1]): - if not silent: - error("package version has trailing dot") - __ververify_cache__[myorigval] = 0 - return 0 - try: - foo = int(myval[-1]) - __ververify_cache__[myorigval] = 1 - return 1 - except: - pass - - # ok, our last component is not a plain number or blank, let's continue - if myval[-1][-1] in lowercase: - try: - foo = int(myval[-1][:-1]) - return 1 - __ververify_cache__[myorigval] = 1 - # 1a, 2.0b, etc. - except: - pass - # ok, maybe we have a 1_alpha or 1_beta2; let's see - ep=string.split(myval[-1],"_") - if len(ep)!= 2: - if not silent: - error("package version has more than one letter at then end") - __ververify_cache__[myorigval] = 0 - return 0 - try: - foo = string.atoi(ep[0]) - except: - # this needs to be numeric, i.e. the "1" in "1_alpha" - if not silent: - error("package version must have numeric part before the '_'") - __ververify_cache__[myorigval] = 0 - return 0 - - for mye in _package_ends_: - if ep[1][0:len(mye)] == mye: - if len(mye) == len(ep[1]): - # no trailing numeric is ok - __ververify_cache__[myorigval] = 1 - return 1 - else: - try: - foo = string.atoi(ep[1][len(mye):]) - __ververify_cache__[myorigval] = 1 - return 1 - except: - # if no _package_weights_ work, *then* we return 0 - pass - if not silent: - error("package version extension after '_' is invalid") - __ververify_cache__[myorigval] = 0 - return 0 - - -def isjustname(mypkg): - myparts = string.split(mypkg,'-') - for x in myparts: - if ververify(x): - return 0 - return 1 - - -_isspecific_cache_={} - -def isspecific(mypkg): - "now supports packages with no category" - try: - return __isspecific_cache__[mypkg] - except: - pass - - mysplit = string.split(mypkg,"/") - if not isjustname(mysplit[-1]): - __isspecific_cache__[mypkg] = 1 - return 1 - __isspecific_cache__[mypkg] = 0 - return 0 - - -####################################################################### - -__pkgsplit_cache__={} - -def pkgsplit(mypkg, silent=1): - - """This function can be used as a package verification function. If - it is a valid name, pkgsplit will return a list containing: - [pkgname, pkgversion(norev), pkgrev ]. - - >>> pkgsplit('') - >>> pkgsplit('x') - >>> pkgsplit('x-') - >>> pkgsplit('-1') - >>> pkgsplit('glibc-1.2-8.9-r7') - >>> pkgsplit('glibc-2.2.5-r7') - ['glibc', '2.2.5', 'r7'] - >>> pkgsplit('foo-1.2-1') - >>> pkgsplit('Mesa-3.0') - ['Mesa', '3.0', 'r0'] - """ - - try: - return __pkgsplit_cache__[mypkg] - except KeyError: - pass - - myparts = string.split(mypkg,'-') - if len(myparts) < 2: - if not silent: - error("package name without name or version part") - __pkgsplit_cache__[mypkg] = None - return None - for x in myparts: - if len(x) == 0: - if not silent: - error("package name with empty name or version part") - __pkgsplit_cache__[mypkg] = None - return None - # verify rev - revok = 0 - myrev = myparts[-1] - ververify(myrev, silent) - if len(myrev) and myrev[0] == "r": - try: - string.atoi(myrev[1:]) - revok = 1 - except: - pass - if revok: - if ververify(myparts[-2]): - if len(myparts) == 2: - __pkgsplit_cache__[mypkg] = None - return None - else: - for x in myparts[:-2]: - if ververify(x): - __pkgsplit_cache__[mypkg]=None - return None - # names can't have versiony looking parts - myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]] - __pkgsplit_cache__[mypkg]=myval - return myval - else: - __pkgsplit_cache__[mypkg] = None - return None - - elif ververify(myparts[-1],silent): - if len(myparts)==1: - if not silent: - print "!!! Name error in",mypkg+": missing name part." - __pkgsplit_cache__[mypkg]=None - return None - else: - for x in myparts[:-1]: - if ververify(x): - if not silent: error("package name has multiple version parts") - __pkgsplit_cache__[mypkg] = None - return None - myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"] - __pkgsplit_cache__[mypkg] = myval - return myval - else: - __pkgsplit_cache__[mypkg] = None - return None - - -####################################################################### - -__catpkgsplit_cache__ = {} - -def catpkgsplit(mydata,silent=1): - """returns [cat, pkgname, version, rev ] - - >>> catpkgsplit('sys-libs/glibc-1.2-r7') - ['sys-libs', 'glibc', '1.2', 'r7'] - >>> catpkgsplit('glibc-1.2-r7') - [None, 'glibc', '1.2', 'r7'] - """ - - try: - return __catpkgsplit_cache__[mydata] - except KeyError: - pass - - cat = os.path.basename(os.path.dirname(mydata)) - mydata = os.path.join(cat, os.path.basename(mydata)) - if mydata[-3:] == '.bb': - mydata = mydata[:-3] - - mysplit = mydata.split("/") - p_split = None - splitlen = len(mysplit) - if splitlen == 1: - retval = [None] - p_split = pkgsplit(mydata,silent) - else: - retval = [mysplit[splitlen - 2]] - p_split = pkgsplit(mysplit[splitlen - 1],silent) - if not p_split: - __catpkgsplit_cache__[mydata] = None - return None - retval.extend(p_split) - __catpkgsplit_cache__[mydata] = retval - return retval - - -####################################################################### - -__vercmp_cache__ = {} - -def vercmp(val1,val2): - """This takes two version strings and returns an integer to tell you whether - the versions are the same, val1>val2 or val2>val1. - - >>> vercmp('1', '2') - -1.0 - >>> vercmp('2', '1') - 1.0 - >>> vercmp('1', '1.0') - 0 - >>> vercmp('1', '1.1') - -1.0 - >>> vercmp('1.1', '1_p2') - 1.0 - """ - - # quick short-circuit - if val1 == val2: - return 0 - valkey = val1+" "+val2 - - # cache lookup - try: - return __vercmp_cache__[valkey] - try: - return - __vercmp_cache__[val2+" "+val1] - except KeyError: - pass - except KeyError: - pass - - # consider 1_p2 vc 1.1 - # after expansion will become (1_p2,0) vc (1,1) - # then 1_p2 is compared with 1 before 0 is compared with 1 - # to solve the bug we need to convert it to (1,0_p2) - # by splitting _prepart part and adding it back _after_expansion - - val1_prepart = val2_prepart = '' - if val1.count('_'): - val1, val1_prepart = val1.split('_', 1) - if val2.count('_'): - val2, val2_prepart = val2.split('_', 1) - - # replace '-' by '.' - # FIXME: Is it needed? can val1/2 contain '-'? - - val1 = string.split(val1,'-') - if len(val1) == 2: - val1[0] = val1[0] +"."+ val1[1] - val2 = string.split(val2,'-') - if len(val2) == 2: - val2[0] = val2[0] +"."+ val2[1] - - val1 = string.split(val1[0],'.') - val2 = string.split(val2[0],'.') - - # add back decimal point so that .03 does not become "3" ! - for x in range(1,len(val1)): - if val1[x][0] == '0' : - val1[x] = '.' + val1[x] - for x in range(1,len(val2)): - if val2[x][0] == '0' : - val2[x] = '.' + val2[x] - - # extend varion numbers - if len(val2) < len(val1): - val2.extend(["0"]*(len(val1)-len(val2))) - elif len(val1) < len(val2): - val1.extend(["0"]*(len(val2)-len(val1))) - - # add back _prepart tails - if val1_prepart: - val1[-1] += '_' + val1_prepart - if val2_prepart: - val2[-1] += '_' + val2_prepart - # The above code will extend version numbers out so they - # have the same number of digits. - for x in range(0,len(val1)): - cmp1 = relparse(val1[x]) - cmp2 = relparse(val2[x]) - for y in range(0,3): - myret = cmp1[y] - cmp2[y] - if myret != 0: - __vercmp_cache__[valkey] = myret - return myret - __vercmp_cache__[valkey] = 0 - return 0 - - -####################################################################### - -def pkgcmp(pkg1,pkg2): - """ Compares two packages, which should have been split via - pkgsplit(). if the return value val is less than zero, then pkg2 is - newer than pkg1, zero if equal and positive if older. - - >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7']) - 0 - >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7']) - -1 - >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2']) - 1 - """ - - mycmp = vercmp(pkg1[1],pkg2[1]) - if mycmp > 0: - return 1 - if mycmp < 0: - return -1 - r1=string.atoi(pkg1[2][1:]) - r2=string.atoi(pkg2[2][1:]) - if r1 > r2: - return 1 - if r2 > r1: - return -1 - return 0 - - -####################################################################### - -def dep_parenreduce(mysplit, mypos=0): - """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists: - - >>> dep_parenreduce(['']) - [''] - >>> dep_parenreduce(['1', '2', '3']) - ['1', '2', '3'] - >>> dep_parenreduce(['1', '(', '2', '3', ')', '4']) - ['1', ['2', '3'], '4'] - """ - - while mypos < len(mysplit): - if mysplit[mypos] == "(": - firstpos = mypos - mypos = mypos + 1 - while mypos < len(mysplit): - if mysplit[mypos] == ")": - mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]] - mypos = firstpos - break - elif mysplit[mypos] == "(": - # recurse - mysplit = dep_parenreduce(mysplit,mypos) - mypos = mypos + 1 - mypos = mypos + 1 - return mysplit - - -def dep_opconvert(mysplit, myuse): - "Does dependency operator conversion" - - mypos = 0 - newsplit = [] - while mypos < len(mysplit): - if type(mysplit[mypos]) == types.ListType: - newsplit.append(dep_opconvert(mysplit[mypos],myuse)) - mypos += 1 - elif mysplit[mypos] == ")": - # mismatched paren, error - return None - elif mysplit[mypos]=="||": - if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType): - # || must be followed by paren'd list - return None - try: - mynew = dep_opconvert(mysplit[mypos+1],myuse) - except Exception, e: - error("unable to satisfy OR dependancy: " + string.join(mysplit," || ")) - raise e - mynew[0:0] = ["||"] - newsplit.append(mynew) - mypos += 2 - elif mysplit[mypos][-1] == "?": - # use clause, i.e "gnome? ( foo bar )" - # this is a quick and dirty hack so that repoman can enable all USE vars: - if (len(myuse) == 1) and (myuse[0] == "*"): - # enable it even if it's ! (for repoman) but kill it if it's - # an arch variable that isn't for this arch. XXX Sparc64? - if (mysplit[mypos][:-1] not in settings.usemask) or \ - (mysplit[mypos][:-1]==settings["ARCH"]): - enabled=1 - else: - enabled=0 - else: - if mysplit[mypos][0] == "!": - myusevar = mysplit[mypos][1:-1] - enabled = not myusevar in myuse - #if myusevar in myuse: - # enabled = 0 - #else: - # enabled = 1 - else: - myusevar=mysplit[mypos][:-1] - enabled = myusevar in myuse - #if myusevar in myuse: - # enabled=1 - #else: - # enabled=0 - if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"): - # colon mode - if enabled: - # choose the first option - if type(mysplit[mypos+1]) == types.ListType: - newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) - else: - newsplit.append(mysplit[mypos+1]) - else: - # choose the alternate option - if type(mysplit[mypos+1]) == types.ListType: - newsplit.append(dep_opconvert(mysplit[mypos+3],myuse)) - else: - newsplit.append(mysplit[mypos+3]) - mypos += 4 - else: - # normal use mode - if enabled: - if type(mysplit[mypos+1]) == types.ListType: - newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) - else: - newsplit.append(mysplit[mypos+1]) - # otherwise, continue - mypos += 2 - else: - # normal item - newsplit.append(mysplit[mypos]) - mypos += 1 - return newsplit - -if __name__ == "__main__": - import doctest, bb - bb.msg.set_debug_level(0) - doctest.testmod(bb) diff --git a/bitbake-dev/lib/bb/build.py b/bitbake-dev/lib/bb/build.py deleted file mode 100644 index 6d80b4b54..000000000 --- a/bitbake-dev/lib/bb/build.py +++ /dev/null @@ -1,394 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# BitBake 'Build' implementation -# -# Core code for function execution and task handling in the -# BitBake build tools. -# -# Copyright (C) 2003, 2004 Chris Larson -# -# Based on Gentoo's portage.py. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -#Based on functions from the base bb module, Copyright 2003 Holger Schurig - -from bb import data, event, mkdirhier, utils -import bb, os, sys - -# When we execute a python function we'd like certain things -# in all namespaces, hence we add them to __builtins__ -# If we do not do this and use the exec globals, they will -# not be available to subfunctions. -__builtins__['bb'] = bb -__builtins__['os'] = os - -# events -class FuncFailed(Exception): - """ - Executed function failed - First parameter a message - Second paramter is a logfile (optional) - """ - -class EventException(Exception): - """Exception which is associated with an Event.""" - - def __init__(self, msg, event): - self.args = msg, event - -class TaskBase(event.Event): - """Base class for task events""" - - def __init__(self, t, d ): - self._task = t - self._package = bb.data.getVar("PF", d, 1) - event.Event.__init__(self) - self._message = "package %s: task %s: %s" % (bb.data.getVar("PF", d, 1), t, bb.event.getName(self)[4:]) - - def getTask(self): - return self._task - - def setTask(self, task): - self._task = task - - task = property(getTask, setTask, None, "task property") - -class TaskStarted(TaskBase): - """Task execution started""" - -class TaskSucceeded(TaskBase): - """Task execution completed""" - -class TaskFailed(TaskBase): - """Task execution failed""" - def __init__(self, msg, logfile, t, d ): - self.logfile = logfile - self.msg = msg - TaskBase.__init__(self, t, d) - -class InvalidTask(TaskBase): - """Invalid Task""" - -# functions - -def exec_func(func, d, dirs = None): - """Execute an BB 'function'""" - - body = data.getVar(func, d) - if not body: - return - - flags = data.getVarFlags(func, d) - for item in ['deps', 'check', 'interactive', 'python', 'cleandirs', 'dirs', 'lockfiles', 'fakeroot']: - if not item in flags: - flags[item] = None - - ispython = flags['python'] - - cleandirs = (data.expand(flags['cleandirs'], d) or "").split() - for cdir in cleandirs: - os.system("rm -rf %s" % cdir) - - if dirs: - dirs = data.expand(dirs, d) - else: - dirs = (data.expand(flags['dirs'], d) or "").split() - for adir in dirs: - mkdirhier(adir) - - if len(dirs) > 0: - adir = dirs[-1] - else: - adir = data.getVar('B', d, 1) - - # Save current directory - try: - prevdir = os.getcwd() - except OSError: - prevdir = data.getVar('TOPDIR', d, True) - - # Setup logfiles - t = data.getVar('T', d, 1) - if not t: - bb.msg.fatal(bb.msg.domain.Build, "T not set") - mkdirhier(t) - # Gross hack, FIXME - import random - logfile = "%s/log.%s.%s.%s" % (t, func, str(os.getpid()),random.random()) - runfile = "%s/run.%s.%s" % (t, func, str(os.getpid())) - - # Change to correct directory (if specified) - if adir and os.access(adir, os.F_OK): - os.chdir(adir) - - # Handle logfiles - si = file('/dev/null', 'r') - try: - if bb.msg.debug_level['default'] > 0 or ispython: - so = os.popen("tee \"%s\"" % logfile, "w") - else: - so = file(logfile, 'w') - except OSError, e: - bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e) - pass - - se = so - - # Dup the existing fds so we dont lose them - osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()] - oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()] - ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()] - - # Replace those fds with our own - os.dup2(si.fileno(), osi[1]) - os.dup2(so.fileno(), oso[1]) - os.dup2(se.fileno(), ose[1]) - - locks = [] - lockfiles = (data.expand(flags['lockfiles'], d) or "").split() - for lock in lockfiles: - locks.append(bb.utils.lockfile(lock)) - - try: - # Run the function - if ispython: - exec_func_python(func, d, runfile, logfile) - else: - exec_func_shell(func, d, runfile, logfile, flags) - - # Restore original directory - try: - os.chdir(prevdir) - except: - pass - - finally: - - # Unlock any lockfiles - for lock in locks: - bb.utils.unlockfile(lock) - - # Restore the backup fds - os.dup2(osi[0], osi[1]) - os.dup2(oso[0], oso[1]) - os.dup2(ose[0], ose[1]) - - # Close our logs - si.close() - so.close() - se.close() - - if os.path.exists(logfile) and os.path.getsize(logfile) == 0: - bb.msg.debug(2, bb.msg.domain.Build, "Zero size logfile %s, removing" % logfile) - os.remove(logfile) - - # Close the backup fds - os.close(osi[0]) - os.close(oso[0]) - os.close(ose[0]) - -def exec_func_python(func, d, runfile, logfile): - """Execute a python BB 'function'""" - import re, os - - bbfile = bb.data.getVar('FILE', d, 1) - tmp = "def " + func + "():\n%s" % data.getVar(func, d) - tmp += '\n' + func + '()' - - f = open(runfile, "w") - f.write(tmp) - comp = utils.better_compile(tmp, func, bbfile) - g = {} # globals - g['d'] = d - try: - utils.better_exec(comp, g, tmp, bbfile) - except: - (t,value,tb) = sys.exc_info() - - if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: - raise - bb.msg.error(bb.msg.domain.Build, "Function %s failed" % func) - raise FuncFailed("function %s failed" % func, logfile) - -def exec_func_shell(func, d, runfile, logfile, flags): - """Execute a shell BB 'function' Returns true if execution was successful. - - For this, it creates a bash shell script in the tmp dectory, writes the local - data into it and finally executes. The output of the shell will end in a log file and stdout. - - Note on directory behavior. The 'dirs' varflag should contain a list - of the directories you need created prior to execution. The last - item in the list is where we will chdir/cd to. - """ - - deps = flags['deps'] - check = flags['check'] - if check in globals(): - if globals()[check](func, deps): - return - - f = open(runfile, "w") - f.write("#!/bin/sh -e\n") - if bb.msg.debug_level['default'] > 0: f.write("set -x\n") - data.emit_env(f, d) - - f.write("cd %s\n" % os.getcwd()) - if func: f.write("%s\n" % func) - f.close() - os.chmod(runfile, 0775) - if not func: - bb.msg.error(bb.msg.domain.Build, "Function not specified") - raise FuncFailed("Function not specified for exec_func_shell") - - # execute function - if flags['fakeroot']: - maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1) - else: - maybe_fakeroot = '' - lang_environment = "LC_ALL=C " - ret = os.system('%s%ssh -e %s' % (lang_environment, maybe_fakeroot, runfile)) - - if ret == 0: - return - - bb.msg.error(bb.msg.domain.Build, "Function %s failed" % func) - raise FuncFailed("function %s failed" % func, logfile) - - -def exec_task(task, d): - """Execute an BB 'task' - - The primary difference between executing a task versus executing - a function is that a task exists in the task digraph, and therefore - has dependencies amongst other tasks.""" - - # Check whther this is a valid task - if not data.getVarFlag(task, 'task', d): - raise EventException("No such task", InvalidTask(task, d)) - - try: - bb.msg.debug(1, bb.msg.domain.Build, "Executing task %s" % task) - old_overrides = data.getVar('OVERRIDES', d, 0) - localdata = data.createCopy(d) - data.setVar('OVERRIDES', 'task-%s:%s' % (task[3:], old_overrides), localdata) - data.update_data(localdata) - data.expandKeys(localdata) - event.fire(TaskStarted(task, localdata), localdata) - exec_func(task, localdata) - event.fire(TaskSucceeded(task, localdata), localdata) - except FuncFailed, message: - # Try to extract the optional logfile - try: - (msg, logfile) = message - except: - logfile = None - msg = message - bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % message ) - failedevent = TaskFailed(msg, logfile, task, d) - event.fire(failedevent, d) - raise EventException("Function failed in task: %s" % message, failedevent) - - # make stamp, or cause event and raise exception - if not data.getVarFlag(task, 'nostamp', d) and not data.getVarFlag(task, 'selfstamp', d): - make_stamp(task, d) - -def extract_stamp(d, fn): - """ - Extracts stamp format which is either a data dictonary (fn unset) - or a dataCache entry (fn set). - """ - if fn: - return d.stamp[fn] - return data.getVar('STAMP', d, 1) - -def stamp_internal(task, d, file_name): - """ - Internal stamp helper function - Removes any stamp for the given task - Makes sure the stamp directory exists - Returns the stamp path+filename - """ - stamp = extract_stamp(d, file_name) - if not stamp: - return - stamp = "%s.%s" % (stamp, task) - mkdirhier(os.path.dirname(stamp)) - # Remove the file and recreate to force timestamp - # change on broken NFS filesystems - if os.access(stamp, os.F_OK): - os.remove(stamp) - return stamp - -def make_stamp(task, d, file_name = None): - """ - Creates/updates a stamp for a given task - (d can be a data dict or dataCache) - """ - stamp = stamp_internal(task, d, file_name) - if stamp: - f = open(stamp, "w") - f.close() - -def del_stamp(task, d, file_name = None): - """ - Removes a stamp for a given task - (d can be a data dict or dataCache) - """ - stamp_internal(task, d, file_name) - -def add_tasks(tasklist, d): - task_deps = data.getVar('_task_deps', d) - if not task_deps: - task_deps = {} - if not 'tasks' in task_deps: - task_deps['tasks'] = [] - if not 'parents' in task_deps: - task_deps['parents'] = {} - - for task in tasklist: - task = data.expand(task, d) - data.setVarFlag(task, 'task', 1, d) - - if not task in task_deps['tasks']: - task_deps['tasks'].append(task) - - flags = data.getVarFlags(task, d) - def getTask(name): - if not name in task_deps: - task_deps[name] = {} - if name in flags: - deptask = data.expand(flags[name], d) - task_deps[name][task] = deptask - getTask('depends') - getTask('deptask') - getTask('rdeptask') - getTask('recrdeptask') - getTask('nostamp') - task_deps['parents'][task] = [] - for dep in flags['deps']: - dep = data.expand(dep, d) - task_deps['parents'][task].append(dep) - - # don't assume holding a reference - data.setVar('_task_deps', task_deps, d) - -def remove_task(task, kill, d): - """Remove an BB 'task'. - - If kill is 1, also remove tasks that depend on this task.""" - - data.delVarFlag(task, 'task', d) - diff --git a/bitbake-dev/lib/bb/cache.py b/bitbake-dev/lib/bb/cache.py deleted file mode 100644 index 2f1b8fa60..000000000 --- a/bitbake-dev/lib/bb/cache.py +++ /dev/null @@ -1,533 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# BitBake 'Event' implementation -# -# Caching of bitbake variables before task execution - -# Copyright (C) 2006 Richard Purdie - -# but small sections based on code from bin/bitbake: -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer -# Copyright (C) 2005 Holger Hans Peter Freyther -# Copyright (C) 2005 ROAD GmbH -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import os, re -import bb.data -import bb.utils - -try: - import cPickle as pickle -except ImportError: - import pickle - bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.") - -__cache_version__ = "130" - -class Cache: - """ - BitBake Cache implementation - """ - def __init__(self, cooker): - - - self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True) - self.clean = {} - self.checked = {} - self.depends_cache = {} - self.data = None - self.data_fn = None - self.cacheclean = True - - if self.cachedir in [None, '']: - self.has_cache = False - bb.msg.note(1, bb.msg.domain.Cache, "Not using a cache. Set CACHE = to enable.") - return - - self.has_cache = True - self.cachefile = os.path.join(self.cachedir,"bb_cache.dat") - - bb.msg.debug(1, bb.msg.domain.Cache, "Using cache in '%s'" % self.cachedir) - try: - os.stat( self.cachedir ) - except OSError: - bb.mkdirhier( self.cachedir ) - - # If any of configuration.data's dependencies are newer than the - # cache there isn't even any point in loading it... - newest_mtime = 0 - deps = bb.data.getVar("__depends", cooker.configuration.data, True) - for f,old_mtime in deps: - if old_mtime > newest_mtime: - newest_mtime = old_mtime - - if bb.parse.cached_mtime_noerror(self.cachefile) >= newest_mtime: - try: - p = pickle.Unpickler(file(self.cachefile, "rb")) - self.depends_cache, version_data = p.load() - if version_data['CACHE_VER'] != __cache_version__: - raise ValueError, 'Cache Version Mismatch' - if version_data['BITBAKE_VER'] != bb.__version__: - raise ValueError, 'Bitbake Version Mismatch' - except EOFError: - bb.msg.note(1, bb.msg.domain.Cache, "Truncated cache found, rebuilding...") - self.depends_cache = {} - except: - bb.msg.note(1, bb.msg.domain.Cache, "Invalid cache found, rebuilding...") - self.depends_cache = {} - else: - try: - os.stat( self.cachefile ) - bb.msg.note(1, bb.msg.domain.Cache, "Out of date cache found, rebuilding...") - except OSError: - pass - - def getVar(self, var, fn, exp = 0): - """ - Gets the value of a variable - (similar to getVar in the data class) - - There are two scenarios: - 1. We have cached data - serve from depends_cache[fn] - 2. We're learning what data to cache - serve from data - backend but add a copy of the data to the cache. - """ - if fn in self.clean: - return self.depends_cache[fn][var] - - if not fn in self.depends_cache: - self.depends_cache[fn] = {} - - if fn != self.data_fn: - # We're trying to access data in the cache which doesn't exist - # yet setData hasn't been called to setup the right access. Very bad. - bb.msg.error(bb.msg.domain.Cache, "Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn)) - - self.cacheclean = False - result = bb.data.getVar(var, self.data, exp) - self.depends_cache[fn][var] = result - return result - - def setData(self, virtualfn, fn, data): - """ - Called to prime bb_cache ready to learn which variables to cache. - Will be followed by calls to self.getVar which aren't cached - but can be fulfilled from self.data. - """ - self.data_fn = virtualfn - self.data = data - - # Make sure __depends makes the depends_cache - # If we're a virtual class we need to make sure all our depends are appended - # to the depends of fn. - depends = self.getVar("__depends", virtualfn, True) or [] - if "__depends" not in self.depends_cache[fn] or not self.depends_cache[fn]["__depends"]: - self.depends_cache[fn]["__depends"] = depends - for dep in depends: - if dep not in self.depends_cache[fn]["__depends"]: - self.depends_cache[fn]["__depends"].append(dep) - - # Make sure BBCLASSEXTEND always makes the cache too - self.getVar('BBCLASSEXTEND', virtualfn, True) - - self.depends_cache[virtualfn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn) - - def virtualfn2realfn(self, virtualfn): - """ - Convert a virtual file name to a real one + the associated subclass keyword - """ - - fn = virtualfn - cls = "" - if virtualfn.startswith('virtual:'): - cls = virtualfn.split(':', 2)[1] - fn = virtualfn.replace('virtual:' + cls + ':', '') - #bb.msg.debug(2, bb.msg.domain.Cache, "virtualfn2realfn %s to %s %s" % (virtualfn, fn, cls)) - return (fn, cls) - - def realfn2virtual(self, realfn, cls): - """ - Convert a real filename + the associated subclass keyword to a virtual filename - """ - if cls == "": - #bb.msg.debug(2, bb.msg.domain.Cache, "realfn2virtual %s and '%s' to %s" % (realfn, cls, realfn)) - return realfn - #bb.msg.debug(2, bb.msg.domain.Cache, "realfn2virtual %s and %s to %s" % (realfn, cls, "virtual:" + cls + ":" + realfn)) - return "virtual:" + cls + ":" + realfn - - def loadDataFull(self, virtualfn, cfgData): - """ - Return a complete set of data for fn. - To do this, we need to parse the file. - """ - - (fn, cls) = self.virtualfn2realfn(virtualfn) - - bb.msg.debug(1, bb.msg.domain.Cache, "Parsing %s (full)" % fn) - - bb_data = self.load_bbfile(fn, cfgData) - return bb_data[cls] - - def loadData(self, fn, cfgData, cacheData): - """ - Load a subset of data for fn. - If the cached data is valid we do nothing, - To do this, we need to parse the file and set the system - to record the variables accessed. - Return the cache status and whether the file was skipped when parsed - """ - skipped = 0 - virtuals = 0 - - if fn not in self.checked: - self.cacheValidUpdate(fn) - - if self.cacheValid(fn): - multi = self.getVar('BBCLASSEXTEND', fn, True) - for cls in (multi or "").split() + [""]: - virtualfn = self.realfn2virtual(fn, cls) - if self.depends_cache[virtualfn]["__SKIPPED"]: - skipped += 1 - bb.msg.debug(1, bb.msg.domain.Cache, "Skipping %s" % virtualfn) - continue - self.handle_data(virtualfn, cacheData) - virtuals += 1 - return True, skipped, virtuals - - bb.msg.debug(1, bb.msg.domain.Cache, "Parsing %s" % fn) - - bb_data = self.load_bbfile(fn, cfgData) - - for data in bb_data: - virtualfn = self.realfn2virtual(fn, data) - self.setData(virtualfn, fn, bb_data[data]) - if self.getVar("__SKIPPED", virtualfn, True): - skipped += 1 - bb.msg.debug(1, bb.msg.domain.Cache, "Skipping %s" % virtualfn) - else: - self.handle_data(virtualfn, cacheData) - virtuals += 1 - return False, skipped, virtuals - - - def cacheValid(self, fn): - """ - Is the cache valid for fn? - Fast version, no timestamps checked. - """ - # Is cache enabled? - if not self.has_cache: - return False - if fn in self.clean: - return True - return False - - def cacheValidUpdate(self, fn): - """ - Is the cache valid for fn? - Make thorough (slower) checks including timestamps. - """ - # Is cache enabled? - if not self.has_cache: - return False - - self.checked[fn] = "" - - # Pretend we're clean so getVar works - self.clean[fn] = "" - - # File isn't in depends_cache - if not fn in self.depends_cache: - bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s is not cached" % fn) - self.remove(fn) - return False - - mtime = bb.parse.cached_mtime_noerror(fn) - - # Check file still exists - if mtime == 0: - bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s not longer exists" % fn) - self.remove(fn) - return False - - # Check the file's timestamp - if mtime != self.getVar("CACHETIMESTAMP", fn, True): - bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s changed" % fn) - self.remove(fn) - return False - - # Check dependencies are still valid - depends = self.getVar("__depends", fn, True) - if depends: - for f,old_mtime in depends: - fmtime = bb.parse.cached_mtime_noerror(f) - # Check if file still exists - if old_mtime != 0 and fmtime == 0: - self.remove(fn) - return False - - if (fmtime != old_mtime): - bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s's dependency %s changed" % (fn, f)) - self.remove(fn) - return False - - #bb.msg.debug(2, bb.msg.domain.Cache, "Depends Cache: %s is clean" % fn) - if not fn in self.clean: - self.clean[fn] = "" - - # Mark extended class data as clean too - multi = self.getVar('BBCLASSEXTEND', fn, True) - for cls in (multi or "").split(): - virtualfn = self.realfn2virtual(fn, cls) - self.clean[virtualfn] = "" - - return True - - def remove(self, fn): - """ - Remove a fn from the cache - Called from the parser in error cases - """ - bb.msg.debug(1, bb.msg.domain.Cache, "Removing %s from cache" % fn) - if fn in self.depends_cache: - del self.depends_cache[fn] - if fn in self.clean: - del self.clean[fn] - - def sync(self): - """ - Save the cache - Called from the parser when complete (or exiting) - """ - import copy - - if not self.has_cache: - return - - if self.cacheclean: - bb.msg.note(1, bb.msg.domain.Cache, "Cache is clean, not saving.") - return - - version_data = {} - version_data['CACHE_VER'] = __cache_version__ - version_data['BITBAKE_VER'] = bb.__version__ - - cache_data = copy.deepcopy(self.depends_cache) - for fn in self.depends_cache: - if '__BB_DONT_CACHE' in self.depends_cache[fn] and self.depends_cache[fn]['__BB_DONT_CACHE']: - bb.msg.debug(2, bb.msg.domain.Cache, "Not caching %s, marked as not cacheable" % fn) - del cache_data[fn] - elif 'PV' in self.depends_cache[fn] and 'SRCREVINACTION' in self.depends_cache[fn]['PV']: - bb.msg.error(bb.msg.domain.Cache, "Not caching %s as it had SRCREVINACTION in PV. Please report this bug" % fn) - del cache_data[fn] - - p = pickle.Pickler(file(self.cachefile, "wb" ), -1 ) - p.dump([cache_data, version_data]) - - def mtime(self, cachefile): - return bb.parse.cached_mtime_noerror(cachefile) - - def handle_data(self, file_name, cacheData): - """ - Save data we need into the cache - """ - - pn = self.getVar('PN', file_name, True) - pe = self.getVar('PE', file_name, True) or "0" - pv = self.getVar('PV', file_name, True) - if 'SRCREVINACTION' in pv: - bb.note("Found SRCREVINACTION in PV (%s) or %s. Please report this bug." % (pv, file_name)) - pr = self.getVar('PR', file_name, True) - dp = int(self.getVar('DEFAULT_PREFERENCE', file_name, True) or "0") - depends = bb.utils.explode_deps(self.getVar("DEPENDS", file_name, True) or "") - packages = (self.getVar('PACKAGES', file_name, True) or "").split() - packages_dynamic = (self.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split() - rprovides = (self.getVar("RPROVIDES", file_name, True) or "").split() - - cacheData.task_deps[file_name] = self.getVar("_task_deps", file_name, True) - - # build PackageName to FileName lookup table - if pn not in cacheData.pkg_pn: - cacheData.pkg_pn[pn] = [] - cacheData.pkg_pn[pn].append(file_name) - - cacheData.stamp[file_name] = self.getVar('STAMP', file_name, True) - - # build FileName to PackageName lookup table - cacheData.pkg_fn[file_name] = pn - cacheData.pkg_pepvpr[file_name] = (pe,pv,pr) - cacheData.pkg_dp[file_name] = dp - - provides = [pn] - for provide in (self.getVar("PROVIDES", file_name, True) or "").split(): - if provide not in provides: - provides.append(provide) - - # Build forward and reverse provider hashes - # Forward: virtual -> [filenames] - # Reverse: PN -> [virtuals] - if pn not in cacheData.pn_provides: - cacheData.pn_provides[pn] = [] - - cacheData.fn_provides[file_name] = provides - for provide in provides: - if provide not in cacheData.providers: - cacheData.providers[provide] = [] - cacheData.providers[provide].append(file_name) - if not provide in cacheData.pn_provides[pn]: - cacheData.pn_provides[pn].append(provide) - - cacheData.deps[file_name] = [] - for dep in depends: - if not dep in cacheData.deps[file_name]: - cacheData.deps[file_name].append(dep) - if not dep in cacheData.all_depends: - cacheData.all_depends.append(dep) - - # Build reverse hash for PACKAGES, so runtime dependencies - # can be be resolved (RDEPENDS, RRECOMMENDS etc.) - for package in packages: - if not package in cacheData.packages: - cacheData.packages[package] = [] - cacheData.packages[package].append(file_name) - rprovides += (self.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() - - for package in packages_dynamic: - if not package in cacheData.packages_dynamic: - cacheData.packages_dynamic[package] = [] - cacheData.packages_dynamic[package].append(file_name) - - for rprovide in rprovides: - if not rprovide in cacheData.rproviders: - cacheData.rproviders[rprovide] = [] - cacheData.rproviders[rprovide].append(file_name) - - # Build hash of runtime depends and rececommends - - if not file_name in cacheData.rundeps: - cacheData.rundeps[file_name] = {} - if not file_name in cacheData.runrecs: - cacheData.runrecs[file_name] = {} - - rdepends = self.getVar('RDEPENDS', file_name, True) or "" - rrecommends = self.getVar('RRECOMMENDS', file_name, True) or "" - for package in packages + [pn]: - if not package in cacheData.rundeps[file_name]: - cacheData.rundeps[file_name][package] = [] - if not package in cacheData.runrecs[file_name]: - cacheData.runrecs[file_name][package] = [] - - cacheData.rundeps[file_name][package] = rdepends + " " + (self.getVar("RDEPENDS_%s" % package, file_name, True) or "") - cacheData.runrecs[file_name][package] = rrecommends + " " + (self.getVar("RRECOMMENDS_%s" % package, file_name, True) or "") - - # Collect files we may need for possible world-dep - # calculations - if not self.getVar('BROKEN', file_name, True) and not self.getVar('EXCLUDE_FROM_WORLD', file_name, True): - cacheData.possible_world.append(file_name) - - # Touch this to make sure its in the cache - self.getVar('__BB_DONT_CACHE', file_name, True) - self.getVar('BBCLASSEXTEND', file_name, True) - - def load_bbfile( self, bbfile , config): - """ - Load and parse one .bb build file - Return the data and whether parsing resulted in the file being skipped - """ - - import bb - from bb import utils, data, parse, debug, event, fatal - - # expand tmpdir to include this topdir - data.setVar('TMPDIR', data.getVar('TMPDIR', config, 1) or "", config) - bbfile_loc = os.path.abspath(os.path.dirname(bbfile)) - oldpath = os.path.abspath(os.getcwd()) - if bb.parse.cached_mtime_noerror(bbfile_loc): - os.chdir(bbfile_loc) - bb_data = data.init_db(config) - try: - bb_data = parse.handle(bbfile, bb_data) # read .bb data - os.chdir(oldpath) - return bb_data - except: - os.chdir(oldpath) - raise - -def init(cooker): - """ - The Objective: Cache the minimum amount of data possible yet get to the - stage of building packages (i.e. tryBuild) without reparsing any .bb files. - - To do this, we intercept getVar calls and only cache the variables we see - being accessed. We rely on the cache getVar calls being made for all - variables bitbake might need to use to reach this stage. For each cached - file we need to track: - - * Its mtime - * The mtimes of all its dependencies - * Whether it caused a parse.SkipPackage exception - - Files causing parsing errors are evicted from the cache. - - """ - return Cache(cooker) - - - -#============================================================================# -# CacheData -#============================================================================# -class CacheData: - """ - The data structures we compile from the cached data - """ - - def __init__(self): - """ - Direct cache variables - (from Cache.handle_data) - """ - self.providers = {} - self.rproviders = {} - self.packages = {} - self.packages_dynamic = {} - self.possible_world = [] - self.pkg_pn = {} - self.pkg_fn = {} - self.pkg_pepvpr = {} - self.pkg_dp = {} - self.pn_provides = {} - self.fn_provides = {} - self.all_depends = [] - self.deps = {} - self.rundeps = {} - self.runrecs = {} - self.task_queues = {} - self.task_deps = {} - self.stamp = {} - self.preferred = {} - - """ - Indirect Cache variables - (set elsewhere) - """ - self.ignored_dependencies = [] - self.world_target = set() - self.bbfile_priority = {} - self.bbfile_config_priorities = [] diff --git a/bitbake-dev/lib/bb/command.py b/bitbake-dev/lib/bb/command.py deleted file mode 100644 index 2bb5365c0..000000000 --- a/bitbake-dev/lib/bb/command.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -BitBake 'Command' module - -Provide an interface to interact with the bitbake server through 'commands' -""" - -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" -The bitbake server takes 'commands' from its UI/commandline. -Commands are either synchronous or asynchronous. -Async commands return data to the client in the form of events. -Sync commands must only return data through the function return value -and must not trigger events, directly or indirectly. -Commands are queued in a CommandQueue -""" - -import bb - -async_cmds = {} -sync_cmds = {} - -class Command: - """ - A queue of asynchronous commands for bitbake - """ - def __init__(self, cooker): - - self.cooker = cooker - self.cmds_sync = CommandsSync() - self.cmds_async = CommandsAsync() - - # FIXME Add lock for this - self.currentAsyncCommand = None - - for attr in CommandsSync.__dict__: - command = attr[:].lower() - method = getattr(CommandsSync, attr) - sync_cmds[command] = (method) - - for attr in CommandsAsync.__dict__: - command = attr[:].lower() - method = getattr(CommandsAsync, attr) - async_cmds[command] = (method) - - def runCommand(self, commandline): - try: - command = commandline.pop(0) - if command in CommandsSync.__dict__: - # Can run synchronous commands straight away - return getattr(CommandsSync, command)(self.cmds_sync, self, commandline) - if self.currentAsyncCommand is not None: - return "Busy (%s in progress)" % self.currentAsyncCommand[0] - if command not in CommandsAsync.__dict__: - return "No such command" - self.currentAsyncCommand = (command, commandline) - self.cooker.server.register_idle_function(self.cooker.runCommands, self.cooker) - return True - except: - import traceback - return traceback.format_exc() - - def runAsyncCommand(self): - try: - if self.currentAsyncCommand is not None: - (command, options) = self.currentAsyncCommand - commandmethod = getattr(CommandsAsync, command) - needcache = getattr( commandmethod, "needcache" ) - if needcache and self.cooker.cookerState != bb.cooker.cookerParsed: - self.cooker.updateCache() - return True - else: - commandmethod(self.cmds_async, self, options) - return False - else: - return False - except: - import traceback - self.finishAsyncCommand(traceback.format_exc()) - return False - - def finishAsyncCommand(self, error = None): - if error: - bb.event.fire(bb.command.CookerCommandFailed(error), self.cooker.configuration.event_data) - else: - bb.event.fire(bb.command.CookerCommandCompleted(), self.cooker.configuration.event_data) - self.currentAsyncCommand = None - - -class CommandsSync: - """ - A class of synchronous commands - These should run quickly so as not to hurt interactive performance. - These must not influence any running synchronous command. - """ - - def stateShutdown(self, command, params): - """ - Trigger cooker 'shutdown' mode - """ - command.cooker.cookerAction = bb.cooker.cookerShutdown - - def stateStop(self, command, params): - """ - Stop the cooker - """ - command.cooker.cookerAction = bb.cooker.cookerStop - - def getCmdLineAction(self, command, params): - """ - Get any command parsed from the commandline - """ - return command.cooker.commandlineAction - - def getVariable(self, command, params): - """ - Read the value of a variable from configuration.data - """ - varname = params[0] - expand = True - if len(params) > 1: - expand = params[1] - - return bb.data.getVar(varname, command.cooker.configuration.data, expand) - - def setVariable(self, command, params): - """ - Set the value of variable in configuration.data - """ - varname = params[0] - value = params[1] - bb.data.setVar(varname, value, command.cooker.configuration.data) - - -class CommandsAsync: - """ - A class of asynchronous commands - These functions communicate via generated events. - Any function that requires metadata parsing should be here. - """ - - def buildFile(self, command, params): - """ - Build a single specified .bb file - """ - bfile = params[0] - task = params[1] - - command.cooker.buildFile(bfile, task) - buildFile.needcache = False - - def buildTargets(self, command, params): - """ - Build a set of targets - """ - pkgs_to_build = params[0] - task = params[1] - - command.cooker.buildTargets(pkgs_to_build, task) - buildTargets.needcache = True - - def generateDepTreeEvent(self, command, params): - """ - Generate an event containing the dependency information - """ - pkgs_to_build = params[0] - task = params[1] - - command.cooker.generateDepTreeEvent(pkgs_to_build, task) - command.finishAsyncCommand() - generateDepTreeEvent.needcache = True - - def generateDotGraph(self, command, params): - """ - Dump dependency information to disk as .dot files - """ - pkgs_to_build = params[0] - task = params[1] - - command.cooker.generateDotGraphFiles(pkgs_to_build, task) - command.finishAsyncCommand() - generateDotGraph.needcache = True - - def showVersions(self, command, params): - """ - Show the currently selected versions - """ - command.cooker.showVersions() - command.finishAsyncCommand() - showVersions.needcache = True - - def showEnvironmentTarget(self, command, params): - """ - Print the environment of a target recipe - (needs the cache to work out which recipe to use) - """ - pkg = params[0] - - command.cooker.showEnvironment(None, pkg) - command.finishAsyncCommand() - showEnvironmentTarget.needcache = True - - def showEnvironment(self, command, params): - """ - Print the standard environment - or if specified the environment for a specified recipe - """ - bfile = params[0] - - command.cooker.showEnvironment(bfile) - command.finishAsyncCommand() - showEnvironment.needcache = False - - def parseFiles(self, command, params): - """ - Parse the .bb files - """ - command.cooker.updateCache() - command.finishAsyncCommand() - parseFiles.needcache = True - - def compareRevisions(self, command, params): - """ - Parse the .bb files - """ - command.cooker.compareRevisions() - command.finishAsyncCommand() - compareRevisions.needcache = True - -# -# Events -# -class CookerCommandCompleted(bb.event.Event): - """ - Cooker command completed - """ - def __init__(self): - bb.event.Event.__init__(self) - - -class CookerCommandFailed(bb.event.Event): - """ - Cooker command completed - """ - def __init__(self, error): - bb.event.Event.__init__(self) - self.error = error - -class CookerCommandSetExitCode(bb.event.Event): - """ - Set the exit code for a cooker command - """ - def __init__(self, exitcode): - bb.event.Event.__init__(self) - self.exitcode = int(exitcode) - - - diff --git a/bitbake-dev/lib/bb/cooker.py b/bitbake-dev/lib/bb/cooker.py deleted file mode 100644 index 8036d7e9d..000000000 --- a/bitbake-dev/lib/bb/cooker.py +++ /dev/null @@ -1,978 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer -# Copyright (C) 2005 Holger Hans Peter Freyther -# Copyright (C) 2005 ROAD GmbH -# Copyright (C) 2006 - 2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import sys, os, getopt, glob, copy, os.path, re, time -import bb -from bb import utils, data, parse, event, cache, providers, taskdata, runqueue -from bb import command -import bb.server.xmlrpc -import itertools, sre_constants - -class MultipleMatches(Exception): - """ - Exception raised when multiple file matches are found - """ - -class ParsingErrorsFound(Exception): - """ - Exception raised when parsing errors are found - """ - -class NothingToBuild(Exception): - """ - Exception raised when there is nothing to build - """ - - -# Different states cooker can be in -cookerClean = 1 -cookerParsing = 2 -cookerParsed = 3 - -# Different action states the cooker can be in -cookerRun = 1 # Cooker is running normally -cookerShutdown = 2 # Active tasks should be brought to a controlled stop -cookerStop = 3 # Stop, now! - -#============================================================================# -# BBCooker -#============================================================================# -class BBCooker: - """ - Manages one bitbake build run - """ - - def __init__(self, configuration, server): - self.status = None - - self.cache = None - self.bb_cache = None - - self.server = server.BitBakeServer(self) - - self.configuration = configuration - - if self.configuration.verbose: - bb.msg.set_verbose(True) - - if self.configuration.debug: - bb.msg.set_debug_level(self.configuration.debug) - else: - bb.msg.set_debug_level(0) - - if self.configuration.debug_domains: - bb.msg.set_debug_domains(self.configuration.debug_domains) - - self.configuration.data = bb.data.init() - - bb.data.inheritFromOS(self.configuration.data) - - for f in self.configuration.file: - self.parseConfigurationFile( f ) - - self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) ) - - if not self.configuration.cmd: - self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data, True) or "build" - - bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, True) - if bbpkgs and len(self.configuration.pkgs_to_build) == 0: - self.configuration.pkgs_to_build.extend(bbpkgs.split()) - - # - # Special updated configuration we use for firing events - # - self.configuration.event_data = bb.data.createCopy(self.configuration.data) - bb.data.update_data(self.configuration.event_data) - - # TOSTOP must not be set or our children will hang when they output - fd = sys.stdout.fileno() - if os.isatty(fd): - import termios - tcattr = termios.tcgetattr(fd) - if tcattr[3] & termios.TOSTOP: - bb.msg.note(1, bb.msg.domain.Build, "The terminal had the TOSTOP bit set, clearing...") - tcattr[3] = tcattr[3] & ~termios.TOSTOP - termios.tcsetattr(fd, termios.TCSANOW, tcattr) - - self.command = bb.command.Command(self) - self.cookerState = cookerClean - self.cookerAction = cookerRun - - def parseConfiguration(self): - - - # Change nice level if we're asked to - nice = bb.data.getVar("BB_NICE_LEVEL", self.configuration.data, True) - if nice: - curnice = os.nice(0) - nice = int(nice) - curnice - bb.msg.note(2, bb.msg.domain.Build, "Renice to %s " % os.nice(nice)) - - def parseCommandLine(self): - # Parse any commandline into actions - if self.configuration.show_environment: - self.commandlineAction = None - - if 'world' in self.configuration.pkgs_to_build: - bb.error("'world' is not a valid target for --environment.") - elif len(self.configuration.pkgs_to_build) > 1: - bb.error("Only one target can be used with the --environment option.") - elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0: - bb.error("No target should be used with the --environment and --buildfile options.") - elif len(self.configuration.pkgs_to_build) > 0: - self.commandlineAction = ["showEnvironmentTarget", self.configuration.pkgs_to_build] - else: - self.commandlineAction = ["showEnvironment", self.configuration.buildfile] - elif self.configuration.buildfile is not None: - self.commandlineAction = ["buildFile", self.configuration.buildfile, self.configuration.cmd] - elif self.configuration.revisions_changed: - self.commandlineAction = ["compareRevisions"] - elif self.configuration.show_versions: - self.commandlineAction = ["showVersions"] - elif self.configuration.parse_only: - self.commandlineAction = ["parseFiles"] - # FIXME - implement - #elif self.configuration.interactive: - # self.interactiveMode() - elif self.configuration.dot_graph: - if self.configuration.pkgs_to_build: - self.commandlineAction = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd] - else: - self.commandlineAction = None - bb.error("Please specify a package name for dependency graph generation.") - else: - if self.configuration.pkgs_to_build: - self.commandlineAction = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd] - else: - self.commandlineAction = None - bb.error("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") - - def runCommands(self, server, data, abort): - """ - Run any queued asynchronous command - This is done by the idle handler so it runs in true context rather than - tied to any UI. - """ - - return self.command.runAsyncCommand() - - def tryBuildPackage(self, fn, item, task, the_data): - """ - Build one task of a package, optionally build following task depends - """ - try: - if not self.configuration.dry_run: - bb.build.exec_task('do_%s' % task, the_data) - return True - except bb.build.FuncFailed: - bb.msg.error(bb.msg.domain.Build, "task stack execution failed") - raise - except bb.build.EventException, e: - event = e.args[1] - bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event)) - raise - - def tryBuild(self, fn, task): - """ - Build a provider and its dependencies. - build_depends is a list of previous build dependencies (not runtime) - If build_depends is empty, we're dealing with a runtime depends - """ - - the_data = self.bb_cache.loadDataFull(fn, self.configuration.data) - - item = self.status.pkg_fn[fn] - - #if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data): - # return True - - return self.tryBuildPackage(fn, item, task, the_data) - - def showVersions(self): - - # Need files parsed - self.updateCache() - - pkg_pn = self.status.pkg_pn - preferred_versions = {} - latest_versions = {} - - # Sort by priority - for pn in pkg_pn.keys(): - (last_ver,last_file,pref_ver,pref_file) = bb.providers.findBestProvider(pn, self.configuration.data, self.status) - preferred_versions[pn] = (pref_ver, pref_file) - latest_versions[pn] = (last_ver, last_file) - - pkg_list = pkg_pn.keys() - pkg_list.sort() - - bb.msg.plain("%-35s %25s %25s" % ("Package Name", "Latest Version", "Preferred Version")) - bb.msg.plain("%-35s %25s %25s\n" % ("============", "==============", "=================")) - - for p in pkg_list: - pref = preferred_versions[p] - latest = latest_versions[p] - - prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2] - lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2] - - if pref == latest: - prefstr = "" - - bb.msg.plain("%-35s %25s %25s" % (p, lateststr, prefstr)) - - def compareRevisions(self): - ret = bb.fetch.fetcher_compare_revisons(self.configuration.data) - bb.event.fire(bb.command.CookerCommandSetExitCode(ret), self.configuration.event_data) - - def showEnvironment(self, buildfile = None, pkgs_to_build = []): - """ - Show the outer or per-package environment - """ - fn = None - envdata = None - - if buildfile: - self.cb = None - self.bb_cache = bb.cache.init(self) - fn = self.matchFile(buildfile) - elif len(pkgs_to_build) == 1: - self.updateCache() - - localdata = data.createCopy(self.configuration.data) - bb.data.update_data(localdata) - bb.data.expandKeys(localdata) - - taskdata = bb.taskdata.TaskData(self.configuration.abort) - taskdata.add_provider(localdata, self.status, pkgs_to_build[0]) - taskdata.add_unresolved(localdata, self.status) - - targetid = taskdata.getbuild_id(pkgs_to_build[0]) - fnid = taskdata.build_targets[targetid][0] - fn = taskdata.fn_index[fnid] - else: - envdata = self.configuration.data - - if fn: - try: - envdata = self.bb_cache.loadDataFull(fn, self.configuration.data) - except IOError, e: - bb.msg.error(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e)) - raise - except Exception, e: - bb.msg.error(bb.msg.domain.Parsing, "%s" % e) - raise - - class dummywrite: - def __init__(self): - self.writebuf = "" - def write(self, output): - self.writebuf = self.writebuf + output - - # emit variables and shell functions - try: - data.update_data(envdata) - wb = dummywrite() - data.emit_env(wb, envdata, True) - bb.msg.plain(wb.writebuf) - except Exception, e: - bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) - # emit the metadata which isnt valid shell - data.expandKeys(envdata) - for e in envdata.keys(): - if data.getVarFlag( e, 'python', envdata ): - bb.msg.plain("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1))) - - def generateDepTreeData(self, pkgs_to_build, task): - """ - Create a dependency tree of pkgs_to_build, returning the data. - """ - - # Need files parsed - self.updateCache() - - # If we are told to do the None task then query the default task - if (task == None): - task = self.configuration.cmd - - pkgs_to_build = self.checkPackages(pkgs_to_build) - - localdata = data.createCopy(self.configuration.data) - bb.data.update_data(localdata) - bb.data.expandKeys(localdata) - taskdata = bb.taskdata.TaskData(self.configuration.abort) - - runlist = [] - for k in pkgs_to_build: - taskdata.add_provider(localdata, self.status, k) - runlist.append([k, "do_%s" % task]) - taskdata.add_unresolved(localdata, self.status) - - rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - rq.prepare_runqueue() - - seen_fnids = [] - depend_tree = {} - depend_tree["depends"] = {} - depend_tree["tdepends"] = {} - depend_tree["pn"] = {} - depend_tree["rdepends-pn"] = {} - depend_tree["packages"] = {} - depend_tree["rdepends-pkg"] = {} - depend_tree["rrecs-pkg"] = {} - - for task in range(len(rq.runq_fnid)): - taskname = rq.runq_task[task] - fnid = rq.runq_fnid[task] - fn = taskdata.fn_index[fnid] - pn = self.status.pkg_fn[fn] - version = "%s:%s-%s" % self.status.pkg_pepvpr[fn] - if pn not in depend_tree["pn"]: - depend_tree["pn"][pn] = {} - depend_tree["pn"][pn]["filename"] = fn - depend_tree["pn"][pn]["version"] = version - for dep in rq.runq_depends[task]: - depfn = taskdata.fn_index[rq.runq_fnid[dep]] - deppn = self.status.pkg_fn[depfn] - dotname = "%s.%s" % (pn, rq.runq_task[task]) - if not dotname in depend_tree["tdepends"]: - depend_tree["tdepends"][dotname] = [] - depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, rq.runq_task[dep])) - if fnid not in seen_fnids: - seen_fnids.append(fnid) - packages = [] - - depend_tree["depends"][pn] = [] - for dep in taskdata.depids[fnid]: - depend_tree["depends"][pn].append(taskdata.build_names_index[dep]) - - depend_tree["rdepends-pn"][pn] = [] - for rdep in taskdata.rdepids[fnid]: - depend_tree["rdepends-pn"][pn].append(taskdata.run_names_index[rdep]) - - rdepends = self.status.rundeps[fn] - for package in rdepends: - depend_tree["rdepends-pkg"][package] = [] - for rdepend in rdepends[package]: - depend_tree["rdepends-pkg"][package].append(rdepend) - packages.append(package) - - rrecs = self.status.runrecs[fn] - for package in rrecs: - depend_tree["rrecs-pkg"][package] = [] - for rdepend in rrecs[package]: - depend_tree["rrecs-pkg"][package].append(rdepend) - if not package in packages: - packages.append(package) - - for package in packages: - if package not in depend_tree["packages"]: - depend_tree["packages"][package] = {} - depend_tree["packages"][package]["pn"] = pn - depend_tree["packages"][package]["filename"] = fn - depend_tree["packages"][package]["version"] = version - - return depend_tree - - - def generateDepTreeEvent(self, pkgs_to_build, task): - """ - Create a task dependency graph of pkgs_to_build. - Generate an event with the result - """ - depgraph = self.generateDepTreeData(pkgs_to_build, task) - bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.configuration.data) - - def generateDotGraphFiles(self, pkgs_to_build, task): - """ - Create a task dependency graph of pkgs_to_build. - Save the result to a set of .dot files. - """ - - depgraph = self.generateDepTreeData(pkgs_to_build, task) - - # Prints a flattened form of package-depends below where subpackages of a package are merged into the main pn - depends_file = file('pn-depends.dot', 'w' ) - print >> depends_file, "digraph depends {" - for pn in depgraph["pn"]: - fn = depgraph["pn"][pn]["filename"] - version = depgraph["pn"][pn]["version"] - print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) - for pn in depgraph["depends"]: - for depend in depgraph["depends"][pn]: - print >> depends_file, '"%s" -> "%s"' % (pn, depend) - for pn in depgraph["rdepends-pn"]: - for rdepend in depgraph["rdepends-pn"][pn]: - print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, rdepend) - print >> depends_file, "}" - bb.msg.plain("PN dependencies saved to 'pn-depends.dot'") - - depends_file = file('package-depends.dot', 'w' ) - print >> depends_file, "digraph depends {" - for package in depgraph["packages"]: - pn = depgraph["packages"][package]["pn"] - fn = depgraph["packages"][package]["filename"] - version = depgraph["packages"][package]["version"] - if package == pn: - print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) - else: - print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn) - for depend in depgraph["depends"][pn]: - print >> depends_file, '"%s" -> "%s"' % (package, depend) - for package in depgraph["rdepends-pkg"]: - for rdepend in depgraph["rdepends-pkg"][package]: - print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) - for package in depgraph["rrecs-pkg"]: - for rdepend in depgraph["rrecs-pkg"][package]: - print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) - print >> depends_file, "}" - bb.msg.plain("Package dependencies saved to 'package-depends.dot'") - - tdepends_file = file('task-depends.dot', 'w' ) - print >> tdepends_file, "digraph depends {" - for task in depgraph["tdepends"]: - (pn, taskname) = task.rsplit(".", 1) - fn = depgraph["pn"][pn]["filename"] - version = depgraph["pn"][pn]["version"] - print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn) - for dep in depgraph["tdepends"][task]: - print >> tdepends_file, '"%s" -> "%s"' % (task, dep) - print >> tdepends_file, "}" - bb.msg.plain("Task dependencies saved to 'task-depends.dot'") - - def buildDepgraph( self ): - all_depends = self.status.all_depends - pn_provides = self.status.pn_provides - - localdata = data.createCopy(self.configuration.data) - bb.data.update_data(localdata) - bb.data.expandKeys(localdata) - - def calc_bbfile_priority(filename): - for (regex, pri) in self.status.bbfile_config_priorities: - if regex.match(filename): - return pri - return 0 - - # Handle PREFERRED_PROVIDERS - for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split(): - try: - (providee, provider) = p.split(':') - except: - bb.msg.fatal(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p) - continue - if providee in self.status.preferred and self.status.preferred[providee] != provider: - bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee])) - self.status.preferred[providee] = provider - - # Calculate priorities for each file - for p in self.status.pkg_fn.keys(): - self.status.bbfile_priority[p] = calc_bbfile_priority(p) - - def buildWorldTargetList(self): - """ - Build package list for "bitbake world" - """ - all_depends = self.status.all_depends - pn_provides = self.status.pn_provides - bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"") - for f in self.status.possible_world: - terminal = True - pn = self.status.pkg_fn[f] - - for p in pn_provides[pn]: - if p.startswith('virtual/'): - bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p)) - terminal = False - break - for pf in self.status.providers[p]: - if self.status.pkg_fn[pf] != pn: - bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p)) - terminal = False - break - if terminal: - self.status.world_target.add(pn) - - # drop reference count now - self.status.possible_world = None - self.status.all_depends = None - - def interactiveMode( self ): - """Drop off into a shell""" - try: - from bb import shell - except ImportError, details: - bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details ) - else: - shell.start( self ) - - def parseConfigurationFile( self, afile ): - try: - self.configuration.data = bb.parse.handle( afile, self.configuration.data ) - - # Handle any INHERITs and inherit the base class - inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split() - for inherit in inherits: - self.configuration.data = bb.parse.handle(os.path.join('classes', '%s.bbclass' % inherit), self.configuration.data, True ) - - # Nomally we only register event handlers at the end of parsing .bb files - # We register any handlers we've found so far here... - for var in data.getVar('__BBHANDLERS', self.configuration.data) or []: - bb.event.register(var,bb.data.getVar(var, self.configuration.data)) - - bb.fetch.fetcher_init(self.configuration.data) - - bb.event.fire(bb.event.ConfigParsed(), self.configuration.data) - - except IOError, e: - bb.msg.fatal(bb.msg.domain.Parsing, "Error when parsing %s: %s" % (afile, str(e))) - except bb.parse.ParseError, details: - bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) ) - - def handleCollections( self, collections ): - """Handle collections""" - if collections: - collection_list = collections.split() - for c in collection_list: - regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1) - if regex == None: - bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c) - continue - priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1) - if priority == None: - bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c) - continue - try: - cre = re.compile(regex) - except re.error: - bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex)) - continue - try: - pri = int(priority) - self.status.bbfile_config_priorities.append((cre, pri)) - except ValueError: - bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority)) - - def buildSetVars(self): - """ - Setup any variables needed before starting a build - """ - if not bb.data.getVar("BUILDNAME", self.configuration.data): - bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data) - bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()), self.configuration.data) - - def matchFiles(self, buildfile): - """ - Find the .bb files which match the expression in 'buildfile'. - """ - - bf = os.path.abspath(buildfile) - try: - os.stat(bf) - return [bf] - except OSError: - (filelist, masked) = self.collect_bbfiles() - regexp = re.compile(buildfile) - matches = [] - for f in filelist: - if regexp.search(f) and os.path.isfile(f): - bf = f - matches.append(f) - return matches - - def matchFile(self, buildfile): - """ - Find the .bb file which matches the expression in 'buildfile'. - Raise an error if multiple files - """ - matches = self.matchFiles(buildfile) - if len(matches) != 1: - bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches))) - for f in matches: - bb.msg.error(bb.msg.domain.Parsing, " %s" % f) - raise MultipleMatches - return matches[0] - - def buildFile(self, buildfile, task): - """ - Build the file matching regexp buildfile - """ - - # Parse the configuration here. We need to do it explicitly here since - # buildFile() doesn't use the cache - self.parseConfiguration() - - # If we are told to do the None task then query the default task - if (task == None): - task = self.configuration.cmd - - fn = self.matchFile(buildfile) - self.buildSetVars() - - # Load data into the cache for fn and parse the loaded cache data - self.bb_cache = bb.cache.init(self) - self.status = bb.cache.CacheData() - self.bb_cache.loadData(fn, self.configuration.data, self.status) - - # Tweak some variables - item = self.bb_cache.getVar('PN', fn, True) - self.status.ignored_dependencies = set() - self.status.bbfile_priority[fn] = 1 - - # Remove external dependencies - self.status.task_deps[fn]['depends'] = {} - self.status.deps[fn] = [] - self.status.rundeps[fn] = [] - self.status.runrecs[fn] = [] - - # Remove stamp for target if force mode active - if self.configuration.force: - bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (task, fn)) - bb.build.del_stamp('do_%s' % task, self.status, fn) - - # Setup taskdata structure - taskdata = bb.taskdata.TaskData(self.configuration.abort) - taskdata.add_provider(self.configuration.data, self.status, item) - - buildname = bb.data.getVar("BUILDNAME", self.configuration.data) - bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.configuration.event_data) - - # Execute the runqueue - runlist = [[item, "do_%s" % task]] - - rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - - def buildFileIdle(server, rq, abort): - - if abort or self.cookerAction == cookerStop: - rq.finish_runqueue(True) - elif self.cookerAction == cookerShutdown: - rq.finish_runqueue(False) - failures = 0 - try: - retval = rq.execute_runqueue() - except runqueue.TaskFailure, fnids: - for fnid in fnids: - bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) - failures = failures + 1 - retval = False - if not retval: - self.command.finishAsyncCommand() - bb.event.fire(bb.event.BuildCompleted(buildname, item, failures), self.configuration.event_data) - return False - return 0.5 - - self.server.register_idle_function(buildFileIdle, rq) - - def buildTargets(self, targets, task): - """ - Attempt to build the targets specified - """ - - # Need files parsed - self.updateCache() - - # If we are told to do the NULL task then query the default task - if (task == None): - task = self.configuration.cmd - - targets = self.checkPackages(targets) - - def buildTargetsIdle(server, rq, abort): - - if abort or self.cookerAction == cookerStop: - rq.finish_runqueue(True) - elif self.cookerAction == cookerShutdown: - rq.finish_runqueue(False) - failures = 0 - try: - retval = rq.execute_runqueue() - except runqueue.TaskFailure, fnids: - for fnid in fnids: - bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) - failures = failures + 1 - retval = False - if not retval: - self.command.finishAsyncCommand() - bb.event.fire(bb.event.BuildCompleted(buildname, targets, failures), self.configuration.event_data) - return None - return 0.5 - - self.buildSetVars() - - buildname = bb.data.getVar("BUILDNAME", self.configuration.data) - bb.event.fire(bb.event.BuildStarted(buildname, targets), self.configuration.event_data) - - localdata = data.createCopy(self.configuration.data) - bb.data.update_data(localdata) - bb.data.expandKeys(localdata) - - taskdata = bb.taskdata.TaskData(self.configuration.abort) - - runlist = [] - for k in targets: - taskdata.add_provider(localdata, self.status, k) - runlist.append([k, "do_%s" % task]) - taskdata.add_unresolved(localdata, self.status) - - rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - - self.server.register_idle_function(buildTargetsIdle, rq) - - def updateCache(self): - - if self.cookerState == cookerParsed: - return - - if self.cookerState != cookerParsing: - - self.parseConfiguration () - - # Import Psyco if available and not disabled - import platform - if platform.machine() in ['i386', 'i486', 'i586', 'i686']: - if not self.configuration.disable_psyco: - try: - import psyco - except ImportError: - bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.") - else: - psyco.bind( CookerParser.parse_next ) - else: - bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.") - - self.status = bb.cache.CacheData() - - ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" - self.status.ignored_dependencies = set(ignore.split()) - - for dep in self.configuration.extra_assume_provided: - self.status.ignored_dependencies.add(dep) - - self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) ) - - bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files") - (filelist, masked) = self.collect_bbfiles() - bb.data.renameVar("__depends", "__base_depends", self.configuration.data) - - self.parser = CookerParser(self, filelist, masked) - self.cookerState = cookerParsing - - if not self.parser.parse_next(): - bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete") - self.buildDepgraph() - self.cookerState = cookerParsed - return None - - return True - - def checkPackages(self, pkgs_to_build): - - if len(pkgs_to_build) == 0: - raise NothingToBuild - - if 'world' in pkgs_to_build: - self.buildWorldTargetList() - pkgs_to_build.remove('world') - for t in self.status.world_target: - pkgs_to_build.append(t) - - return pkgs_to_build - - def get_bbfiles( self, path = os.getcwd() ): - """Get list of default .bb files by reading out the current directory""" - contents = os.listdir(path) - bbfiles = [] - for f in contents: - (root, ext) = os.path.splitext(f) - if ext == ".bb": - bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f))) - return bbfiles - - def find_bbfiles( self, path ): - """Find all the .bb files in a directory""" - from os.path import join - - found = [] - for dir, dirs, files in os.walk(path): - for ignored in ('SCCS', 'CVS', '.svn'): - if ignored in dirs: - dirs.remove(ignored) - found += [join(dir,f) for f in files if f.endswith('.bb')] - - return found - - def collect_bbfiles( self ): - """Collect all available .bb build files""" - parsed, cached, skipped, masked = 0, 0, 0, 0 - self.bb_cache = bb.cache.init(self) - - files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split() - data.setVar("BBFILES", " ".join(files), self.configuration.data) - - if not len(files): - files = self.get_bbfiles() - - if not len(files): - bb.msg.error(bb.msg.domain.Collection, "no files to build.") - - newfiles = [] - for f in files: - if os.path.isdir(f): - dirfiles = self.find_bbfiles(f) - if dirfiles: - newfiles += dirfiles - continue - else: - globbed = glob.glob(f) - if not globbed and os.path.exists(f): - globbed = [f] - newfiles += globbed - - bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) - - if not bbmask: - return (newfiles, 0) - - try: - bbmask_compiled = re.compile(bbmask) - except sre_constants.error: - bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.") - - finalfiles = [] - for f in newfiles: - if bbmask_compiled.search(f): - bb.msg.debug(1, bb.msg.domain.Collection, "skipping masked file %s" % f) - masked += 1 - continue - finalfiles.append(f) - - return (finalfiles, masked) - - def serve(self): - - # Empty the environment. The environment will be populated as - # necessary from the data store. - bb.utils.empty_environment() - - if self.configuration.profile: - try: - import cProfile as profile - except: - import profile - - profile.runctx("self.server.serve_forever()", globals(), locals(), "profile.log") - - # Redirect stdout to capture profile information - pout = open('profile.log.processed', 'w') - so = sys.stdout.fileno() - os.dup2(pout.fileno(), so) - - import pstats - p = pstats.Stats('profile.log') - p.sort_stats('time') - p.print_stats() - p.print_callers() - p.sort_stats('cumulative') - p.print_stats() - - os.dup2(so, pout.fileno()) - pout.flush() - pout.close() - else: - self.server.serve_forever() - - bb.event.fire(CookerExit(), self.configuration.event_data) - -class CookerExit(bb.event.Event): - """ - Notify clients of the Cooker shutdown - """ - - def __init__(self): - bb.event.Event.__init__(self) - -class CookerParser: - def __init__(self, cooker, filelist, masked): - # Internal data - self.filelist = filelist - self.cooker = cooker - - # Accounting statistics - self.parsed = 0 - self.cached = 0 - self.error = 0 - self.masked = masked - self.total = len(filelist) - - self.skipped = 0 - self.virtuals = 0 - - # Pointer to the next file to parse - self.pointer = 0 - - def parse_next(self): - if self.pointer < len(self.filelist): - f = self.filelist[self.pointer] - cooker = self.cooker - - try: - fromCache, skipped, virtuals = cooker.bb_cache.loadData(f, cooker.configuration.data, cooker.status) - if fromCache: - self.cached += 1 - else: - self.parsed += 1 - - self.skipped += skipped - self.virtuals += virtuals - - except IOError, e: - self.error += 1 - cooker.bb_cache.remove(f) - bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e)) - pass - except KeyboardInterrupt: - cooker.bb_cache.remove(f) - cooker.bb_cache.sync() - raise - except Exception, e: - self.error += 1 - cooker.bb_cache.remove(f) - bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f)) - except: - cooker.bb_cache.remove(f) - raise - finally: - bb.event.fire(bb.event.ParseProgress(self.cached, self.parsed, self.skipped, self.masked, self.virtuals, self.error, self.total), cooker.configuration.event_data) - - self.pointer += 1 - - if self.pointer >= self.total: - cooker.bb_cache.sync() - if self.error > 0: - raise ParsingErrorsFound - return False - return True - diff --git a/bitbake-dev/lib/bb/daemonize.py b/bitbake-dev/lib/bb/daemonize.py deleted file mode 100644 index 1a8bb379f..000000000 --- a/bitbake-dev/lib/bb/daemonize.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -Python Deamonizing helper - -Configurable daemon behaviors: - - 1.) The current working directory set to the "/" directory. - 2.) The current file creation mode mask set to 0. - 3.) Close all open files (1024). - 4.) Redirect standard I/O streams to "/dev/null". - -A failed call to fork() now raises an exception. - -References: - 1) Advanced Programming in the Unix Environment: W. Richard Stevens - 2) Unix Programming Frequently Asked Questions: - http://www.erlenstar.demon.co.uk/unix/faq_toc.html - -Modified to allow a function to be daemonized and return for -bitbake use by Richard Purdie -""" - -__author__ = "Chad J. Schroeder" -__copyright__ = "Copyright (C) 2005 Chad J. Schroeder" -__version__ = "0.2" - -# Standard Python modules. -import os # Miscellaneous OS interfaces. -import sys # System-specific parameters and functions. - -# Default daemon parameters. -# File mode creation mask of the daemon. -# For BitBake's children, we do want to inherit the parent umask. -UMASK = None - -# Default maximum for the number of available file descriptors. -MAXFD = 1024 - -# The standard I/O file descriptors are redirected to /dev/null by default. -if (hasattr(os, "devnull")): - REDIRECT_TO = os.devnull -else: - REDIRECT_TO = "/dev/null" - -def createDaemon(function, logfile): - """ - Detach a process from the controlling terminal and run it in the - background as a daemon, returning control to the caller. - """ - - try: - # Fork a child process so the parent can exit. This returns control to - # the command-line or shell. It also guarantees that the child will not - # be a process group leader, since the child receives a new process ID - # and inherits the parent's process group ID. This step is required - # to insure that the next call to os.setsid is successful. - pid = os.fork() - except OSError, e: - raise Exception, "%s [%d]" % (e.strerror, e.errno) - - if (pid == 0): # The first child. - # To become the session leader of this new session and the process group - # leader of the new process group, we call os.setsid(). The process is - # also guaranteed not to have a controlling terminal. - os.setsid() - - # Is ignoring SIGHUP necessary? - # - # It's often suggested that the SIGHUP signal should be ignored before - # the second fork to avoid premature termination of the process. The - # reason is that when the first child terminates, all processes, e.g. - # the second child, in the orphaned group will be sent a SIGHUP. - # - # "However, as part of the session management system, there are exactly - # two cases where SIGHUP is sent on the death of a process: - # - # 1) When the process that dies is the session leader of a session that - # is attached to a terminal device, SIGHUP is sent to all processes - # in the foreground process group of that terminal device. - # 2) When the death of a process causes a process group to become - # orphaned, and one or more processes in the orphaned group are - # stopped, then SIGHUP and SIGCONT are sent to all members of the - # orphaned group." [2] - # - # The first case can be ignored since the child is guaranteed not to have - # a controlling terminal. The second case isn't so easy to dismiss. - # The process group is orphaned when the first child terminates and - # POSIX.1 requires that every STOPPED process in an orphaned process - # group be sent a SIGHUP signal followed by a SIGCONT signal. Since the - # second child is not STOPPED though, we can safely forego ignoring the - # SIGHUP signal. In any case, there are no ill-effects if it is ignored. - # - # import signal # Set handlers for asynchronous events. - # signal.signal(signal.SIGHUP, signal.SIG_IGN) - - try: - # Fork a second child and exit immediately to prevent zombies. This - # causes the second child process to be orphaned, making the init - # process responsible for its cleanup. And, since the first child is - # a session leader without a controlling terminal, it's possible for - # it to acquire one by opening a terminal in the future (System V- - # based systems). This second fork guarantees that the child is no - # longer a session leader, preventing the daemon from ever acquiring - # a controlling terminal. - pid = os.fork() # Fork a second child. - except OSError, e: - raise Exception, "%s [%d]" % (e.strerror, e.errno) - - if (pid == 0): # The second child. - # We probably don't want the file mode creation mask inherited from - # the parent, so we give the child complete control over permissions. - if UMASK is not None: - os.umask(UMASK) - else: - # Parent (the first child) of the second child. - os._exit(0) - else: - # exit() or _exit()? - # _exit is like exit(), but it doesn't call any functions registered - # with atexit (and on_exit) or any registered signal handlers. It also - # closes any open file descriptors. Using exit() may cause all stdio - # streams to be flushed twice and any temporary files may be unexpectedly - # removed. It's therefore recommended that child branches of a fork() - # and the parent branch(es) of a daemon use _exit(). - return - - # Close all open file descriptors. This prevents the child from keeping - # open any file descriptors inherited from the parent. There is a variety - # of methods to accomplish this task. Three are listed below. - # - # Try the system configuration variable, SC_OPEN_MAX, to obtain the maximum - # number of open file descriptors to close. If it doesn't exists, use - # the default value (configurable). - # - # try: - # maxfd = os.sysconf("SC_OPEN_MAX") - # except (AttributeError, ValueError): - # maxfd = MAXFD - # - # OR - # - # if (os.sysconf_names.has_key("SC_OPEN_MAX")): - # maxfd = os.sysconf("SC_OPEN_MAX") - # else: - # maxfd = MAXFD - # - # OR - # - # Use the getrlimit method to retrieve the maximum file descriptor number - # that can be opened by this process. If there is not limit on the - # resource, use the default value. - # - import resource # Resource usage information. - maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] - if (maxfd == resource.RLIM_INFINITY): - maxfd = MAXFD - - # Iterate through and close all file descriptors. -# for fd in range(0, maxfd): -# try: -# os.close(fd) -# except OSError: # ERROR, fd wasn't open to begin with (ignored) -# pass - - # Redirect the standard I/O file descriptors to the specified file. Since - # the daemon has no controlling terminal, most daemons redirect stdin, - # stdout, and stderr to /dev/null. This is done to prevent side-effects - # from reads and writes to the standard I/O file descriptors. - - # This call to open is guaranteed to return the lowest file descriptor, - # which will be 0 (stdin), since it was closed above. -# os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) - - # Duplicate standard input to standard output and standard error. -# os.dup2(0, 1) # standard output (1) -# os.dup2(0, 2) # standard error (2) - - - si = file('/dev/null', 'r') - so = file(logfile, 'w') - se = so - - - # Replace those fds with our own - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - - function() - - os._exit(0) - diff --git a/bitbake-dev/lib/bb/data.py b/bitbake-dev/lib/bb/data.py deleted file mode 100644 index d3058b9a1..000000000 --- a/bitbake-dev/lib/bb/data.py +++ /dev/null @@ -1,562 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Data' implementations - -Functions for interacting with the data structure used by the -BitBake build tools. - -The expandData and update_data are the most expensive -operations. At night the cookie monster came by and -suggested 'give me cookies on setting the variables and -things will work out'. Taking this suggestion into account -applying the skills from the not yet passed 'Entwurf und -Analyse von Algorithmen' lecture and the cookie -monster seems to be right. We will track setVar more carefully -to have faster update_data and expandKeys operations. - -This is a treade-off between speed and memory again but -the speed is more critical here. -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2005 Holger Hans Peter Freyther -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -#Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import sys, os, re, types -if sys.argv[0][-5:] == "pydoc": - path = os.path.dirname(os.path.dirname(sys.argv[1])) -else: - path = os.path.dirname(os.path.dirname(sys.argv[0])) -sys.path.insert(0,path) - -from bb import data_smart -import bb - -_dict_type = data_smart.DataSmart - -def init(): - return _dict_type() - -def init_db(parent = None): - if parent: - return parent.createCopy() - else: - return _dict_type() - -def createCopy(source): - """Link the source set to the destination - If one does not find the value in the destination set, - search will go on to the source set to get the value. - Value from source are copy-on-write. i.e. any try to - modify one of them will end up putting the modified value - in the destination set. - """ - return source.createCopy() - -def initVar(var, d): - """Non-destructive var init for data structure""" - d.initVar(var) - - -def setVar(var, value, d): - """Set a variable to a given value - - Example: - >>> d = init() - >>> setVar('TEST', 'testcontents', d) - >>> print getVar('TEST', d) - testcontents - """ - d.setVar(var,value) - - -def getVar(var, d, exp = 0): - """Gets the value of a variable - - Example: - >>> d = init() - >>> setVar('TEST', 'testcontents', d) - >>> print getVar('TEST', d) - testcontents - """ - return d.getVar(var,exp) - - -def renameVar(key, newkey, d): - """Renames a variable from key to newkey - - Example: - >>> d = init() - >>> setVar('TEST', 'testcontents', d) - >>> renameVar('TEST', 'TEST2', d) - >>> print getVar('TEST2', d) - testcontents - """ - d.renameVar(key, newkey) - -def delVar(var, d): - """Removes a variable from the data set - - Example: - >>> d = init() - >>> setVar('TEST', 'testcontents', d) - >>> print getVar('TEST', d) - testcontents - >>> delVar('TEST', d) - >>> print getVar('TEST', d) - None - """ - d.delVar(var) - -def setVarFlag(var, flag, flagvalue, d): - """Set a flag for a given variable to a given value - - Example: - >>> d = init() - >>> setVarFlag('TEST', 'python', 1, d) - >>> print getVarFlag('TEST', 'python', d) - 1 - """ - d.setVarFlag(var,flag,flagvalue) - -def getVarFlag(var, flag, d): - """Gets given flag from given var - - Example: - >>> d = init() - >>> setVarFlag('TEST', 'python', 1, d) - >>> print getVarFlag('TEST', 'python', d) - 1 - """ - return d.getVarFlag(var,flag) - -def delVarFlag(var, flag, d): - """Removes a given flag from the variable's flags - - Example: - >>> d = init() - >>> setVarFlag('TEST', 'testflag', 1, d) - >>> print getVarFlag('TEST', 'testflag', d) - 1 - >>> delVarFlag('TEST', 'testflag', d) - >>> print getVarFlag('TEST', 'testflag', d) - None - - """ - d.delVarFlag(var,flag) - -def setVarFlags(var, flags, d): - """Set the flags for a given variable - - Note: - setVarFlags will not clear previous - flags. Think of this method as - addVarFlags - - Example: - >>> d = init() - >>> myflags = {} - >>> myflags['test'] = 'blah' - >>> setVarFlags('TEST', myflags, d) - >>> print getVarFlag('TEST', 'test', d) - blah - """ - d.setVarFlags(var,flags) - -def getVarFlags(var, d): - """Gets a variable's flags - - Example: - >>> d = init() - >>> setVarFlag('TEST', 'test', 'blah', d) - >>> print getVarFlags('TEST', d)['test'] - blah - """ - return d.getVarFlags(var) - -def delVarFlags(var, d): - """Removes a variable's flags - - Example: - >>> data = init() - >>> setVarFlag('TEST', 'testflag', 1, data) - >>> print getVarFlag('TEST', 'testflag', data) - 1 - >>> delVarFlags('TEST', data) - >>> print getVarFlags('TEST', data) - None - - """ - d.delVarFlags(var) - -def keys(d): - """Return a list of keys in d - - Example: - >>> d = init() - >>> setVar('TEST', 1, d) - >>> setVar('MOO' , 2, d) - >>> setVarFlag('TEST', 'test', 1, d) - >>> keys(d) - ['TEST', 'MOO'] - """ - return d.keys() - -def getData(d): - """Returns the data object used""" - return d - -def setData(newData, d): - """Sets the data object to the supplied value""" - d = newData - - -## -## Cookie Monsters' query functions -## -def _get_override_vars(d, override): - """ - Internal!!! - - Get the Names of Variables that have a specific - override. This function returns a iterable - Set or an empty list - """ - return [] - -def _get_var_flags_triple(d): - """ - Internal!!! - - """ - return [] - -__expand_var_regexp__ = re.compile(r"\${[^{}]+}") -__expand_python_regexp__ = re.compile(r"\${@.+?}") - -def expand(s, d, varname = None): - """Variable expansion using the data store. - - Example: - Standard expansion: - >>> d = init() - >>> setVar('A', 'sshd', d) - >>> print expand('/usr/bin/${A}', d) - /usr/bin/sshd - - Python expansion: - >>> d = init() - >>> print expand('result: ${@37 * 72}', d) - result: 2664 - - Shell expansion: - >>> d = init() - >>> print expand('${TARGET_MOO}', d) - ${TARGET_MOO} - >>> setVar('TARGET_MOO', 'yupp', d) - >>> print expand('${TARGET_MOO}',d) - yupp - >>> setVar('SRC_URI', 'http://somebug.${TARGET_MOO}', d) - >>> delVar('TARGET_MOO', d) - >>> print expand('${SRC_URI}', d) - http://somebug.${TARGET_MOO} - """ - return d.expand(s, varname) - -def expandKeys(alterdata, readdata = None): - if readdata == None: - readdata = alterdata - - todolist = {} - for key in keys(alterdata): - if not '${' in key: - continue - - ekey = expand(key, readdata) - if key == ekey: - continue - todolist[key] = ekey - - # These two for loops are split for performance to maximise the - # usefulness of the expand cache - - for key in todolist: - ekey = todolist[key] - renameVar(key, ekey, alterdata) - -def expandData(alterdata, readdata = None): - """For each variable in alterdata, expand it, and update the var contents. - Replacements use data from readdata. - - Example: - >>> a=init() - >>> b=init() - >>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a) - >>> setVar("DL_DIR", "/path/to/whatever", b) - >>> expandData(a, b) - >>> print getVar("dlmsg", a) - dl_dir is /path/to/whatever - """ - if readdata == None: - readdata = alterdata - - for key in keys(alterdata): - val = getVar(key, alterdata) - if type(val) is not types.StringType: - continue - expanded = expand(val, readdata) -# print "key is %s, val is %s, expanded is %s" % (key, val, expanded) - if val != expanded: - setVar(key, expanded, alterdata) - -def inheritFromOS(d): - """Inherit variables from the environment.""" - for s in os.environ.keys(): - try: - setVar(s, os.environ[s], d) - setVarFlag(s, "export", True, d) - except TypeError: - pass - -def emit_var(var, o=sys.__stdout__, d = init(), all=False): - """Emit a variable to be sourced by a shell.""" - if getVarFlag(var, "python", d): - return 0 - - export = getVarFlag(var, "export", d) - unexport = getVarFlag(var, "unexport", d) - func = getVarFlag(var, "func", d) - if not all and not export and not unexport and not func: - return 0 - - try: - if all: - oval = getVar(var, d, 0) - val = getVar(var, d, 1) - except KeyboardInterrupt: - raise - except: - excname = str(sys.exc_info()[0]) - if excname == "bb.build.FuncFailed": - raise - o.write('# expansion of %s threw %s\n' % (var, excname)) - return 0 - - if all: - o.write('# %s=%s\n' % (var, oval)) - - if type(val) is not types.StringType: - return 0 - - if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all: - return 0 - - varExpanded = expand(var, d) - - if unexport: - o.write('unset %s\n' % varExpanded) - return 1 - - val.rstrip() - if not val: - return 0 - - if func: - # NOTE: should probably check for unbalanced {} within the var - o.write("%s() {\n%s\n}\n" % (varExpanded, val)) - return 1 - - if export: - o.write('export ') - - # if we're going to output this within doublequotes, - # to a shell, we need to escape the quotes in the var - alter = re.sub('"', '\\"', val.strip()) - o.write('%s="%s"\n' % (varExpanded, alter)) - return 1 - - -def emit_env(o=sys.__stdout__, d = init(), all=False): - """Emits all items in the data store in a format such that it can be sourced by a shell.""" - - env = keys(d) - - for e in env: - if getVarFlag(e, "func", d): - continue - emit_var(e, o, d, all) and o.write('\n') - - for e in env: - if not getVarFlag(e, "func", d): - continue - emit_var(e, o, d) and o.write('\n') - -def update_data(d): - """Modifies the environment vars according to local overrides and commands. - Examples: - Appending to a variable: - >>> d = init() - >>> setVar('TEST', 'this is a', d) - >>> setVar('TEST_append', ' test', d) - >>> setVar('TEST_append', ' of the emergency broadcast system.', d) - >>> update_data(d) - >>> print getVar('TEST', d) - this is a test of the emergency broadcast system. - - Prepending to a variable: - >>> setVar('TEST', 'virtual/libc', d) - >>> setVar('TEST_prepend', 'virtual/tmake ', d) - >>> setVar('TEST_prepend', 'virtual/patcher ', d) - >>> update_data(d) - >>> print getVar('TEST', d) - virtual/patcher virtual/tmake virtual/libc - - Overrides: - >>> setVar('TEST_arm', 'target', d) - >>> setVar('TEST_ramses', 'machine', d) - >>> setVar('TEST_local', 'local', d) - >>> setVar('OVERRIDES', 'arm', d) - - >>> setVar('TEST', 'original', d) - >>> update_data(d) - >>> print getVar('TEST', d) - target - - >>> setVar('OVERRIDES', 'arm:ramses:local', d) - >>> setVar('TEST', 'original', d) - >>> update_data(d) - >>> print getVar('TEST', d) - local - - CopyMonster: - >>> e = d.createCopy() - >>> setVar('TEST_foo', 'foo', e) - >>> update_data(e) - >>> print getVar('TEST', e) - local - - >>> setVar('OVERRIDES', 'arm:ramses:local:foo', e) - >>> update_data(e) - >>> print getVar('TEST', e) - foo - - >>> f = d.createCopy() - >>> setVar('TEST_moo', 'something', f) - >>> setVar('OVERRIDES', 'moo:arm:ramses:local:foo', e) - >>> update_data(e) - >>> print getVar('TEST', e) - foo - - - >>> h = init() - >>> setVar('SRC_URI', 'file://append.foo;patch=1 ', h) - >>> g = h.createCopy() - >>> setVar('SRC_URI_append_arm', 'file://other.foo;patch=1', g) - >>> setVar('OVERRIDES', 'arm:moo', g) - >>> update_data(g) - >>> print getVar('SRC_URI', g) - file://append.foo;patch=1 file://other.foo;patch=1 - - """ - bb.msg.debug(2, bb.msg.domain.Data, "update_data()") - - # now ask the cookie monster for help - #print "Cookie Monster" - #print "Append/Prepend %s" % d._special_values - #print "Overrides %s" % d._seen_overrides - - overrides = (getVar('OVERRIDES', d, 1) or "").split(':') or [] - - # - # Well let us see what breaks here. We used to iterate - # over each variable and apply the override and then - # do the line expanding. - # If we have bad luck - which we will have - the keys - # where in some order that is so important for this - # method which we don't have anymore. - # Anyway we will fix that and write test cases this - # time. - - # - # First we apply all overrides - # Then we will handle _append and _prepend - # - - for o in overrides: - # calculate '_'+override - l = len(o)+1 - - # see if one should even try - if not d._seen_overrides.has_key(o): - continue - - vars = d._seen_overrides[o] - for var in vars: - name = var[:-l] - try: - d[name] = d[var] - except: - bb.msg.note(1, bb.msg.domain.Data, "Untracked delVar") - - # now on to the appends and prepends - if d._special_values.has_key('_append'): - appends = d._special_values['_append'] or [] - for append in appends: - for (a, o) in getVarFlag(append, '_append', d) or []: - # maybe the OVERRIDE was not yet added so keep the append - if (o and o in overrides) or not o: - delVarFlag(append, '_append', d) - if o and not o in overrides: - continue - - sval = getVar(append,d) or "" - sval+=a - setVar(append, sval, d) - - - if d._special_values.has_key('_prepend'): - prepends = d._special_values['_prepend'] or [] - - for prepend in prepends: - for (a, o) in getVarFlag(prepend, '_prepend', d) or []: - # maybe the OVERRIDE was not yet added so keep the prepend - if (o and o in overrides) or not o: - delVarFlag(prepend, '_prepend', d) - if o and not o in overrides: - continue - - sval = a + (getVar(prepend,d) or "") - setVar(prepend, sval, d) - - -def inherits_class(klass, d): - val = getVar('__inherit_cache', d) or [] - if os.path.join('classes', '%s.bbclass' % klass) in val: - return True - return False - -def _test(): - """Start a doctest run on this module""" - import doctest - import bb - from bb import data - bb.msg.set_debug_level(0) - doctest.testmod(data) - -if __name__ == "__main__": - _test() diff --git a/bitbake-dev/lib/bb/data_smart.py b/bitbake-dev/lib/bb/data_smart.py deleted file mode 100644 index 988d5c357..000000000 --- a/bitbake-dev/lib/bb/data_smart.py +++ /dev/null @@ -1,289 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake Smart Dictionary Implementation - -Functions for interacting with the data structure used by the -BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2004, 2005 Seb Frankengul -# Copyright (C) 2005, 2006 Holger Hans Peter Freyther -# Copyright (C) 2005 Uli Luckas -# Copyright (C) 2005 ROAD GmbH -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import copy, os, re, sys, time, types -import bb -from bb import utils, methodpool -from COW import COWDictBase -from new import classobj - - -__setvar_keyword__ = ["_append","_prepend"] -__setvar_regexp__ = re.compile('(?P.*?)(?P_append|_prepend)(_(?P.*))?') -__expand_var_regexp__ = re.compile(r"\${[^{}]+}") -__expand_python_regexp__ = re.compile(r"\${@.+?}") - - -class DataSmart: - def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ): - self.dict = {} - - # cookie monster tribute - self._special_values = special - self._seen_overrides = seen - - self.expand_cache = {} - - def expand(self,s, varname): - def var_sub(match): - key = match.group()[2:-1] - if varname and key: - if varname == key: - raise Exception("variable %s references itself!" % varname) - var = self.getVar(key, 1) - if var is not None: - return var - else: - return match.group() - - def python_sub(match): - import bb - code = match.group()[3:-1] - locals()['d'] = self - s = eval(code) - if type(s) == types.IntType: s = str(s) - return s - - if type(s) is not types.StringType: # sanity check - return s - - if varname and varname in self.expand_cache: - return self.expand_cache[varname] - - while s.find('${') != -1: - olds = s - try: - s = __expand_var_regexp__.sub(var_sub, s) - s = __expand_python_regexp__.sub(python_sub, s) - if s == olds: break - if type(s) is not types.StringType: # sanity check - bb.msg.error(bb.msg.domain.Data, 'expansion of %s returned non-string %s' % (olds, s)) - except KeyboardInterrupt: - raise - except: - bb.msg.note(1, bb.msg.domain.Data, "%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s)) - raise - - if varname: - self.expand_cache[varname] = s - - return s - - def initVar(self, var): - self.expand_cache = {} - if not var in self.dict: - self.dict[var] = {} - - def _findVar(self,var): - _dest = self.dict - - while (_dest and var not in _dest): - if not "_data" in _dest: - _dest = None - break - _dest = _dest["_data"] - - if _dest and var in _dest: - return _dest[var] - return None - - def _makeShadowCopy(self, var): - if var in self.dict: - return - - local_var = self._findVar(var) - - if local_var: - self.dict[var] = copy.copy(local_var) - else: - self.initVar(var) - - def setVar(self,var,value): - self.expand_cache = {} - match = __setvar_regexp__.match(var) - if match and match.group("keyword") in __setvar_keyword__: - base = match.group('base') - keyword = match.group("keyword") - override = match.group('add') - l = self.getVarFlag(base, keyword) or [] - l.append([value, override]) - self.setVarFlag(base, keyword, l) - - # todo make sure keyword is not __doc__ or __module__ - # pay the cookie monster - try: - self._special_values[keyword].add( base ) - except: - self._special_values[keyword] = set() - self._special_values[keyword].add( base ) - - return - - if not var in self.dict: - self._makeShadowCopy(var) - - # more cookies for the cookie monster - if '_' in var: - override = var[var.rfind('_')+1:] - if not self._seen_overrides.has_key(override): - self._seen_overrides[override] = set() - self._seen_overrides[override].add( var ) - - # setting var - self.dict[var]["content"] = value - - def getVar(self,var,exp): - value = self.getVarFlag(var,"content") - - if exp and value: - return self.expand(value,var) - return value - - def renameVar(self, key, newkey): - """ - Rename the variable key to newkey - """ - val = self.getVar(key, 0) - if val is not None: - self.setVar(newkey, val) - - for i in ('_append', '_prepend'): - src = self.getVarFlag(key, i) - if src is None: - continue - - dest = self.getVarFlag(newkey, i) or [] - dest.extend(src) - self.setVarFlag(newkey, i, dest) - - if self._special_values.has_key(i) and key in self._special_values[i]: - self._special_values[i].remove(key) - self._special_values[i].add(newkey) - - self.delVar(key) - - def delVar(self,var): - self.expand_cache = {} - self.dict[var] = {} - - def setVarFlag(self,var,flag,flagvalue): - if not var in self.dict: - self._makeShadowCopy(var) - self.dict[var][flag] = flagvalue - - def getVarFlag(self,var,flag): - local_var = self._findVar(var) - if local_var: - if flag in local_var: - return copy.copy(local_var[flag]) - return None - - def delVarFlag(self,var,flag): - local_var = self._findVar(var) - if not local_var: - return - if not var in self.dict: - self._makeShadowCopy(var) - - if var in self.dict and flag in self.dict[var]: - del self.dict[var][flag] - - def setVarFlags(self,var,flags): - if not var in self.dict: - self._makeShadowCopy(var) - - for i in flags.keys(): - if i == "content": - continue - self.dict[var][i] = flags[i] - - def getVarFlags(self,var): - local_var = self._findVar(var) - flags = {} - - if local_var: - for i in local_var.keys(): - if i == "content": - continue - flags[i] = local_var[i] - - if len(flags) == 0: - return None - return flags - - - def delVarFlags(self,var): - if not var in self.dict: - self._makeShadowCopy(var) - - if var in self.dict: - content = None - - # try to save the content - if "content" in self.dict[var]: - content = self.dict[var]["content"] - self.dict[var] = {} - self.dict[var]["content"] = content - else: - del self.dict[var] - - - def createCopy(self): - """ - Create a copy of self by setting _data to self - """ - # we really want this to be a DataSmart... - data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy()) - data.dict["_data"] = self.dict - - return data - - # Dictionary Methods - def keys(self): - def _keys(d, mykey): - if "_data" in d: - _keys(d["_data"],mykey) - - for key in d.keys(): - if key != "_data": - mykey[key] = None - keytab = {} - _keys(self.dict,keytab) - return keytab.keys() - - def __getitem__(self,item): - #print "Warning deprecated" - return self.getVar(item, False) - - def __setitem__(self,var,data): - #print "Warning deprecated" - self.setVar(var,data) - - diff --git a/bitbake-dev/lib/bb/event.py b/bitbake-dev/lib/bb/event.py deleted file mode 100644 index 7251d7871..000000000 --- a/bitbake-dev/lib/bb/event.py +++ /dev/null @@ -1,275 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Event' implementation - -Classes and functions for manipulating 'events' in the -BitBake build tools. -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import os, re -import bb.utils -import pickle - -# This is the pid for which we should generate the event. This is set when -# the runqueue forks off. -worker_pid = 0 -worker_pipe = None - -class Event: - """Base class for events""" - - def __init__(self): - self.pid = worker_pid - -NotHandled = 0 -Handled = 1 - -Registered = 10 -AlreadyRegistered = 14 - -# Internal -_handlers = {} -_ui_handlers = {} -_ui_handler_seq = 0 - -def fire(event, d): - """Fire off an Event""" - - if worker_pid != 0: - worker_fire(event, d) - return - - for handler in _handlers: - h = _handlers[handler] - event.data = d - if type(h).__name__ == "code": - exec(h) - tmpHandler(event) - else: - h(event) - del event.data - - errors = [] - for h in _ui_handlers: - #print "Sending event %s" % event - try: - # We use pickle here since it better handles object instances - # which xmlrpc's marshaller does not. Events *must* be serializable - # by pickle. - _ui_handlers[h].event.send((pickle.dumps(event))) - except: - errors.append(h) - for h in errors: - del _ui_handlers[h] - -def worker_fire(event, d): - data = "" + pickle.dumps(event) + "" - if os.write(worker_pipe, data) != len (data): - print "Error sending event to server (short write)" - -def fire_from_worker(event, d): - if not event.startswith("") or not event.endswith(""): - print "Error, not an event" - return - event = pickle.loads(event[7:-8]) - bb.event.fire(event, d) - -def register(name, handler): - """Register an Event handler""" - - # already registered - if name in _handlers: - return AlreadyRegistered - - if handler is not None: - # handle string containing python code - if type(handler).__name__ == "str": - tmp = "def tmpHandler(e):\n%s" % handler - comp = bb.utils.better_compile(tmp, "tmpHandler(e)", "bb.event._registerCode") - _handlers[name] = comp - else: - _handlers[name] = handler - - return Registered - -def remove(name, handler): - """Remove an Event handler""" - _handlers.pop(name) - -def register_UIHhandler(handler): - bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 - _ui_handlers[_ui_handler_seq] = handler - return _ui_handler_seq - -def unregister_UIHhandler(handlerNum): - if handlerNum in _ui_handlers: - del _ui_handlers[handlerNum] - return - -def getName(e): - """Returns the name of a class or class instance""" - if getattr(e, "__name__", None) == None: - return e.__class__.__name__ - else: - return e.__name__ - -class ConfigParsed(Event): - """Configuration Parsing Complete""" - -class RecipeParsed(Event): - """ Recipe Parsing Complete """ - - def __init__(self, fn): - self.fn = fn - Event.__init__(self) - -class StampUpdate(Event): - """Trigger for any adjustment of the stamp files to happen""" - - def __init__(self, targets, stampfns): - self._targets = targets - self._stampfns = stampfns - Event.__init__(self) - - def getStampPrefix(self): - return self._stampfns - - def getTargets(self): - return self._targets - - stampPrefix = property(getStampPrefix) - targets = property(getTargets) - -class BuildBase(Event): - """Base class for bbmake run events""" - - def __init__(self, n, p, failures = 0): - self._name = n - self._pkgs = p - Event.__init__(self) - self._failures = failures - - def getPkgs(self): - return self._pkgs - - def setPkgs(self, pkgs): - self._pkgs = pkgs - - def getName(self): - return self._name - - def setName(self, name): - self._name = name - - def getCfg(self): - return self.data - - def setCfg(self, cfg): - self.data = cfg - - def getFailures(self): - """ - Return the number of failed packages - """ - return self._failures - - pkgs = property(getPkgs, setPkgs, None, "pkgs property") - name = property(getName, setName, None, "name property") - cfg = property(getCfg, setCfg, None, "cfg property") - - - - - -class BuildStarted(BuildBase): - """bbmake build run started""" - - -class BuildCompleted(BuildBase): - """bbmake build run completed""" - - - - -class NoProvider(Event): - """No Provider for an Event""" - - def __init__(self, item, runtime=False): - Event.__init__(self) - self._item = item - self._runtime = runtime - - def getItem(self): - return self._item - - def isRuntime(self): - return self._runtime - -class MultipleProviders(Event): - """Multiple Providers""" - - def __init__(self, item, candidates, runtime = False): - Event.__init__(self) - self._item = item - self._candidates = candidates - self._is_runtime = runtime - - def isRuntime(self): - """ - Is this a runtime issue? - """ - return self._is_runtime - - def getItem(self): - """ - The name for the to be build item - """ - return self._item - - def getCandidates(self): - """ - Get the possible Candidates for a PROVIDER. - """ - return self._candidates - -class ParseProgress(Event): - """ - Parsing Progress Event - """ - - def __init__(self, cached, parsed, skipped, masked, virtuals, errors, total): - Event.__init__(self) - self.cached = cached - self.parsed = parsed - self.skipped = skipped - self.virtuals = virtuals - self.masked = masked - self.errors = errors - self.sofar = cached + parsed - self.total = total - -class DepTreeGenerated(Event): - """ - Event when a dependency tree has been generated - """ - - def __init__(self, depgraph): - Event.__init__(self) - self._depgraph = depgraph - diff --git a/bitbake-dev/lib/bb/fetch/__init__.py b/bitbake-dev/lib/bb/fetch/__init__.py deleted file mode 100644 index ab4658bc3..000000000 --- a/bitbake-dev/lib/bb/fetch/__init__.py +++ /dev/null @@ -1,640 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -Classes for obtaining upstream sources for the -BitBake build tools. -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os, re -import bb -from bb import data -from bb import persist_data - -class FetchError(Exception): - """Exception raised when a download fails""" - -class NoMethodError(Exception): - """Exception raised when there is no method to obtain a supplied url or set of urls""" - -class MissingParameterError(Exception): - """Exception raised when a fetch method is missing a critical parameter in the url""" - -class ParameterError(Exception): - """Exception raised when a url cannot be proccessed due to invalid parameters.""" - -class MD5SumError(Exception): - """Exception raised when a MD5SUM of a file does not match the expected one""" - -class InvalidSRCREV(Exception): - """Exception raised when an invalid SRCREV is encountered""" - -def uri_replace(uri, uri_find, uri_replace, d): -# bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: operating on %s" % uri) - if not uri or not uri_find or not uri_replace: - bb.msg.debug(1, bb.msg.domain.Fetcher, "uri_replace: passed an undefined value, not replacing") - uri_decoded = list(bb.decodeurl(uri)) - uri_find_decoded = list(bb.decodeurl(uri_find)) - uri_replace_decoded = list(bb.decodeurl(uri_replace)) - result_decoded = ['','','','','',{}] - for i in uri_find_decoded: - loc = uri_find_decoded.index(i) - result_decoded[loc] = uri_decoded[loc] - import types - if type(i) == types.StringType: - if (re.match(i, uri_decoded[loc])): - result_decoded[loc] = re.sub(i, uri_replace_decoded[loc], uri_decoded[loc]) - if uri_find_decoded.index(i) == 2: - if d: - localfn = bb.fetch.localpath(uri, d) - if localfn: - result_decoded[loc] = os.path.dirname(result_decoded[loc]) + "/" + os.path.basename(bb.fetch.localpath(uri, d)) -# bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: matching %s against %s and replacing with %s" % (i, uri_decoded[loc], uri_replace_decoded[loc])) - else: -# bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: no match") - return uri -# else: -# for j in i.keys(): -# FIXME: apply replacements against options - return bb.encodeurl(result_decoded) - -methods = [] -urldata_cache = {} -saved_headrevs = {} - -def fetcher_init(d): - """ - Called to initilize the fetchers once the configuration data is known - Calls before this must not hit the cache. - """ - pd = persist_data.PersistData(d) - # When to drop SCM head revisions controled by user policy - srcrev_policy = bb.data.getVar('BB_SRCREV_POLICY', d, 1) or "clear" - if srcrev_policy == "cache": - bb.msg.debug(1, bb.msg.domain.Fetcher, "Keeping SRCREV cache due to cache policy of: %s" % srcrev_policy) - elif srcrev_policy == "clear": - bb.msg.debug(1, bb.msg.domain.Fetcher, "Clearing SRCREV cache due to cache policy of: %s" % srcrev_policy) - try: - bb.fetch.saved_headrevs = pd.getKeyValues("BB_URI_HEADREVS") - except: - pass - pd.delDomain("BB_URI_HEADREVS") - else: - bb.msg.fatal(bb.msg.domain.Fetcher, "Invalid SRCREV cache policy of: %s" % srcrev_policy) - - for m in methods: - if hasattr(m, "init"): - m.init(d) - - # Make sure our domains exist - pd.addDomain("BB_URI_HEADREVS") - pd.addDomain("BB_URI_LOCALCOUNT") - -def fetcher_compare_revisons(d): - """ - Compare the revisions in the persistant cache with current values and - return true/false on whether they've changed. - """ - - pd = persist_data.PersistData(d) - data = pd.getKeyValues("BB_URI_HEADREVS") - data2 = bb.fetch.saved_headrevs - - changed = False - for key in data: - if key not in data2 or data2[key] != data[key]: - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s changed" % key) - changed = True - return True - else: - bb.msg.debug(2, bb.msg.domain.Fetcher, "%s did not change" % key) - return False - -# Function call order is usually: -# 1. init -# 2. go -# 3. localpaths -# localpath can be called at any time - -def init(urls, d, setup = True): - urldata = {} - fn = bb.data.getVar('FILE', d, 1) - if fn in urldata_cache: - urldata = urldata_cache[fn] - - for url in urls: - if url not in urldata: - urldata[url] = FetchData(url, d) - - if setup: - for url in urldata: - if not urldata[url].setup: - urldata[url].setup_localpath(d) - - urldata_cache[fn] = urldata - return urldata - -def go(d, urls = None): - """ - Fetch all urls - init must have previously been called - """ - if not urls: - urls = d.getVar("SRC_URI", 1).split() - urldata = init(urls, d, True) - - for u in urls: - ud = urldata[u] - m = ud.method - if ud.localfile: - if not m.forcefetch(u, ud, d) and os.path.exists(ud.md5): - # File already present along with md5 stamp file - # Touch md5 file to show activity - try: - os.utime(ud.md5, None) - except: - # Errors aren't fatal here - pass - continue - lf = bb.utils.lockfile(ud.lockfile) - if not m.forcefetch(u, ud, d) and os.path.exists(ud.md5): - # If someone else fetched this before we got the lock, - # notice and don't try again - try: - os.utime(ud.md5, None) - except: - # Errors aren't fatal here - pass - bb.utils.unlockfile(lf) - continue - m.go(u, ud, d) - if ud.localfile: - if not m.forcefetch(u, ud, d): - Fetch.write_md5sum(u, ud, d) - bb.utils.unlockfile(lf) - - -def checkstatus(d): - """ - Check all urls exist upstream - init must have previously been called - """ - urldata = init([], d, True) - - for u in urldata: - ud = urldata[u] - m = ud.method - bb.msg.note(1, bb.msg.domain.Fetcher, "Testing URL %s" % u) - ret = m.checkstatus(u, ud, d) - if not ret: - bb.msg.fatal(bb.msg.domain.Fetcher, "URL %s doesn't work" % u) - -def localpaths(d): - """ - Return a list of the local filenames, assuming successful fetch - """ - local = [] - urldata = init([], d, True) - - for u in urldata: - ud = urldata[u] - local.append(ud.localpath) - - return local - -srcrev_internal_call = False - -def get_srcrev(d): - """ - Return the version string for the current package - (usually to be used as PV) - Most packages usually only have one SCM so we just pass on the call. - In the multi SCM case, we build a value based on SRCREV_FORMAT which must - have been set. - """ - - # - # Ugly code alert. localpath in the fetchers will try to evaluate SRCREV which - # could translate into a call to here. If it does, we need to catch this - # and provide some way so it knows get_srcrev is active instead of being - # some number etc. hence the srcrev_internal_call tracking and the magic - # "SRCREVINACTION" return value. - # - # Neater solutions welcome! - # - if bb.fetch.srcrev_internal_call: - return "SRCREVINACTION" - - scms = [] - - # Only call setup_localpath on URIs which suppports_srcrev() - urldata = init(bb.data.getVar('SRC_URI', d, 1).split(), d, False) - for u in urldata: - ud = urldata[u] - if ud.method.suppports_srcrev(): - if not ud.setup: - ud.setup_localpath(d) - scms.append(u) - - if len(scms) == 0: - bb.msg.error(bb.msg.domain.Fetcher, "SRCREV was used yet no valid SCM was found in SRC_URI") - raise ParameterError - - bb.data.setVar('__BB_DONT_CACHE','1', d) - - if len(scms) == 1: - return urldata[scms[0]].method.sortable_revision(scms[0], urldata[scms[0]], d) - - # - # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT - # - format = bb.data.getVar('SRCREV_FORMAT', d, 1) - if not format: - bb.msg.error(bb.msg.domain.Fetcher, "The SRCREV_FORMAT variable must be set when multiple SCMs are used.") - raise ParameterError - - for scm in scms: - if 'name' in urldata[scm].parm: - name = urldata[scm].parm["name"] - rev = urldata[scm].method.sortable_revision(scm, urldata[scm], d) - format = format.replace(name, rev) - - return format - -def localpath(url, d, cache = True): - """ - Called from the parser with cache=False since the cache isn't ready - at this point. Also called from classed in OE e.g. patch.bbclass - """ - ud = init([url], d) - if ud[url].method: - return ud[url].localpath - return url - -def runfetchcmd(cmd, d, quiet = False): - """ - Run cmd returning the command output - Raise an error if interrupted or cmd fails - Optionally echo command output to stdout - """ - - # Need to export PATH as binary could be in metadata paths - # rather than host provided - # Also include some other variables. - # FIXME: Should really include all export varaiables? - exportvars = ['PATH', 'GIT_PROXY_COMMAND', 'GIT_PROXY_HOST', 'GIT_PROXY_PORT', 'GIT_CONFIG', 'http_proxy', 'ftp_proxy', 'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 'HOME'] - - for var in exportvars: - val = data.getVar(var, d, True) - if val: - cmd = 'export ' + var + '=%s; %s' % (val, cmd) - - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % cmd) - - # redirect stderr to stdout - stdout_handle = os.popen(cmd + " 2>&1", "r") - output = "" - - while 1: - line = stdout_handle.readline() - if not line: - break - if not quiet: - print line, - output += line - - status = stdout_handle.close() or 0 - signal = status >> 8 - exitstatus = status & 0xff - - if signal: - raise FetchError("Fetch command %s failed with signal %s, output:\n%s" % (cmd, signal, output)) - elif status != 0: - raise FetchError("Fetch command %s failed with exit code %s, output:\n%s" % (cmd, status, output)) - - return output - -class FetchData(object): - """ - A class which represents the fetcher state for a given URI. - """ - def __init__(self, url, d): - self.localfile = "" - (self.type, self.host, self.path, self.user, self.pswd, self.parm) = bb.decodeurl(data.expand(url, d)) - self.date = Fetch.getSRCDate(self, d) - self.url = url - if not self.user and "user" in self.parm: - self.user = self.parm["user"] - if not self.pswd and "pswd" in self.parm: - self.pswd = self.parm["pswd"] - self.setup = False - for m in methods: - if m.supports(url, self, d): - self.method = m - return - raise NoMethodError("Missing implementation for url %s" % url) - - def setup_localpath(self, d): - self.setup = True - if "localpath" in self.parm: - # if user sets localpath for file, use it instead. - self.localpath = self.parm["localpath"] - else: - try: - bb.fetch.srcrev_internal_call = True - self.localpath = self.method.localpath(self.url, self, d) - finally: - bb.fetch.srcrev_internal_call = False - # We have to clear data's internal caches since the cached value of SRCREV is now wrong. - # Horrible... - bb.data.delVar("ISHOULDNEVEREXIST", d) - self.md5 = self.localpath + '.md5' - self.lockfile = self.localpath + '.lock' - - -class Fetch(object): - """Base class for 'fetch'ing data""" - - def __init__(self, urls = []): - self.urls = [] - - def supports(self, url, urldata, d): - """ - Check to see if this fetch class supports a given url. - """ - return 0 - - def localpath(self, url, urldata, d): - """ - Return the local filename of a given url assuming a successful fetch. - Can also setup variables in urldata for use in go (saving code duplication - and duplicate code execution) - """ - return url - - def setUrls(self, urls): - self.__urls = urls - - def getUrls(self): - return self.__urls - - urls = property(getUrls, setUrls, None, "Urls property") - - def forcefetch(self, url, urldata, d): - """ - Force a fetch, even if localpath exists? - """ - return False - - def suppports_srcrev(self): - """ - The fetcher supports auto source revisions (SRCREV) - """ - return False - - def go(self, url, urldata, d): - """ - Fetch urls - Assumes localpath was called first - """ - raise NoMethodError("Missing implementation for url") - - def checkstatus(self, url, urldata, d): - """ - Check the status of a URL - Assumes localpath was called first - """ - bb.msg.note(1, bb.msg.domain.Fetcher, "URL %s could not be checked for status since no method exists." % url) - return True - - def getSRCDate(urldata, d): - """ - Return the SRC Date for the component - - d the bb.data module - """ - if "srcdate" in urldata.parm: - return urldata.parm['srcdate'] - - pn = data.getVar("PN", d, 1) - - if pn: - return data.getVar("SRCDATE_%s" % pn, d, 1) or data.getVar("CVSDATE_%s" % pn, d, 1) or data.getVar("SRCDATE", d, 1) or data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1) - - return data.getVar("SRCDATE", d, 1) or data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1) - getSRCDate = staticmethod(getSRCDate) - - def srcrev_internal_helper(ud, d): - """ - Return: - a) a source revision if specified - b) True if auto srcrev is in action - c) False otherwise - """ - - if 'rev' in ud.parm: - return ud.parm['rev'] - - if 'tag' in ud.parm: - return ud.parm['tag'] - - rev = None - if 'name' in ud.parm: - pn = data.getVar("PN", d, 1) - rev = data.getVar("SRCREV_pn-" + pn + "_" + ud.parm['name'], d, 1) - if not rev: - rev = data.getVar("SRCREV", d, 1) - if rev == "INVALID": - raise InvalidSRCREV("Please set SRCREV to a valid value") - if not rev: - return False - if rev is "SRCREVINACTION": - return True - return rev - - srcrev_internal_helper = staticmethod(srcrev_internal_helper) - - def localcount_internal_helper(ud, d): - """ - Return: - a) a locked localcount if specified - b) None otherwise - """ - - localcount= None - if 'name' in ud.parm: - pn = data.getVar("PN", d, 1) - localcount = data.getVar("LOCALCOUNT_" + ud.parm['name'], d, 1) - if not localcount: - localcount = data.getVar("LOCALCOUNT", d, 1) - return localcount - - localcount_internal_helper = staticmethod(localcount_internal_helper) - - def try_mirror(d, tarfn): - """ - Try to use a mirrored version of the sources. We do this - to avoid massive loads on foreign cvs and svn servers. - This method will be used by the different fetcher - implementations. - - d Is a bb.data instance - tarfn is the name of the tarball - """ - tarpath = os.path.join(data.getVar("DL_DIR", d, 1), tarfn) - if os.access(tarpath, os.R_OK): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists, skipping checkout." % tarfn) - return True - - pn = data.getVar('PN', d, True) - src_tarball_stash = None - if pn: - src_tarball_stash = (data.getVar('SRC_TARBALL_STASH_%s' % pn, d, True) or data.getVar('CVS_TARBALL_STASH_%s' % pn, d, True) or data.getVar('SRC_TARBALL_STASH', d, True) or data.getVar('CVS_TARBALL_STASH', d, True) or "").split() - - ld = d.createCopy() - for stash in src_tarball_stash: - url = stash + tarfn - try: - ud = FetchData(url, ld) - except bb.fetch.NoMethodError: - bb.msg.debug(1, bb.msg.domain.Fetcher, "No method for %s" % url) - continue - - ud.setup_localpath(ld) - - try: - ud.method.go(url, ud, ld) - return True - except (bb.fetch.MissingParameterError, - bb.fetch.FetchError, - bb.fetch.MD5SumError): - import sys - (type, value, traceback) = sys.exc_info() - bb.msg.debug(2, bb.msg.domain.Fetcher, "Tarball stash fetch failure: %s" % value) - return False - try_mirror = staticmethod(try_mirror) - - def verify_md5sum(ud, got_sum): - """ - Verify the md5sum we wanted with the one we got - """ - wanted_sum = None - if 'md5sum' in ud.parm: - wanted_sum = ud.parm['md5sum'] - if not wanted_sum: - return True - - return wanted_sum == got_sum - verify_md5sum = staticmethod(verify_md5sum) - - def write_md5sum(url, ud, d): - md5data = bb.utils.md5_file(ud.localpath) - # verify the md5sum - if not Fetch.verify_md5sum(ud, md5data): - raise MD5SumError(url) - - md5out = file(ud.md5, 'w') - md5out.write(md5data) - md5out.close() - write_md5sum = staticmethod(write_md5sum) - - def latest_revision(self, url, ud, d): - """ - Look in the cache for the latest revision, if not present ask the SCM. - """ - if not hasattr(self, "_latest_revision"): - raise ParameterError - - pd = persist_data.PersistData(d) - key = self.generate_revision_key(url, ud, d) - rev = pd.getValue("BB_URI_HEADREVS", key) - if rev != None: - return str(rev) - - rev = self._latest_revision(url, ud, d) - pd.setValue("BB_URI_HEADREVS", key, rev) - return rev - - def sortable_revision(self, url, ud, d): - """ - - """ - if hasattr(self, "_sortable_revision"): - return self._sortable_revision(url, ud, d) - - pd = persist_data.PersistData(d) - key = self.generate_revision_key(url, ud, d) - - latest_rev = self._build_revision(url, ud, d) - last_rev = pd.getValue("BB_URI_LOCALCOUNT", key + "_rev") - uselocalcount = bb.data.getVar("BB_LOCALCOUNT_OVERRIDE", d, True) or False - count = None - if uselocalcount: - count = Fetch.localcount_internal_helper(ud, d) - if count is None: - count = pd.getValue("BB_URI_LOCALCOUNT", key + "_count") - - if last_rev == latest_rev: - return str(count + "+" + latest_rev) - - buildindex_provided = hasattr(self, "_sortable_buildindex") - if buildindex_provided: - count = self._sortable_buildindex(url, ud, d, latest_rev) - - if count is None: - count = "0" - elif uselocalcount or buildindex_provided: - count = str(count) - else: - count = str(int(count) + 1) - - pd.setValue("BB_URI_LOCALCOUNT", key + "_rev", latest_rev) - pd.setValue("BB_URI_LOCALCOUNT", key + "_count", count) - - return str(count + "+" + latest_rev) - - def generate_revision_key(self, url, ud, d): - key = self._revision_key(url, ud, d) - return "%s-%s" % (key, bb.data.getVar("PN", d, True) or "") - -import cvs -import git -import local -import svn -import wget -import svk -import ssh -import perforce -import bzr -import hg -import osc - -methods.append(local.Local()) -methods.append(wget.Wget()) -methods.append(svn.Svn()) -methods.append(git.Git()) -methods.append(cvs.Cvs()) -methods.append(svk.Svk()) -methods.append(ssh.SSH()) -methods.append(perforce.Perforce()) -methods.append(bzr.Bzr()) -methods.append(hg.Hg()) -methods.append(osc.Osc()) diff --git a/bitbake-dev/lib/bb/fetch/bzr.py b/bitbake-dev/lib/bb/fetch/bzr.py deleted file mode 100644 index b27fb63d0..000000000 --- a/bitbake-dev/lib/bb/fetch/bzr.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -BitBake 'Fetch' implementation for bzr. - -""" - -# Copyright (C) 2007 Ross Burton -# Copyright (C) 2007 Richard Purdie -# -# Classes for obtaining upstream sources for the -# BitBake build tools. -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import os -import sys -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import runfetchcmd - -class Bzr(Fetch): - def supports(self, url, ud, d): - return ud.type in ['bzr'] - - def localpath (self, url, ud, d): - - # Create paths to bzr checkouts - relpath = ud.path - if relpath.startswith('/'): - # Remove leading slash as os.path.join can't cope - relpath = relpath[1:] - ud.pkgdir = os.path.join(data.expand('${BZRDIR}', d), ud.host, relpath) - - revision = Fetch.srcrev_internal_helper(ud, d) - if revision is True: - ud.revision = self.latest_revision(url, ud, d) - elif revision: - ud.revision = revision - - if not ud.revision: - ud.revision = self.latest_revision(url, ud, d) - - ud.localfile = data.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def _buildbzrcommand(self, ud, d, command): - """ - Build up an bzr commandline based on ud - command is "fetch", "update", "revno" - """ - - basecmd = data.expand('${FETCHCMD_bzr}', d) - - proto = "http" - if "proto" in ud.parm: - proto = ud.parm["proto"] - - bzrroot = ud.host + ud.path - - options = [] - - if command is "revno": - bzrcmd = "%s revno %s %s://%s" % (basecmd, " ".join(options), proto, bzrroot) - else: - if ud.revision: - options.append("-r %s" % ud.revision) - - if command is "fetch": - bzrcmd = "%s co %s %s://%s" % (basecmd, " ".join(options), proto, bzrroot) - elif command is "update": - bzrcmd = "%s pull %s --overwrite" % (basecmd, " ".join(options)) - else: - raise FetchError("Invalid bzr command %s" % command) - - return bzrcmd - - def go(self, loc, ud, d): - """Fetch url""" - - # try to use the tarball stash - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping bzr checkout." % ud.localpath) - return - - if os.access(os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir), '.bzr'), os.R_OK): - bzrcmd = self._buildbzrcommand(ud, d, "update") - bb.msg.debug(1, bb.msg.domain.Fetcher, "BZR Update %s" % loc) - os.chdir(os.path.join (ud.pkgdir, os.path.basename(ud.path))) - runfetchcmd(bzrcmd, d) - else: - os.system("rm -rf %s" % os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir))) - bzrcmd = self._buildbzrcommand(ud, d, "fetch") - bb.msg.debug(1, bb.msg.domain.Fetcher, "BZR Checkout %s" % loc) - bb.mkdirhier(ud.pkgdir) - os.chdir(ud.pkgdir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % bzrcmd) - runfetchcmd(bzrcmd, d) - - os.chdir(ud.pkgdir) - # tar them up to a defined filename - try: - runfetchcmd("tar -czf %s %s" % (ud.localpath, os.path.basename(ud.pkgdir)), d) - except: - t, v, tb = sys.exc_info() - try: - os.unlink(ud.localpath) - except OSError: - pass - raise t, v, tb - - def suppports_srcrev(self): - return True - - def _revision_key(self, url, ud, d): - """ - Return a unique key for the url - """ - return "bzr:" + ud.pkgdir - - def _latest_revision(self, url, ud, d): - """ - Return the latest upstream revision number - """ - bb.msg.debug(2, bb.msg.domain.Fetcher, "BZR fetcher hitting network for %s" % url) - - output = runfetchcmd(self._buildbzrcommand(ud, d, "revno"), d, True) - - return output.strip() - - def _sortable_revision(self, url, ud, d): - """ - Return a sortable revision number which in our case is the revision number - """ - - return self._build_revision(url, ud, d) - - def _build_revision(self, url, ud, d): - return ud.revision - diff --git a/bitbake-dev/lib/bb/fetch/cvs.py b/bitbake-dev/lib/bb/fetch/cvs.py deleted file mode 100644 index 90a006500..000000000 --- a/bitbake-dev/lib/bb/fetch/cvs.py +++ /dev/null @@ -1,182 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -Classes for obtaining upstream sources for the -BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -#Based on functions from the base bb module, Copyright 2003 Holger Schurig -# - -import os -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import MissingParameterError - -class Cvs(Fetch): - """ - Class to fetch a module or modules from cvs repositories - """ - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with cvs. - """ - return ud.type in ['cvs'] - - def localpath(self, url, ud, d): - if not "module" in ud.parm: - raise MissingParameterError("cvs method needs a 'module' parameter") - ud.module = ud.parm["module"] - - ud.tag = "" - if 'tag' in ud.parm: - ud.tag = ud.parm['tag'] - - # Override the default date in certain cases - if 'date' in ud.parm: - ud.date = ud.parm['date'] - elif ud.tag: - ud.date = "" - - norecurse = '' - if 'norecurse' in ud.parm: - norecurse = '_norecurse' - - fullpath = '' - if 'fullpath' in ud.parm: - fullpath = '_fullpath' - - ud.localfile = data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def forcefetch(self, url, ud, d): - if (ud.date == "now"): - return True - return False - - def go(self, loc, ud, d): - - # try to use the tarball stash - if not self.forcefetch(loc, ud, d) and Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping cvs checkout." % ud.localpath) - return - - method = "pserver" - if "method" in ud.parm: - method = ud.parm["method"] - - localdir = ud.module - if "localdir" in ud.parm: - localdir = ud.parm["localdir"] - - cvs_port = "" - if "port" in ud.parm: - cvs_port = ud.parm["port"] - - cvs_rsh = None - if method == "ext": - if "rsh" in ud.parm: - cvs_rsh = ud.parm["rsh"] - - if method == "dir": - cvsroot = ud.path - else: - cvsroot = ":" + method - cvsproxyhost = data.getVar('CVS_PROXY_HOST', d, True) - if cvsproxyhost: - cvsroot += ";proxy=" + cvsproxyhost - cvsproxyport = data.getVar('CVS_PROXY_PORT', d, True) - if cvsproxyport: - cvsroot += ";proxyport=" + cvsproxyport - cvsroot += ":" + ud.user - if ud.pswd: - cvsroot += ":" + ud.pswd - cvsroot += "@" + ud.host + ":" + cvs_port + ud.path - - options = [] - if 'norecurse' in ud.parm: - options.append("-l") - if ud.date: - # treat YYYYMMDDHHMM specially for CVS - if len(ud.date) == 12: - options.append("-D \"%s %s:%s UTC\"" % (ud.date[0:8], ud.date[8:10], ud.date[10:12])) - else: - options.append("-D \"%s UTC\"" % ud.date) - if ud.tag: - options.append("-r %s" % ud.tag) - - localdata = data.createCopy(d) - data.setVar('OVERRIDES', "cvs:%s" % data.getVar('OVERRIDES', localdata), localdata) - data.update_data(localdata) - - data.setVar('CVSROOT', cvsroot, localdata) - data.setVar('CVSCOOPTS', " ".join(options), localdata) - data.setVar('CVSMODULE', ud.module, localdata) - cvscmd = data.getVar('FETCHCOMMAND', localdata, 1) - cvsupdatecmd = data.getVar('UPDATECOMMAND', localdata, 1) - - if cvs_rsh: - cvscmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvscmd) - cvsupdatecmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvsupdatecmd) - - # create module directory - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: checking for module directory") - pkg = data.expand('${PN}', d) - pkgdir = os.path.join(data.expand('${CVSDIR}', localdata), pkg) - moddir = os.path.join(pkgdir,localdir) - if os.access(os.path.join(moddir,'CVS'), os.R_OK): - bb.msg.note(1, bb.msg.domain.Fetcher, "Update " + loc) - # update sources there - os.chdir(moddir) - myret = os.system(cvsupdatecmd) - else: - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - # check out sources there - bb.mkdirhier(pkgdir) - os.chdir(pkgdir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % cvscmd) - myret = os.system(cvscmd) - - if myret != 0 or not os.access(moddir, os.R_OK): - try: - os.rmdir(moddir) - except OSError: - pass - raise FetchError(ud.module) - - # tar them up to a defined filename - if 'fullpath' in ud.parm: - os.chdir(pkgdir) - myret = os.system("tar -czf %s %s" % (ud.localpath, localdir)) - else: - os.chdir(moddir) - os.chdir('..') - myret = os.system("tar -czf %s %s" % (ud.localpath, os.path.basename(moddir))) - - if myret != 0: - try: - os.unlink(ud.localpath) - except OSError: - pass - raise FetchError(ud.module) diff --git a/bitbake-dev/lib/bb/fetch/git.py b/bitbake-dev/lib/bb/fetch/git.py deleted file mode 100644 index 0e68325db..000000000 --- a/bitbake-dev/lib/bb/fetch/git.py +++ /dev/null @@ -1,216 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' git implementation - -""" - -#Copyright (C) 2005 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import os -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import runfetchcmd - -class Git(Fetch): - """Class to fetch a module or modules from git repositories""" - def init(self, d): - # - # Only enable _sortable revision if the key is set - # - if bb.data.getVar("BB_GIT_CLONE_FOR_SRCREV", d, True): - self._sortable_buildindex = self._sortable_buildindex_disabled - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with git. - """ - return ud.type in ['git'] - - def localpath(self, url, ud, d): - - if 'protocol' in ud.parm: - ud.proto = ud.parm['protocol'] - elif not ud.host: - ud.proto = 'file' - else: - ud.proto = "rsync" - - ud.branch = ud.parm.get("branch", "master") - - gitsrcname = '%s%s' % (ud.host, ud.path.replace('/', '.')) - ud.mirrortarball = 'git_%s.tar.gz' % (gitsrcname) - ud.clonedir = os.path.join(data.expand('${GITDIR}', d), gitsrcname) - - tag = Fetch.srcrev_internal_helper(ud, d) - if tag is True: - ud.tag = self.latest_revision(url, ud, d) - elif tag: - ud.tag = tag - - if not ud.tag or ud.tag == "master": - ud.tag = self.latest_revision(url, ud, d) - - subdir = ud.parm.get("subpath", "") - if subdir != "": - if subdir.endswith("/"): - subdir = subdir[:-1] - subdirpath = os.path.join(ud.path, subdir); - else: - subdirpath = ud.path; - - if 'fullclone' in ud.parm: - ud.localfile = ud.mirrortarball - else: - ud.localfile = data.expand('git_%s%s_%s.tar.gz' % (ud.host, subdirpath.replace('/', '.'), ud.tag), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def go(self, loc, ud, d): - """Fetch url""" - - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists (or was stashed). Skipping git checkout." % ud.localpath) - return - - if ud.user: - username = ud.user + '@' - else: - username = "" - - repofile = os.path.join(data.getVar("DL_DIR", d, 1), ud.mirrortarball) - - coname = '%s' % (ud.tag) - codir = os.path.join(ud.clonedir, coname) - - if not os.path.exists(ud.clonedir): - if Fetch.try_mirror(d, ud.mirrortarball): - bb.mkdirhier(ud.clonedir) - os.chdir(ud.clonedir) - runfetchcmd("tar -xzf %s" % (repofile), d) - else: - runfetchcmd("git clone -n %s://%s%s%s %s" % (ud.proto, username, ud.host, ud.path, ud.clonedir), d) - - os.chdir(ud.clonedir) - # Remove all but the .git directory - if not self._contains_ref(ud.tag, d): - runfetchcmd("rm * -Rf", d) - runfetchcmd("git fetch %s://%s%s%s %s" % (ud.proto, username, ud.host, ud.path, ud.branch), d) - runfetchcmd("git fetch --tags %s://%s%s%s" % (ud.proto, username, ud.host, ud.path), d) - runfetchcmd("git prune-packed", d) - runfetchcmd("git pack-redundant --all | xargs -r rm", d) - - os.chdir(ud.clonedir) - mirror_tarballs = data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) - if mirror_tarballs != "0" or 'fullclone' in ud.parm: - bb.msg.note(1, bb.msg.domain.Fetcher, "Creating tarball of git repository") - runfetchcmd("tar -czf %s %s" % (repofile, os.path.join(".", ".git", "*") ), d) - - if 'fullclone' in ud.parm: - return - - if os.path.exists(codir): - bb.utils.prunedir(codir) - - subdir = ud.parm.get("subpath", "") - if subdir != "": - if subdir.endswith("/"): - subdirbase = os.path.basename(subdir[:-1]) - else: - subdirbase = os.path.basename(subdir) - else: - subdirbase = "" - - if subdir != "": - readpathspec = ":%s" % (subdir) - codir = os.path.join(codir, "git") - coprefix = os.path.join(codir, subdirbase, "") - else: - readpathspec = "" - coprefix = os.path.join(codir, "git", "") - - bb.mkdirhier(codir) - os.chdir(ud.clonedir) - runfetchcmd("git read-tree %s%s" % (ud.tag, readpathspec), d) - runfetchcmd("git checkout-index -q -f --prefix=%s -a" % (coprefix), d) - - os.chdir(codir) - bb.msg.note(1, bb.msg.domain.Fetcher, "Creating tarball of git checkout") - runfetchcmd("tar -czf %s %s" % (ud.localpath, os.path.join(".", "*") ), d) - - os.chdir(ud.clonedir) - bb.utils.prunedir(codir) - - def suppports_srcrev(self): - return True - - def _contains_ref(self, tag, d): - output = runfetchcmd("git log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % tag, d, quiet=True) - return output.split()[0] != "0" - - def _revision_key(self, url, ud, d): - """ - Return a unique key for the url - """ - return "git:" + ud.host + ud.path.replace('/', '.') - - def _latest_revision(self, url, ud, d): - """ - Compute the HEAD revision for the url - """ - if ud.user: - username = ud.user + '@' - else: - username = "" - - cmd = "git ls-remote %s://%s%s%s %s" % (ud.proto, username, ud.host, ud.path, ud.branch) - output = runfetchcmd(cmd, d, True) - if not output: - raise bb.fetch.FetchError("Fetch command %s gave empty output\n" % (cmd)) - return output.split()[0] - - def _build_revision(self, url, ud, d): - return ud.tag - - def _sortable_buildindex_disabled(self, url, ud, d, rev): - """ - Return a suitable buildindex for the revision specified. This is done by counting revisions - using "git rev-list" which may or may not work in different circumstances. - """ - - cwd = os.getcwd() - - # Check if we have the rev already - - if not os.path.exists(ud.clonedir): - print "no repo" - self.go(None, ud, d) - if not os.path.exists(ud.clonedir): - bb.msg.error(bb.msg.domain.Fetcher, "GIT repository for %s doesn't exist in %s, cannot get sortable buildnumber, using old value" % (url, ud.clonedir)) - return None - - - os.chdir(ud.clonedir) - if not self._contains_ref(rev, d): - self.go(None, ud, d) - - output = runfetchcmd("git rev-list %s -- 2> /dev/null | wc -l" % rev, d, quiet=True) - os.chdir(cwd) - - buildindex = "%s" % output.split()[0] - bb.msg.debug(1, bb.msg.domain.Fetcher, "GIT repository for %s in %s is returning %s revisions in rev-list before %s" % (url, repodir, buildindex, rev)) - return buildindex - diff --git a/bitbake-dev/lib/bb/fetch/hg.py b/bitbake-dev/lib/bb/fetch/hg.py deleted file mode 100644 index 08cb61fc2..000000000 --- a/bitbake-dev/lib/bb/fetch/hg.py +++ /dev/null @@ -1,178 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementation for mercurial DRCS (hg). - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2004 Marcin Juszkiewicz -# Copyright (C) 2007 Robert Schuster -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import sys -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import MissingParameterError -from bb.fetch import runfetchcmd - -class Hg(Fetch): - """Class to fetch a from mercurial repositories""" - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with mercurial. - """ - return ud.type in ['hg'] - - def localpath(self, url, ud, d): - if not "module" in ud.parm: - raise MissingParameterError("hg method needs a 'module' parameter") - - ud.module = ud.parm["module"] - - # Create paths to mercurial checkouts - relpath = ud.path - if relpath.startswith('/'): - # Remove leading slash as os.path.join can't cope - relpath = relpath[1:] - ud.pkgdir = os.path.join(data.expand('${HGDIR}', d), ud.host, relpath) - ud.moddir = os.path.join(ud.pkgdir, ud.module) - - if 'rev' in ud.parm: - ud.revision = ud.parm['rev'] - else: - tag = Fetch.srcrev_internal_helper(ud, d) - if tag is True: - ud.revision = self.latest_revision(url, ud, d) - elif tag: - ud.revision = tag - else: - ud.revision = self.latest_revision(url, ud, d) - - ud.localfile = data.expand('%s_%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def _buildhgcommand(self, ud, d, command): - """ - Build up an hg commandline based on ud - command is "fetch", "update", "info" - """ - - basecmd = data.expand('${FETCHCMD_hg}', d) - - proto = "http" - if "proto" in ud.parm: - proto = ud.parm["proto"] - - host = ud.host - if proto == "file": - host = "/" - ud.host = "localhost" - - if not ud.user: - hgroot = host + ud.path - else: - hgroot = ud.user + "@" + host + ud.path - - if command is "info": - return "%s identify -i %s://%s/%s" % (basecmd, proto, hgroot, ud.module) - - options = []; - if ud.revision: - options.append("-r %s" % ud.revision) - - if command is "fetch": - cmd = "%s clone %s %s://%s/%s %s" % (basecmd, " ".join(options), proto, hgroot, ud.module, ud.module) - elif command is "pull": - # do not pass options list; limiting pull to rev causes the local - # repo not to contain it and immediately following "update" command - # will crash - cmd = "%s pull" % (basecmd) - elif command is "update": - cmd = "%s update -C %s" % (basecmd, " ".join(options)) - else: - raise FetchError("Invalid hg command %s" % command) - - return cmd - - def go(self, loc, ud, d): - """Fetch url""" - - # try to use the tarball stash - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping hg checkout." % ud.localpath) - return - - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: checking for module directory '" + ud.moddir + "'") - - if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK): - updatecmd = self._buildhgcommand(ud, d, "pull") - bb.msg.note(1, bb.msg.domain.Fetcher, "Update " + loc) - # update sources there - os.chdir(ud.moddir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % updatecmd) - runfetchcmd(updatecmd, d) - - else: - fetchcmd = self._buildhgcommand(ud, d, "fetch") - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - # check out sources there - bb.mkdirhier(ud.pkgdir) - os.chdir(ud.pkgdir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % fetchcmd) - runfetchcmd(fetchcmd, d) - - # Even when we clone (fetch), we still need to update as hg's clone - # won't checkout the specified revision if its on a branch - updatecmd = self._buildhgcommand(ud, d, "update") - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % updatecmd) - runfetchcmd(updatecmd, d) - - os.chdir(ud.pkgdir) - try: - runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d) - except: - t, v, tb = sys.exc_info() - try: - os.unlink(ud.localpath) - except OSError: - pass - raise t, v, tb - - def suppports_srcrev(self): - return True - - def _latest_revision(self, url, ud, d): - """ - Compute tip revision for the url - """ - output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d) - return output.strip() - - def _build_revision(self, url, ud, d): - return ud.revision - - def _revision_key(self, url, ud, d): - """ - Return a unique key for the url - """ - return "hg:" + ud.moddir - diff --git a/bitbake-dev/lib/bb/fetch/local.py b/bitbake-dev/lib/bb/fetch/local.py deleted file mode 100644 index f9bdf589c..000000000 --- a/bitbake-dev/lib/bb/fetch/local.py +++ /dev/null @@ -1,72 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -Classes for obtaining upstream sources for the -BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import bb -from bb import data -from bb.fetch import Fetch - -class Local(Fetch): - def supports(self, url, urldata, d): - """ - Check to see if a given url represents a local fetch. - """ - return urldata.type in ['file'] - - def localpath(self, url, urldata, d): - """ - Return the local filename of a given url assuming a successful fetch. - """ - path = url.split("://")[1] - path = path.split(";")[0] - newpath = path - if path[0] != "/": - filespath = data.getVar('FILESPATH', d, 1) - if filespath: - newpath = bb.which(filespath, path) - if not newpath: - filesdir = data.getVar('FILESDIR', d, 1) - if filesdir: - newpath = os.path.join(filesdir, path) - # We don't set localfile as for this fetcher the file is already local! - return newpath - - def go(self, url, urldata, d): - """Fetch urls (no-op for Local method)""" - # no need to fetch local files, we'll deal with them in place. - return 1 - - def checkstatus(self, url, urldata, d): - """ - Check the status of the url - """ - if urldata.localpath.find("*") != -1: - bb.msg.note(1, bb.msg.domain.Fetcher, "URL %s looks like a glob and was therefore not checked." % url) - return True - if os.path.exists(urldata.localpath): - return True - return False diff --git a/bitbake-dev/lib/bb/fetch/osc.py b/bitbake-dev/lib/bb/fetch/osc.py deleted file mode 100644 index 2c34caf6c..000000000 --- a/bitbake-dev/lib/bb/fetch/osc.py +++ /dev/null @@ -1,155 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -Bitbake "Fetch" implementation for osc (Opensuse build service client). -Based on the svn "Fetch" implementation. - -""" - -import os -import sys -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import MissingParameterError -from bb.fetch import runfetchcmd - -class Osc(Fetch): - """Class to fetch a module or modules from Opensuse build server - repositories.""" - - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with osc. - """ - return ud.type in ['osc'] - - def localpath(self, url, ud, d): - if not "module" in ud.parm: - raise MissingParameterError("osc method needs a 'module' parameter.") - - ud.module = ud.parm["module"] - - # Create paths to osc checkouts - relpath = ud.path - if relpath.startswith('/'): - # Remove leading slash as os.path.join can't cope - relpath = relpath[1:] - ud.pkgdir = os.path.join(data.expand('${OSCDIR}', d), ud.host) - ud.moddir = os.path.join(ud.pkgdir, relpath, ud.module) - - if 'rev' in ud.parm: - ud.revision = ud.parm['rev'] - else: - pv = data.getVar("PV", d, 0) - rev = Fetch.srcrev_internal_helper(ud, d) - if rev and rev != True: - ud.revision = rev - else: - ud.revision = "" - - ud.localfile = data.expand('%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.path.replace('/', '.'), ud.revision), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def _buildosccommand(self, ud, d, command): - """ - Build up an ocs commandline based on ud - command is "fetch", "update", "info" - """ - - basecmd = data.expand('${FETCHCMD_osc}', d) - - proto = "ocs" - if "proto" in ud.parm: - proto = ud.parm["proto"] - - options = [] - - config = "-c %s" % self.generate_config(ud, d) - - if ud.revision: - options.append("-r %s" % ud.revision) - - coroot = ud.path - if coroot.startswith('/'): - # Remove leading slash as os.path.join can't cope - coroot= coroot[1:] - - if command is "fetch": - osccmd = "%s %s co %s/%s %s" % (basecmd, config, coroot, ud.module, " ".join(options)) - elif command is "update": - osccmd = "%s %s up %s" % (basecmd, config, " ".join(options)) - else: - raise FetchError("Invalid osc command %s" % command) - - return osccmd - - def go(self, loc, ud, d): - """ - Fetch url - """ - - # Try to use the tarball stash - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping osc checkout." % ud.localpath) - return - - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: checking for module directory '" + ud.moddir + "'") - - if os.access(os.path.join(data.expand('${OSCDIR}', d), ud.path, ud.module), os.R_OK): - oscupdatecmd = self._buildosccommand(ud, d, "update") - bb.msg.note(1, bb.msg.domain.Fetcher, "Update "+ loc) - # update sources there - os.chdir(ud.moddir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % oscupdatecmd) - runfetchcmd(oscupdatecmd, d) - else: - oscfetchcmd = self._buildosccommand(ud, d, "fetch") - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - # check out sources there - bb.mkdirhier(ud.pkgdir) - os.chdir(ud.pkgdir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % oscfetchcmd) - runfetchcmd(oscfetchcmd, d) - - os.chdir(os.path.join(ud.pkgdir + ud.path)) - # tar them up to a defined filename - try: - runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d) - except: - t, v, tb = sys.exc_info() - try: - os.unlink(ud.localpath) - except OSError: - pass - raise t, v, tb - - def supports_srcrev(self): - return False - - def generate_config(self, ud, d): - """ - Generate a .oscrc to be used for this run. - """ - - config_path = "%s/oscrc" % data.expand('${OSCDIR}', d) - if (os.path.exists(config_path)): - os.remove(config_path) - - f = open(config_path, 'w') - f.write("[general]\n") - f.write("apisrv = %s\n" % ud.host) - f.write("scheme = http\n") - f.write("su-wrapper = su -c\n") - f.write("build-root = %s\n" % data.expand('${WORKDIR}', d)) - f.write("urllist = http://moblin-obs.jf.intel.com:8888/build/%(project)s/%(repository)s/%(buildarch)s/:full/%(name)s.rpm\n") - f.write("extra-pkgs = gzip\n") - f.write("\n") - f.write("[%s]\n" % ud.host) - f.write("user = %s\n" % ud.parm["user"]) - f.write("pass = %s\n" % ud.parm["pswd"]) - f.close() - - return config_path diff --git a/bitbake-dev/lib/bb/fetch/perforce.py b/bitbake-dev/lib/bb/fetch/perforce.py deleted file mode 100644 index 394f5a225..000000000 --- a/bitbake-dev/lib/bb/fetch/perforce.py +++ /dev/null @@ -1,214 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -Classes for obtaining upstream sources for the -BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError - -class Perforce(Fetch): - def supports(self, url, ud, d): - return ud.type in ['p4'] - - def doparse(url,d): - parm = {} - path = url.split("://")[1] - delim = path.find("@"); - if delim != -1: - (user,pswd,host,port) = path.split('@')[0].split(":") - path = path.split('@')[1] - else: - (host,port) = data.getVar('P4PORT', d).split(':') - user = "" - pswd = "" - - if path.find(";") != -1: - keys=[] - values=[] - plist = path.split(';') - for item in plist: - if item.count('='): - (key,value) = item.split('=') - keys.append(key) - values.append(value) - - parm = dict(zip(keys,values)) - path = "//" + path.split(';')[0] - host += ":%s" % (port) - parm["cset"] = Perforce.getcset(d, path, host, user, pswd, parm) - - return host,path,user,pswd,parm - doparse = staticmethod(doparse) - - def getcset(d, depot,host,user,pswd,parm): - p4opt = "" - if "cset" in parm: - return parm["cset"]; - if user: - p4opt += " -u %s" % (user) - if pswd: - p4opt += " -P %s" % (pswd) - if host: - p4opt += " -p %s" % (host) - - p4date = data.getVar("P4DATE", d, 1) - if "revision" in parm: - depot += "#%s" % (parm["revision"]) - elif "label" in parm: - depot += "@%s" % (parm["label"]) - elif p4date: - depot += "@%s" % (p4date) - - p4cmd = data.getVar('FETCHCOMMAND_p4', d, 1) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s%s changes -m 1 %s" % (p4cmd, p4opt, depot)) - p4file = os.popen("%s%s changes -m 1 %s" % (p4cmd, p4opt, depot)) - cset = p4file.readline().strip() - bb.msg.debug(1, bb.msg.domain.Fetcher, "READ %s" % (cset)) - if not cset: - return -1 - - return cset.split(' ')[1] - getcset = staticmethod(getcset) - - def localpath(self, url, ud, d): - - (host,path,user,pswd,parm) = Perforce.doparse(url,d) - - # If a label is specified, we use that as our filename - - if "label" in parm: - ud.localfile = "%s.tar.gz" % (parm["label"]) - return os.path.join(data.getVar("DL_DIR", d, 1), ud.localfile) - - base = path - which = path.find('/...') - if which != -1: - base = path[:which] - - if base[0] == "/": - base = base[1:] - - cset = Perforce.getcset(d, path, host, user, pswd, parm) - - ud.localfile = data.expand('%s+%s+%s.tar.gz' % (host,base.replace('/', '.'), cset), d) - - return os.path.join(data.getVar("DL_DIR", d, 1), ud.localfile) - - def go(self, loc, ud, d): - """ - Fetch urls - """ - - # try to use the tarball stash - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping perforce checkout." % ud.localpath) - return - - (host,depot,user,pswd,parm) = Perforce.doparse(loc, d) - - if depot.find('/...') != -1: - path = depot[:depot.find('/...')] - else: - path = depot - - if "module" in parm: - module = parm["module"] - else: - module = os.path.basename(path) - - localdata = data.createCopy(d) - data.setVar('OVERRIDES', "p4:%s" % data.getVar('OVERRIDES', localdata), localdata) - data.update_data(localdata) - - # Get the p4 command - p4opt = "" - if user: - p4opt += " -u %s" % (user) - - if pswd: - p4opt += " -P %s" % (pswd) - - if host: - p4opt += " -p %s" % (host) - - p4cmd = data.getVar('FETCHCOMMAND', localdata, 1) - - # create temp directory - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: creating temporary directory") - bb.mkdirhier(data.expand('${WORKDIR}', localdata)) - data.setVar('TMPBASE', data.expand('${WORKDIR}/oep4.XXXXXX', localdata), localdata) - tmppipe = os.popen(data.getVar('MKTEMPDIRCMD', localdata, 1) or "false") - tmpfile = tmppipe.readline().strip() - if not tmpfile: - bb.error("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.") - raise FetchError(module) - - if "label" in parm: - depot = "%s@%s" % (depot,parm["label"]) - else: - cset = Perforce.getcset(d, depot, host, user, pswd, parm) - depot = "%s@%s" % (depot,cset) - - os.chdir(tmpfile) - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - bb.msg.note(1, bb.msg.domain.Fetcher, "%s%s files %s" % (p4cmd, p4opt, depot)) - p4file = os.popen("%s%s files %s" % (p4cmd, p4opt, depot)) - - if not p4file: - bb.error("Fetch: unable to get the P4 files from %s" % (depot)) - raise FetchError(module) - - count = 0 - - for file in p4file: - list = file.split() - - if list[2] == "delete": - continue - - dest = list[0][len(path)+1:] - where = dest.find("#") - - os.system("%s%s print -o %s/%s %s" % (p4cmd, p4opt, module,dest[:where],list[0])) - count = count + 1 - - if count == 0: - bb.error("Fetch: No files gathered from the P4 fetch") - raise FetchError(module) - - myret = os.system("tar -czf %s %s" % (ud.localpath, module)) - if myret != 0: - try: - os.unlink(ud.localpath) - except OSError: - pass - raise FetchError(module) - # cleanup - os.system('rm -rf %s' % tmpfile) - - diff --git a/bitbake-dev/lib/bb/fetch/ssh.py b/bitbake-dev/lib/bb/fetch/ssh.py deleted file mode 100644 index 68e6fdb1d..000000000 --- a/bitbake-dev/lib/bb/fetch/ssh.py +++ /dev/null @@ -1,118 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -''' -BitBake 'Fetch' implementations - -This implementation is for Secure Shell (SSH), and attempts to comply with the -IETF secsh internet draft: - http://tools.ietf.org/wg/secsh/draft-ietf-secsh-scp-sftp-ssh-uri/ - - Currently does not support the sftp parameters, as this uses scp - Also does not support the 'fingerprint' connection parameter. - -''' - -# Copyright (C) 2006 OpenedHand Ltd. -# -# -# Based in part on svk.py: -# Copyright (C) 2006 Holger Hans Peter Freyther -# Based on svn.py: -# Copyright (C) 2003, 2004 Chris Larson -# Based on functions from the base bb module: -# Copyright 2003 Holger Schurig -# -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import re, os -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError - - -__pattern__ = re.compile(r''' - \s* # Skip leading whitespace - ssh:// # scheme - ( # Optional username/password block - (?P\S+) # username - (:(?P\S+))? # colon followed by the password (optional) - )? - (?P(;[^;]+)*)? # connection parameters block (optional) - @ - (?P\S+?) # non-greedy match of the host - (:(?P[0-9]+))? # colon followed by the port (optional) - / - (?P[^;]+) # path on the remote system, may be absolute or relative, - # and may include the use of '~' to reference the remote home - # directory - (?P(;[^;]+)*)? # parameters block (optional) - $ -''', re.VERBOSE) - -class SSH(Fetch): - '''Class to fetch a module or modules via Secure Shell''' - - def supports(self, url, urldata, d): - return __pattern__.match(url) != None - - def localpath(self, url, urldata, d): - m = __pattern__.match(url) - path = m.group('path') - host = m.group('host') - lpath = os.path.join(data.getVar('DL_DIR', d, True), host, os.path.basename(path)) - return lpath - - def go(self, url, urldata, d): - dldir = data.getVar('DL_DIR', d, 1) - - m = __pattern__.match(url) - path = m.group('path') - host = m.group('host') - port = m.group('port') - user = m.group('user') - password = m.group('pass') - - ldir = os.path.join(dldir, host) - lpath = os.path.join(ldir, os.path.basename(path)) - - if not os.path.exists(ldir): - os.makedirs(ldir) - - if port: - port = '-P %s' % port - else: - port = '' - - if user: - fr = user - if password: - fr += ':%s' % password - fr += '@%s' % host - else: - fr = host - fr += ':%s' % path - - - import commands - cmd = 'scp -B -r %s %s %s/' % ( - port, - commands.mkarg(fr), - commands.mkarg(ldir) - ) - - (exitstatus, output) = commands.getstatusoutput(cmd) - if exitstatus != 0: - print output - raise FetchError('Unable to fetch %s' % url) diff --git a/bitbake-dev/lib/bb/fetch/svk.py b/bitbake-dev/lib/bb/fetch/svk.py deleted file mode 100644 index 120dad9d4..000000000 --- a/bitbake-dev/lib/bb/fetch/svk.py +++ /dev/null @@ -1,109 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -This implementation is for svk. It is based on the svn implementation - -""" - -# Copyright (C) 2006 Holger Hans Peter Freyther -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import MissingParameterError - -class Svk(Fetch): - """Class to fetch a module or modules from svk repositories""" - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with svk. - """ - return ud.type in ['svk'] - - def localpath(self, url, ud, d): - if not "module" in ud.parm: - raise MissingParameterError("svk method needs a 'module' parameter") - else: - ud.module = ud.parm["module"] - - ud.revision = "" - if 'rev' in ud.parm: - ud.revision = ud.parm['rev'] - - ud.localfile = data.expand('%s_%s_%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision, ud.date), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def forcefetch(self, url, ud, d): - if (ud.date == "now"): - return True - return False - - def go(self, loc, ud, d): - """Fetch urls""" - - if not self.forcefetch(loc, ud, d) and Fetch.try_mirror(d, ud.localfile): - return - - svkroot = ud.host + ud.path - - svkcmd = "svk co -r {%s} %s/%s" % (ud.date, svkroot, ud.module) - - if ud.revision: - svkcmd = "svk co -r %s %s/%s" % (ud.revision, svkroot, ud.module) - - # create temp directory - localdata = data.createCopy(d) - data.update_data(localdata) - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: creating temporary directory") - bb.mkdirhier(data.expand('${WORKDIR}', localdata)) - data.setVar('TMPBASE', data.expand('${WORKDIR}/oesvk.XXXXXX', localdata), localdata) - tmppipe = os.popen(data.getVar('MKTEMPDIRCMD', localdata, 1) or "false") - tmpfile = tmppipe.readline().strip() - if not tmpfile: - bb.msg.error(bb.msg.domain.Fetcher, "Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.") - raise FetchError(ud.module) - - # check out sources there - os.chdir(tmpfile) - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % svkcmd) - myret = os.system(svkcmd) - if myret != 0: - try: - os.rmdir(tmpfile) - except OSError: - pass - raise FetchError(ud.module) - - os.chdir(os.path.join(tmpfile, os.path.dirname(ud.module))) - # tar them up to a defined filename - myret = os.system("tar -czf %s %s" % (ud.localpath, os.path.basename(ud.module))) - if myret != 0: - try: - os.unlink(ud.localpath) - except OSError: - pass - raise FetchError(ud.module) - # cleanup - os.system('rm -rf %s' % tmpfile) diff --git a/bitbake-dev/lib/bb/fetch/svn.py b/bitbake-dev/lib/bb/fetch/svn.py deleted file mode 100644 index eef9862a8..000000000 --- a/bitbake-dev/lib/bb/fetch/svn.py +++ /dev/null @@ -1,206 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementation for svn. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2004 Marcin Juszkiewicz -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import sys -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import MissingParameterError -from bb.fetch import runfetchcmd - -class Svn(Fetch): - """Class to fetch a module or modules from svn repositories""" - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with svn. - """ - return ud.type in ['svn'] - - def localpath(self, url, ud, d): - if not "module" in ud.parm: - raise MissingParameterError("svn method needs a 'module' parameter") - - ud.module = ud.parm["module"] - - # Create paths to svn checkouts - relpath = ud.path - if relpath.startswith('/'): - # Remove leading slash as os.path.join can't cope - relpath = relpath[1:] - ud.pkgdir = os.path.join(data.expand('${SVNDIR}', d), ud.host, relpath) - ud.moddir = os.path.join(ud.pkgdir, ud.module) - - if 'rev' in ud.parm: - ud.date = "" - ud.revision = ud.parm['rev'] - elif 'date' in ud.date: - ud.date = ud.parm['date'] - ud.revision = "" - else: - # - # ***Nasty hack*** - # If DATE in unexpanded PV, use ud.date (which is set from SRCDATE) - # Should warn people to switch to SRCREV here - # - pv = data.getVar("PV", d, 0) - if "DATE" in pv: - ud.revision = "" - else: - rev = Fetch.srcrev_internal_helper(ud, d) - if rev is True: - ud.revision = self.latest_revision(url, ud, d) - ud.date = "" - elif rev: - ud.revision = rev - ud.date = "" - else: - ud.revision = "" - - ud.localfile = data.expand('%s_%s_%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision, ud.date), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def _buildsvncommand(self, ud, d, command): - """ - Build up an svn commandline based on ud - command is "fetch", "update", "info" - """ - - basecmd = data.expand('${FETCHCMD_svn}', d) - - proto = "svn" - if "proto" in ud.parm: - proto = ud.parm["proto"] - - svn_rsh = None - if proto == "svn+ssh" and "rsh" in ud.parm: - svn_rsh = ud.parm["rsh"] - - svnroot = ud.host + ud.path - - # either use the revision, or SRCDATE in braces, - options = [] - - if ud.user: - options.append("--username %s" % ud.user) - - if ud.pswd: - options.append("--password %s" % ud.pswd) - - if command is "info": - svncmd = "%s info %s %s://%s/%s/" % (basecmd, " ".join(options), proto, svnroot, ud.module) - else: - suffix = "" - if ud.revision: - options.append("-r %s" % ud.revision) - suffix = "@%s" % (ud.revision) - elif ud.date: - options.append("-r {%s}" % ud.date) - - if command is "fetch": - svncmd = "%s co %s %s://%s/%s%s %s" % (basecmd, " ".join(options), proto, svnroot, ud.module, suffix, ud.module) - elif command is "update": - svncmd = "%s update %s" % (basecmd, " ".join(options)) - else: - raise FetchError("Invalid svn command %s" % command) - - if svn_rsh: - svncmd = "svn_RSH=\"%s\" %s" % (svn_rsh, svncmd) - - return svncmd - - def go(self, loc, ud, d): - """Fetch url""" - - # try to use the tarball stash - if Fetch.try_mirror(d, ud.localfile): - bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists or was mirrored, skipping svn checkout." % ud.localpath) - return - - bb.msg.debug(2, bb.msg.domain.Fetcher, "Fetch: checking for module directory '" + ud.moddir + "'") - - if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK): - svnupdatecmd = self._buildsvncommand(ud, d, "update") - bb.msg.note(1, bb.msg.domain.Fetcher, "Update " + loc) - # update sources there - os.chdir(ud.moddir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % svnupdatecmd) - runfetchcmd(svnupdatecmd, d) - else: - svnfetchcmd = self._buildsvncommand(ud, d, "fetch") - bb.msg.note(1, bb.msg.domain.Fetcher, "Fetch " + loc) - # check out sources there - bb.mkdirhier(ud.pkgdir) - os.chdir(ud.pkgdir) - bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % svnfetchcmd) - runfetchcmd(svnfetchcmd, d) - - os.chdir(ud.pkgdir) - # tar them up to a defined filename - try: - runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d) - except: - t, v, tb = sys.exc_info() - try: - os.unlink(ud.localpath) - except OSError: - pass - raise t, v, tb - - def suppports_srcrev(self): - return True - - def _revision_key(self, url, ud, d): - """ - Return a unique key for the url - """ - return "svn:" + ud.moddir - - def _latest_revision(self, url, ud, d): - """ - Return the latest upstream revision number - """ - bb.msg.debug(2, bb.msg.domain.Fetcher, "SVN fetcher hitting network for %s" % url) - - output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "info"), d, True) - - revision = None - for line in output.splitlines(): - if "Last Changed Rev" in line: - revision = line.split(":")[1].strip() - - return revision - - def _sortable_revision(self, url, ud, d): - """ - Return a sortable revision number which in our case is the revision number - """ - - return self._build_revision(url, ud, d) - - def _build_revision(self, url, ud, d): - return ud.revision diff --git a/bitbake-dev/lib/bb/fetch/wget.py b/bitbake-dev/lib/bb/fetch/wget.py deleted file mode 100644 index fd93c7ec4..000000000 --- a/bitbake-dev/lib/bb/fetch/wget.py +++ /dev/null @@ -1,130 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'Fetch' implementations - -Classes for obtaining upstream sources for the -BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -import os -import bb -from bb import data -from bb.fetch import Fetch -from bb.fetch import FetchError -from bb.fetch import uri_replace - -class Wget(Fetch): - """Class to fetch urls via 'wget'""" - def supports(self, url, ud, d): - """ - Check to see if a given url can be fetched with wget. - """ - return ud.type in ['http','https','ftp'] - - def localpath(self, url, ud, d): - - url = bb.encodeurl([ud.type, ud.host, ud.path, ud.user, ud.pswd, {}]) - ud.basename = os.path.basename(ud.path) - ud.localfile = data.expand(os.path.basename(url), d) - - return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) - - def go(self, uri, ud, d, checkonly = False): - """Fetch urls""" - - def fetch_uri(uri, ud, d): - if checkonly: - fetchcmd = data.getVar("CHECKCOMMAND", d, 1) - elif os.path.exists(ud.localpath): - # file exists, but we didnt complete it.. trying again.. - fetchcmd = data.getVar("RESUMECOMMAND", d, 1) - else: - fetchcmd = data.getVar("FETCHCOMMAND", d, 1) - - uri = uri.split(";")[0] - uri_decoded = list(bb.decodeurl(uri)) - uri_type = uri_decoded[0] - uri_host = uri_decoded[1] - - bb.msg.note(1, bb.msg.domain.Fetcher, "fetch " + uri) - fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0]) - fetchcmd = fetchcmd.replace("${FILE}", ud.basename) - httpproxy = None - ftpproxy = None - if uri_type == 'http': - httpproxy = data.getVar("HTTP_PROXY", d, True) - httpproxy_ignore = (data.getVar("HTTP_PROXY_IGNORE", d, True) or "").split() - for p in httpproxy_ignore: - if uri_host.endswith(p): - httpproxy = None - break - if uri_type == 'ftp': - ftpproxy = data.getVar("FTP_PROXY", d, True) - ftpproxy_ignore = (data.getVar("HTTP_PROXY_IGNORE", d, True) or "").split() - for p in ftpproxy_ignore: - if uri_host.endswith(p): - ftpproxy = None - break - if httpproxy: - fetchcmd = "http_proxy=" + httpproxy + " " + fetchcmd - if ftpproxy: - fetchcmd = "ftp_proxy=" + ftpproxy + " " + fetchcmd - bb.msg.debug(2, bb.msg.domain.Fetcher, "executing " + fetchcmd) - ret = os.system(fetchcmd) - if ret != 0: - return False - - # Sanity check since wget can pretend it succeed when it didn't - # Also, this used to happen if sourceforge sent us to the mirror page - if not os.path.exists(ud.localpath) and not checkonly: - bb.msg.debug(2, bb.msg.domain.Fetcher, "The fetch command for %s returned success but %s doesn't exist?..." % (uri, ud.localpath)) - return False - - return True - - localdata = data.createCopy(d) - data.setVar('OVERRIDES', "wget:" + data.getVar('OVERRIDES', localdata), localdata) - data.update_data(localdata) - - premirrors = [ i.split() for i in (data.getVar('PREMIRRORS', localdata, 1) or "").split('\n') if i ] - for (find, replace) in premirrors: - newuri = uri_replace(uri, find, replace, d) - if newuri != uri: - if fetch_uri(newuri, ud, localdata): - return True - - if fetch_uri(uri, ud, localdata): - return True - - # try mirrors - mirrors = [ i.split() for i in (data.getVar('MIRRORS', localdata, 1) or "").split('\n') if i ] - for (find, replace) in mirrors: - newuri = uri_replace(uri, find, replace, d) - if newuri != uri: - if fetch_uri(newuri, ud, localdata): - return True - - raise FetchError(uri) - - - def checkstatus(self, uri, ud, d): - return self.go(uri, ud, d, True) diff --git a/bitbake-dev/lib/bb/manifest.py b/bitbake-dev/lib/bb/manifest.py deleted file mode 100644 index 4e4b7d98e..000000000 --- a/bitbake-dev/lib/bb/manifest.py +++ /dev/null @@ -1,144 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2003, 2004 Chris Larson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import os, sys -import bb, bb.data - -def getfields(line): - fields = {} - fieldmap = ( "pkg", "src", "dest", "type", "mode", "uid", "gid", "major", "minor", "start", "inc", "count" ) - for f in xrange(len(fieldmap)): - fields[fieldmap[f]] = None - - if not line: - return None - - splitline = line.split() - if not len(splitline): - return None - - try: - for f in xrange(len(fieldmap)): - if splitline[f] == '-': - continue - fields[fieldmap[f]] = splitline[f] - except IndexError: - pass - return fields - -def parse (mfile, d): - manifest = [] - while 1: - line = mfile.readline() - if not line: - break - if line.startswith("#"): - continue - fields = getfields(line) - if not fields: - continue - manifest.append(fields) - return manifest - -def emit (func, manifest, d): -#str = "%s () {\n" % func - str = "" - for line in manifest: - emittedline = emit_line(func, line, d) - if not emittedline: - continue - str += emittedline + "\n" -# str += "}\n" - return str - -def mangle (func, line, d): - import copy - newline = copy.copy(line) - src = bb.data.expand(newline["src"], d) - - if src: - if not os.path.isabs(src): - src = "${WORKDIR}/" + src - - dest = newline["dest"] - if not dest: - return - - if dest.startswith("/"): - dest = dest[1:] - - if func is "do_install": - dest = "${D}/" + dest - - elif func is "do_populate": - dest = "${WORKDIR}/install/" + newline["pkg"] + "/" + dest - - elif func is "do_stage": - varmap = {} - varmap["${bindir}"] = "${STAGING_DIR}/${HOST_SYS}/bin" - varmap["${libdir}"] = "${STAGING_DIR}/${HOST_SYS}/lib" - varmap["${includedir}"] = "${STAGING_DIR}/${HOST_SYS}/include" - varmap["${datadir}"] = "${STAGING_DATADIR}" - - matched = 0 - for key in varmap.keys(): - if dest.startswith(key): - dest = varmap[key] + "/" + dest[len(key):] - matched = 1 - if not matched: - newline = None - return - else: - newline = None - return - - newline["src"] = src - newline["dest"] = dest - return newline - -def emit_line (func, line, d): - import copy - newline = copy.deepcopy(line) - newline = mangle(func, newline, d) - if not newline: - return None - - str = "" - type = newline["type"] - mode = newline["mode"] - src = newline["src"] - dest = newline["dest"] - if type is "d": - str = "install -d " - if mode: - str += "-m %s " % mode - str += dest - elif type is "f": - if not src: - return None - if dest.endswith("/"): - str = "install -d " - str += dest + "\n" - str += "install " - else: - str = "install -D " - if mode: - str += "-m %s " % mode - str += src + " " + dest - del newline - return str diff --git a/bitbake-dev/lib/bb/methodpool.py b/bitbake-dev/lib/bb/methodpool.py deleted file mode 100644 index f43c4a058..000000000 --- a/bitbake-dev/lib/bb/methodpool.py +++ /dev/null @@ -1,84 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# -# Copyright (C) 2006 Holger Hans Peter Freyther -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -""" - What is a method pool? - - BitBake has a global method scope where .bb, .inc and .bbclass - files can install methods. These methods are parsed from strings. - To avoid recompiling and executing these string we introduce - a method pool to do this task. - - This pool will be used to compile and execute the functions. It - will be smart enough to -""" - -from bb.utils import better_compile, better_exec -from bb import error - -# A dict of modules we have handled -# it is the number of .bbclasses + x in size -_parsed_methods = { } -_parsed_fns = { } - -def insert_method(modulename, code, fn): - """ - Add code of a module should be added. The methods - will be simply added, no checking will be done - """ - comp = better_compile(code, "", fn ) - better_exec(comp, __builtins__, code, fn) - - # now some instrumentation - code = comp.co_names - for name in code: - if name in ['None', 'False']: - continue - elif name in _parsed_fns and not _parsed_fns[name] == modulename: - error( "Error Method already seen: %s in' %s' now in '%s'" % (name, _parsed_fns[name], modulename)) - else: - _parsed_fns[name] = modulename - -def check_insert_method(modulename, code, fn): - """ - Add the code if it wasnt added before. The module - name will be used for that - - Variables: - @modulename a short name e.g. base.bbclass - @code The actual python code - @fn The filename from the outer file - """ - if not modulename in _parsed_methods: - return insert_method(modulename, code, fn) - _parsed_methods[modulename] = 1 - -def parsed_module(modulename): - """ - Inform me file xyz was parsed - """ - return modulename in _parsed_methods - - -def get_parsed_dict(): - """ - shortcut - """ - return _parsed_methods diff --git a/bitbake-dev/lib/bb/msg.py b/bitbake-dev/lib/bb/msg.py deleted file mode 100644 index 3fcf7091b..000000000 --- a/bitbake-dev/lib/bb/msg.py +++ /dev/null @@ -1,125 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'msg' implementation - -Message handling infrastructure for bitbake - -""" - -# Copyright (C) 2006 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import sys, bb -from bb import event - -debug_level = {} - -verbose = False - -domain = bb.utils.Enum( - 'Build', - 'Cache', - 'Collection', - 'Data', - 'Depends', - 'Fetcher', - 'Parsing', - 'PersistData', - 'Provider', - 'RunQueue', - 'TaskData', - 'Util') - - -class MsgBase(bb.event.Event): - """Base class for messages""" - - def __init__(self, msg): - self._message = msg - event.Event.__init__(self) - -class MsgDebug(MsgBase): - """Debug Message""" - -class MsgNote(MsgBase): - """Note Message""" - -class MsgWarn(MsgBase): - """Warning Message""" - -class MsgError(MsgBase): - """Error Message""" - -class MsgFatal(MsgBase): - """Fatal Message""" - -class MsgPlain(MsgBase): - """General output""" - -# -# Message control functions -# - -def set_debug_level(level): - bb.msg.debug_level = {} - for domain in bb.msg.domain: - bb.msg.debug_level[domain] = level - bb.msg.debug_level['default'] = level - -def set_verbose(level): - bb.msg.verbose = level - -def set_debug_domains(domains): - for domain in domains: - found = False - for ddomain in bb.msg.domain: - if domain == str(ddomain): - bb.msg.debug_level[ddomain] = bb.msg.debug_level[ddomain] + 1 - found = True - if not found: - bb.msg.warn(None, "Logging domain %s is not valid, ignoring" % domain) - -# -# Message handling functions -# - -def debug(level, domain, msg, fn = None): - if not domain: - domain = 'default' - if debug_level[domain] >= level: - bb.event.fire(MsgDebug(msg), None) - -def note(level, domain, msg, fn = None): - if not domain: - domain = 'default' - if level == 1 or verbose or debug_level[domain] >= 1: - bb.event.fire(MsgNote(msg), None) - -def warn(domain, msg, fn = None): - bb.event.fire(MsgWarn(msg), None) - -def error(domain, msg, fn = None): - bb.event.fire(MsgError(msg), None) - print 'ERROR: ' + msg - -def fatal(domain, msg, fn = None): - bb.event.fire(MsgFatal(msg), None) - print 'FATAL: ' + msg - sys.exit(1) - -def plain(msg, fn = None): - bb.event.fire(MsgPlain(msg), None) - diff --git a/bitbake-dev/lib/bb/parse/__init__.py b/bitbake-dev/lib/bb/parse/__init__.py deleted file mode 100644 index 5dd96c413..000000000 --- a/bitbake-dev/lib/bb/parse/__init__.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -BitBake Parsers - -File parsers for the BitBake build tools. - -""" - - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig - -__all__ = [ 'ParseError', 'SkipPackage', 'cached_mtime', 'mark_dependency', - 'supports', 'handle', 'init' ] -handlers = [] - -import bb, os - -class ParseError(Exception): - """Exception raised when parsing fails""" - -class SkipPackage(Exception): - """Exception raised to skip this package""" - -__mtime_cache = {} -def cached_mtime(f): - if not __mtime_cache.has_key(f): - __mtime_cache[f] = os.stat(f)[8] - return __mtime_cache[f] - -def cached_mtime_noerror(f): - if not __mtime_cache.has_key(f): - try: - __mtime_cache[f] = os.stat(f)[8] - except OSError: - return 0 - return __mtime_cache[f] - -def update_mtime(f): - __mtime_cache[f] = os.stat(f)[8] - return __mtime_cache[f] - -def mark_dependency(d, f): - if f.startswith('./'): - f = "%s/%s" % (os.getcwd(), f[2:]) - deps = bb.data.getVar('__depends', d) or [] - deps.append( (f, cached_mtime(f)) ) - bb.data.setVar('__depends', deps, d) - -def supports(fn, data): - """Returns true if we have a handler for this file, false otherwise""" - for h in handlers: - if h['supports'](fn, data): - return 1 - return 0 - -def handle(fn, data, include = 0): - """Call the handler that is appropriate for this file""" - for h in handlers: - if h['supports'](fn, data): - return h['handle'](fn, data, include) - raise ParseError("%s is not a BitBake file" % fn) - -def init(fn, data): - for h in handlers: - if h['supports'](fn): - return h['init'](data) - - -from parse_py import __version__, ConfHandler, BBHandler diff --git a/bitbake-dev/lib/bb/parse/parse_py/BBHandler.py b/bitbake-dev/lib/bb/parse/parse_py/BBHandler.py deleted file mode 100644 index 86fa18ebd..000000000 --- a/bitbake-dev/lib/bb/parse/parse_py/BBHandler.py +++ /dev/null @@ -1,410 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" - class for handling .bb files - - Reads a .bb file and obtains its metadata - -""" - - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import re, bb, os, sys, time, string -import bb.fetch, bb.build, bb.utils -from bb import data, fetch, methodpool - -from ConfHandler import include, localpath, obtain, init -from bb.parse import ParseError - -__func_start_regexp__ = re.compile( r"(((?Ppython)|(?Pfakeroot))\s*)*(?P[\w\.\-\+\{\}\$]+)?\s*\(\s*\)\s*{$" ) -__inherit_regexp__ = re.compile( r"inherit\s+(.+)" ) -__export_func_regexp__ = re.compile( r"EXPORT_FUNCTIONS\s+(.+)" ) -__addtask_regexp__ = re.compile("addtask\s+(?P\w+)\s*((before\s*(?P((.*(?=after))|(.*))))|(after\s*(?P((.*(?=before))|(.*)))))*") -__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" ) -__def_regexp__ = re.compile( r"def\s+(\w+).*:" ) -__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" ) -__word__ = re.compile(r"\S+") - -__infunc__ = "" -__inpython__ = False -__body__ = [] -__classname__ = "" -classes = [ None, ] - -# We need to indicate EOF to the feeder. This code is so messy that -# factoring it out to a close_parse_file method is out of question. -# We will use the IN_PYTHON_EOF as an indicator to just close the method -# -# The two parts using it are tightly integrated anyway -IN_PYTHON_EOF = -9999999999999 - -__parsed_methods__ = methodpool.get_parsed_dict() - -def supports(fn, d): - localfn = localpath(fn, d) - return localfn[-3:] == ".bb" or localfn[-8:] == ".bbclass" or localfn[-4:] == ".inc" - -def inherit(files, d): - __inherit_cache = data.getVar('__inherit_cache', d) or [] - fn = "" - lineno = 0 - files = data.expand(files, d) - for file in files: - if file[0] != "/" and file[-8:] != ".bbclass": - file = os.path.join('classes', '%s.bbclass' % file) - - if not file in __inherit_cache: - bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s:%d: inheriting %s" % (fn, lineno, file)) - __inherit_cache.append( file ) - data.setVar('__inherit_cache', __inherit_cache, d) - include(fn, file, d, "inherit") - __inherit_cache = data.getVar('__inherit_cache', d) or [] - - -def finalise(fn, d): - data.expandKeys(d) - data.update_data(d) - anonqueue = data.getVar("__anonqueue", d, 1) or [] - body = [x['content'] for x in anonqueue] - flag = { 'python' : 1, 'func' : 1 } - data.setVar("__anonfunc", "\n".join(body), d) - data.setVarFlags("__anonfunc", flag, d) - from bb import build - try: - t = data.getVar('T', d) - data.setVar('T', '${TMPDIR}/anonfunc/', d) - anonfuncs = data.getVar('__BBANONFUNCS', d) or [] - code = "" - for f in anonfuncs: - code = code + " %s(d)\n" % f - data.setVar("__anonfunc", code, d) - build.exec_func("__anonfunc", d) - data.delVar('T', d) - if t: - data.setVar('T', t, d) - except Exception, e: - bb.msg.debug(1, bb.msg.domain.Parsing, "Exception when executing anonymous function: %s" % e) - raise - data.delVar("__anonqueue", d) - data.delVar("__anonfunc", d) - data.update_data(d) - - all_handlers = {} - for var in data.getVar('__BBHANDLERS', d) or []: - # try to add the handler - handler = data.getVar(var,d) - bb.event.register(var, handler) - - tasklist = data.getVar('__BBTASKS', d) or [] - bb.build.add_tasks(tasklist, d) - - bb.event.fire(bb.event.RecipeParsed(fn), d) - - -def handle(fn, d, include = 0): - global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__ - __body__ = [] - __infunc__ = "" - __classname__ = "" - __residue__ = [] - - if include == 0: - bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data)") - else: - bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data, include)") - - (root, ext) = os.path.splitext(os.path.basename(fn)) - base_name = "%s%s" % (root,ext) - init(d) - - if ext == ".bbclass": - __classname__ = root - classes.append(__classname__) - __inherit_cache = data.getVar('__inherit_cache', d) or [] - if not fn in __inherit_cache: - __inherit_cache.append(fn) - data.setVar('__inherit_cache', __inherit_cache, d) - - if include != 0: - oldfile = data.getVar('FILE', d) - else: - oldfile = None - - fn = obtain(fn, d) - bbpath = (data.getVar('BBPATH', d, 1) or '').split(':') - if not os.path.isabs(fn): - f = None - for p in bbpath: - j = os.path.join(p, fn) - if os.access(j, os.R_OK): - abs_fn = j - f = open(j, 'r') - break - if f is None: - raise IOError("file %s not found" % fn) - else: - f = open(fn,'r') - abs_fn = fn - - if include: - bb.parse.mark_dependency(d, abs_fn) - - if ext != ".bbclass": - data.setVar('FILE', fn, d) - - lineno = 0 - while 1: - lineno = lineno + 1 - s = f.readline() - if not s: break - s = s.rstrip() - feeder(lineno, s, fn, base_name, d) - if __inpython__: - # add a blank line to close out any python definition - feeder(IN_PYTHON_EOF, "", fn, base_name, d) - if ext == ".bbclass": - classes.remove(__classname__) - else: - if include == 0: - multi = data.getVar('BBCLASSEXTEND', d, 1) - if multi: - based = bb.data.createCopy(d) - else: - based = d - try: - finalise(fn, based) - except bb.parse.SkipPackage: - bb.data.setVar("__SKIPPED", True, based) - darray = {"": based} - - for cls in (multi or "").split(): - pn = data.getVar('PN', d, True) - based = bb.data.createCopy(d) - data.setVar('PN', pn + '-' + cls, based) - inherit([cls], based) - try: - finalise(fn, based) - except bb.parse.SkipPackage: - bb.data.setVar("__SKIPPED", True, based) - darray[cls] = based - return darray - - bbpath.pop(0) - if oldfile: - bb.data.setVar("FILE", oldfile, d) - - # we have parsed the bb class now - if ext == ".bbclass" or ext == ".inc": - __parsed_methods__[base_name] = 1 - - return d - -def feeder(lineno, s, fn, root, d): - global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__,__infunc__, __body__, classes, bb, __residue__ - if __infunc__: - if s == '}': - __body__.append('') - if __infunc__ == "__anonymous": - funcname = ("__anon_%s_%s" % (lineno, fn.translate(string.maketrans('/.+-', '____')))) - if not funcname in methodpool._parsed_fns: - text = "def %s(d):\n" % (funcname) + '\n'.join(__body__) - methodpool.insert_method(funcname, text, fn) - anonfuncs = data.getVar('__BBANONFUNCS', d) or [] - anonfuncs.append(funcname) - data.setVar('__BBANONFUNCS', anonfuncs, d) - else: - data.setVarFlag(__infunc__, "func", 1, d) - data.setVar(__infunc__, '\n'.join(__body__), d) - __infunc__ = "" - __body__ = [] - else: - __body__.append(s) - return - - if __inpython__: - m = __python_func_regexp__.match(s) - if m and lineno != IN_PYTHON_EOF: - __body__.append(s) - return - else: - # Note we will add root to parsedmethods after having parse - # 'this' file. This means we will not parse methods from - # bb classes twice - if not root in __parsed_methods__: - text = '\n'.join(__body__) - methodpool.insert_method( root, text, fn ) - __body__ = [] - __inpython__ = False - - if lineno == IN_PYTHON_EOF: - return - -# fall through - - if s == '' or s[0] == '#': return # skip comments and empty lines - - if s[-1] == '\\': - __residue__.append(s[:-1]) - return - - s = "".join(__residue__) + s - __residue__ = [] - - m = __func_start_regexp__.match(s) - if m: - __infunc__ = m.group("func") or "__anonymous" - key = __infunc__ - if data.getVar(key, d): -# clean up old version of this piece of metadata, as its -# flags could cause problems - data.setVarFlag(key, 'python', None, d) - data.setVarFlag(key, 'fakeroot', None, d) - if m.group("py") is not None: - data.setVarFlag(key, "python", "1", d) - else: - data.delVarFlag(key, "python", d) - if m.group("fr") is not None: - data.setVarFlag(key, "fakeroot", "1", d) - else: - data.delVarFlag(key, "fakeroot", d) - return - - m = __def_regexp__.match(s) - if m: - __body__.append(s) - __inpython__ = True - return - - m = __export_func_regexp__.match(s) - if m: - fns = m.group(1) - n = __word__.findall(fns) - for f in n: - allvars = [] - allvars.append(f) - allvars.append(classes[-1] + "_" + f) - - vars = [[ allvars[0], allvars[1] ]] - if len(classes) > 1 and classes[-2] is not None: - allvars.append(classes[-2] + "_" + f) - vars = [] - vars.append([allvars[2], allvars[1]]) - vars.append([allvars[0], allvars[2]]) - - for (var, calledvar) in vars: - if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d): - continue - - if data.getVar(var, d): - data.setVarFlag(var, 'python', None, d) - data.setVarFlag(var, 'func', None, d) - - for flag in [ "func", "python" ]: - if data.getVarFlag(calledvar, flag, d): - data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d) - for flag in [ "dirs" ]: - if data.getVarFlag(var, flag, d): - data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d) - - if data.getVarFlag(calledvar, "python", d): - data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d) - else: - data.setVar(var, "\t" + calledvar + "\n", d) - data.setVarFlag(var, 'export_func', '1', d) - - return - - m = __addtask_regexp__.match(s) - if m: - func = m.group("func") - before = m.group("before") - after = m.group("after") - if func is None: - return - if func[:3] != "do_": - var = "do_" + func - - data.setVarFlag(var, "task", 1, d) - - bbtasks = data.getVar('__BBTASKS', d) or [] - if not var in bbtasks: - bbtasks.append(var) - data.setVar('__BBTASKS', bbtasks, d) - - existing = data.getVarFlag(var, "deps", d) or [] - if after is not None: - # set up deps for function - for entry in after.split(): - if entry not in existing: - existing.append(entry) - data.setVarFlag(var, "deps", existing, d) - if before is not None: - # set up things that depend on this func - for entry in before.split(): - existing = data.getVarFlag(entry, "deps", d) or [] - if var not in existing: - data.setVarFlag(entry, "deps", [var] + existing, d) - return - - m = __addhandler_regexp__.match(s) - if m: - fns = m.group(1) - hs = __word__.findall(fns) - bbhands = data.getVar('__BBHANDLERS', d) or [] - for h in hs: - bbhands.append(h) - data.setVarFlag(h, "handler", 1, d) - data.setVar('__BBHANDLERS', bbhands, d) - return - - m = __inherit_regexp__.match(s) - if m: - - files = m.group(1) - n = __word__.findall(files) - inherit(n, d) - return - - from bb.parse import ConfHandler - return ConfHandler.feeder(lineno, s, fn, d) - -__pkgsplit_cache__={} -def vars_from_file(mypkg, d): - if not mypkg: - return (None, None, None) - if mypkg in __pkgsplit_cache__: - return __pkgsplit_cache__[mypkg] - - myfile = os.path.splitext(os.path.basename(mypkg)) - parts = myfile[0].split('_') - __pkgsplit_cache__[mypkg] = parts - if len(parts) > 3: - raise ParseError("Unable to generate default variables from the filename: %s (too many underscores)" % mypkg) - exp = 3 - len(parts) - tmplist = [] - while exp != 0: - exp -= 1 - tmplist.append(None) - parts.extend(tmplist) - return parts - -# Add us to the handlers list -from bb.parse import handlers -handlers.append({'supports': supports, 'handle': handle, 'init': init}) -del handlers diff --git a/bitbake-dev/lib/bb/parse/parse_py/ConfHandler.py b/bitbake-dev/lib/bb/parse/parse_py/ConfHandler.py deleted file mode 100644 index 23316ada5..000000000 --- a/bitbake-dev/lib/bb/parse/parse_py/ConfHandler.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" - class for handling configuration data files - - Reads a .conf file and obtains its metadata - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import re, bb.data, os, sys -from bb.parse import ParseError - -#__config_regexp__ = re.compile( r"(?Pexport\s*)?(?P[a-zA-Z0-9\-_+.${}]+)\s*(?P:)?(?P\?)?=\s*(?P['\"]?)(?P.*)(?P=apo)$") -__config_regexp__ = re.compile( r"(?Pexport\s*)?(?P[a-zA-Z0-9\-_+.${}/]+)(\[(?P[a-zA-Z0-9\-_+.]+)\])?\s*((?P:=)|(?P\?=)|(?P\+=)|(?P=\+)|(?P=\.)|(?P\.=)|=)\s*(?P['\"]?)(?P.*)(?P=apo)$") -__include_regexp__ = re.compile( r"include\s+(.+)" ) -__require_regexp__ = re.compile( r"require\s+(.+)" ) -__export_regexp__ = re.compile( r"export\s+(.+)" ) - -def init(data): - topdir = bb.data.getVar('TOPDIR', data) - if not topdir: - topdir = os.getcwd() - bb.data.setVar('TOPDIR', topdir, data) - if not bb.data.getVar('BBPATH', data): - from pkg_resources import Requirement, resource_filename - bitbake = Requirement.parse("bitbake") - datadir = resource_filename(bitbake, "../share/bitbake") - basedir = resource_filename(bitbake, "..") - bb.data.setVar('BBPATH', '%s:%s:%s' % (topdir, datadir, basedir), data) - - -def supports(fn, d): - return localpath(fn, d)[-5:] == ".conf" - -def localpath(fn, d): - if os.path.exists(fn): - return fn - - if "://" not in fn: - return fn - - localfn = None - try: - localfn = bb.fetch.localpath(fn, d, False) - except bb.MalformedUrl: - pass - - if not localfn: - return fn - return localfn - -def obtain(fn, data): - import sys, bb - fn = bb.data.expand(fn, data) - localfn = bb.data.expand(localpath(fn, data), data) - - if localfn != fn: - dldir = bb.data.getVar('DL_DIR', data, 1) - if not dldir: - bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: DL_DIR not defined") - return localfn - bb.mkdirhier(dldir) - try: - bb.fetch.init([fn], data) - except bb.fetch.NoMethodError: - (type, value, traceback) = sys.exc_info() - bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: no method: %s" % value) - return localfn - - try: - bb.fetch.go(data) - except bb.fetch.MissingParameterError: - (type, value, traceback) = sys.exc_info() - bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: missing parameters: %s" % value) - return localfn - except bb.fetch.FetchError: - (type, value, traceback) = sys.exc_info() - bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: failed: %s" % value) - return localfn - return localfn - - -def include(oldfn, fn, data, error_out): - """ - - error_out If True a ParseError will be reaised if the to be included - """ - if oldfn == fn: # prevent infinate recursion - return None - - import bb - fn = bb.data.expand(fn, data) - oldfn = bb.data.expand(oldfn, data) - - if not os.path.isabs(fn): - dname = os.path.dirname(oldfn) - bbpath = "%s:%s" % (dname, bb.data.getVar("BBPATH", data, 1)) - abs_fn = bb.which(bbpath, fn) - if abs_fn: - fn = abs_fn - - from bb.parse import handle - try: - ret = handle(fn, data, True) - except IOError: - if error_out: - raise ParseError("Could not %(error_out)s file %(fn)s" % vars() ) - bb.msg.debug(2, bb.msg.domain.Parsing, "CONF file '%s' not found" % fn) - -def handle(fn, data, include = 0): - if include: - inc_string = "including" - else: - inc_string = "reading" - init(data) - - if include == 0: - oldfile = None - else: - oldfile = bb.data.getVar('FILE', data) - - fn = obtain(fn, data) - if not os.path.isabs(fn): - f = None - bbpath = bb.data.getVar("BBPATH", data, 1) or [] - for p in bbpath.split(":"): - currname = os.path.join(p, fn) - if os.access(currname, os.R_OK): - f = open(currname, 'r') - abs_fn = currname - bb.msg.debug(2, bb.msg.domain.Parsing, "CONF %s %s" % (inc_string, currname)) - break - if f is None: - raise IOError("file '%s' not found" % fn) - else: - f = open(fn,'r') - bb.msg.debug(1, bb.msg.domain.Parsing, "CONF %s %s" % (inc_string,fn)) - abs_fn = fn - - if include: - bb.parse.mark_dependency(data, abs_fn) - - lineno = 0 - bb.data.setVar('FILE', fn, data) - while 1: - lineno = lineno + 1 - s = f.readline() - if not s: break - w = s.strip() - if not w: continue # skip empty lines - s = s.rstrip() - if s[0] == '#': continue # skip comments - while s[-1] == '\\': - s2 = f.readline()[:-1].strip() - lineno = lineno + 1 - s = s[:-1] + s2 - feeder(lineno, s, fn, data) - - if oldfile: - bb.data.setVar('FILE', oldfile, data) - return data - -def feeder(lineno, s, fn, data): - def getFunc(groupd, key, data): - if 'flag' in groupd and groupd['flag'] != None: - return bb.data.getVarFlag(key, groupd['flag'], data) - else: - return bb.data.getVar(key, data) - - m = __config_regexp__.match(s) - if m: - groupd = m.groupdict() - key = groupd["var"] - if "exp" in groupd and groupd["exp"] != None: - bb.data.setVarFlag(key, "export", 1, data) - if "ques" in groupd and groupd["ques"] != None: - val = getFunc(groupd, key, data) - if val == None: - val = groupd["value"] - elif "colon" in groupd and groupd["colon"] != None: - e = data.createCopy() - bb.data.update_data(e) - val = bb.data.expand(groupd["value"], e) - elif "append" in groupd and groupd["append"] != None: - val = "%s %s" % ((getFunc(groupd, key, data) or ""), groupd["value"]) - elif "prepend" in groupd and groupd["prepend"] != None: - val = "%s %s" % (groupd["value"], (getFunc(groupd, key, data) or "")) - elif "postdot" in groupd and groupd["postdot"] != None: - val = "%s%s" % ((getFunc(groupd, key, data) or ""), groupd["value"]) - elif "predot" in groupd and groupd["predot"] != None: - val = "%s%s" % (groupd["value"], (getFunc(groupd, key, data) or "")) - else: - val = groupd["value"] - if 'flag' in groupd and groupd['flag'] != None: - bb.msg.debug(3, bb.msg.domain.Parsing, "setVarFlag(%s, %s, %s, data)" % (key, groupd['flag'], val)) - bb.data.setVarFlag(key, groupd['flag'], val, data) - else: - bb.data.setVar(key, val, data) - return - - m = __include_regexp__.match(s) - if m: - s = bb.data.expand(m.group(1), data) - bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (fn, lineno, s)) - include(fn, s, data, False) - return - - m = __require_regexp__.match(s) - if m: - s = bb.data.expand(m.group(1), data) - include(fn, s, data, "include required") - return - - m = __export_regexp__.match(s) - if m: - bb.data.setVarFlag(m.group(1), "export", 1, data) - return - - raise ParseError("%s:%d: unparsed line: '%s'" % (fn, lineno, s)); - -# Add us to the handlers list -from bb.parse import handlers -handlers.append({'supports': supports, 'handle': handle, 'init': init}) -del handlers diff --git a/bitbake-dev/lib/bb/parse/parse_py/__init__.py b/bitbake-dev/lib/bb/parse/parse_py/__init__.py deleted file mode 100644 index 9e0e00add..000000000 --- a/bitbake-dev/lib/bb/parse/parse_py/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake Parsers - -File parsers for the BitBake build tools. - -""" - -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Based on functions from the base bb module, Copyright 2003 Holger Schurig -__version__ = '1.0' - -__all__ = [ 'ConfHandler', 'BBHandler'] - -import ConfHandler -import BBHandler diff --git a/bitbake-dev/lib/bb/persist_data.py b/bitbake-dev/lib/bb/persist_data.py deleted file mode 100644 index bc4045fe8..000000000 --- a/bitbake-dev/lib/bb/persist_data.py +++ /dev/null @@ -1,121 +0,0 @@ -# BitBake Persistent Data Store -# -# Copyright (C) 2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import bb, os - -try: - import sqlite3 -except ImportError: - try: - from pysqlite2 import dbapi2 as sqlite3 - except ImportError: - bb.msg.fatal(bb.msg.domain.PersistData, "Importing sqlite3 and pysqlite2 failed, please install one of them. Python 2.5 or a 'python-pysqlite2' like package is likely to be what you need.") - -sqlversion = sqlite3.sqlite_version_info -if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3): - bb.msg.fatal(bb.msg.domain.PersistData, "sqlite3 version 3.3.0 or later is required.") - -class PersistData: - """ - BitBake Persistent Data Store - - Used to store data in a central location such that other threads/tasks can - access them at some future date. - - The "domain" is used as a key to isolate each data pool and in this - implementation corresponds to an SQL table. The SQL table consists of a - simple key and value pair. - - Why sqlite? It handles all the locking issues for us. - """ - def __init__(self, d): - self.cachedir = bb.data.getVar("PERSISTENT_DIR", d, True) or bb.data.getVar("CACHE", d, True) - if self.cachedir in [None, '']: - bb.msg.fatal(bb.msg.domain.PersistData, "Please set the 'PERSISTENT_DIR' or 'CACHE' variable.") - try: - os.stat(self.cachedir) - except OSError: - bb.mkdirhier(self.cachedir) - - self.cachefile = os.path.join(self.cachedir,"bb_persist_data.sqlite3") - bb.msg.debug(1, bb.msg.domain.PersistData, "Using '%s' as the persistent data cache" % self.cachefile) - - self.connection = sqlite3.connect(self.cachefile, timeout=5, isolation_level=None) - - def addDomain(self, domain): - """ - Should be called before any domain is used - Creates it if it doesn't exist. - """ - self.connection.execute("CREATE TABLE IF NOT EXISTS %s(key TEXT, value TEXT);" % domain) - - def delDomain(self, domain): - """ - Removes a domain and all the data it contains - """ - self.connection.execute("DROP TABLE IF EXISTS %s;" % domain) - - def getKeyValues(self, domain): - """ - Return a list of key + value pairs for a domain - """ - ret = {} - data = self.connection.execute("SELECT key, value from %s;" % domain) - for row in data: - ret[str(row[0])] = str(row[1]) - - return ret - - def getValue(self, domain, key): - """ - Return the value of a key for a domain - """ - data = self.connection.execute("SELECT * from %s where key=?;" % domain, [key]) - for row in data: - return row[1] - - def setValue(self, domain, key, value): - """ - Sets the value of a key for a domain - """ - data = self.connection.execute("SELECT * from %s where key=?;" % domain, [key]) - rows = 0 - for row in data: - rows = rows + 1 - if rows: - self._execute("UPDATE %s SET value=? WHERE key=?;" % domain, [value, key]) - else: - self._execute("INSERT into %s(key, value) values (?, ?);" % domain, [key, value]) - - def delValue(self, domain, key): - """ - Deletes a key/value pair - """ - self._execute("DELETE from %s where key=?;" % domain, [key]) - - def _execute(self, *query): - while True: - try: - self.connection.execute(*query) - return - except sqlite3.OperationalError, e: - if 'database is locked' in str(e): - continue - raise - - - diff --git a/bitbake-dev/lib/bb/providers.py b/bitbake-dev/lib/bb/providers.py deleted file mode 100644 index 8617251ca..000000000 --- a/bitbake-dev/lib/bb/providers.py +++ /dev/null @@ -1,327 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2003, 2004 Chris Larson -# Copyright (C) 2003, 2004 Phil Blundell -# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer -# Copyright (C) 2005 Holger Hans Peter Freyther -# Copyright (C) 2005 ROAD GmbH -# Copyright (C) 2006 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import re -from bb import data, utils -import bb - -class NoProvider(Exception): - """Exception raised when no provider of a build dependency can be found""" - -class NoRProvider(Exception): - """Exception raised when no provider of a runtime dependency can be found""" - - -def sortPriorities(pn, dataCache, pkg_pn = None): - """ - Reorder pkg_pn by file priority and default preference - """ - - if not pkg_pn: - pkg_pn = dataCache.pkg_pn - - files = pkg_pn[pn] - priorities = {} - for f in files: - priority = dataCache.bbfile_priority[f] - preference = dataCache.pkg_dp[f] - if priority not in priorities: - priorities[priority] = {} - if preference not in priorities[priority]: - priorities[priority][preference] = [] - priorities[priority][preference].append(f) - pri_list = priorities.keys() - pri_list.sort(lambda a, b: a - b) - tmp_pn = [] - for pri in pri_list: - pref_list = priorities[pri].keys() - pref_list.sort(lambda a, b: b - a) - tmp_pref = [] - for pref in pref_list: - tmp_pref.extend(priorities[pri][pref]) - tmp_pn = [tmp_pref] + tmp_pn - - return tmp_pn - -def preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r): - """ - Check if the version pe,pv,pr is the preferred one. - If there is preferred version defined and ends with '%', then pv has to start with that version after removing the '%' - """ - if (pr == preferred_r or preferred_r == None): - if (pe == preferred_e or preferred_e == None): - if preferred_v == pv: - return True - if preferred_v != None and preferred_v.endswith('%') and pv.startswith(preferred_v[:len(preferred_v)-1]): - return True - return False - -def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None): - """ - Find the first provider in pkg_pn with a PREFERRED_VERSION set. - """ - - preferred_file = None - preferred_ver = None - - localdata = data.createCopy(cfgData) - bb.data.setVar('OVERRIDES', "pn-%s:%s:%s" % (pn, pn, data.getVar('OVERRIDES', localdata)), localdata) - bb.data.update_data(localdata) - - preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True) - if preferred_v: - m = re.match('(\d+:)*(.*)(_.*)*', preferred_v) - if m: - if m.group(1): - preferred_e = int(m.group(1)[:-1]) - else: - preferred_e = None - preferred_v = m.group(2) - if m.group(3): - preferred_r = m.group(3)[1:] - else: - preferred_r = None - else: - preferred_e = None - preferred_r = None - - for file_set in pkg_pn: - for f in file_set: - pe,pv,pr = dataCache.pkg_pepvpr[f] - if preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r): - preferred_file = f - preferred_ver = (pe, pv, pr) - break - if preferred_file: - break; - if preferred_r: - pv_str = '%s-%s' % (preferred_v, preferred_r) - else: - pv_str = preferred_v - if not (preferred_e is None): - pv_str = '%s:%s' % (preferred_e, pv_str) - itemstr = "" - if item: - itemstr = " (for item %s)" % item - if preferred_file is None: - bb.msg.note(1, bb.msg.domain.Provider, "preferred version %s of %s not available%s" % (pv_str, pn, itemstr)) - else: - bb.msg.debug(1, bb.msg.domain.Provider, "selecting %s as PREFERRED_VERSION %s of package %s%s" % (preferred_file, pv_str, pn, itemstr)) - - return (preferred_ver, preferred_file) - - -def findLatestProvider(pn, cfgData, dataCache, file_set): - """ - Return the highest version of the providers in file_set. - Take default preferences into account. - """ - latest = None - latest_p = 0 - latest_f = None - for file_name in file_set: - pe,pv,pr = dataCache.pkg_pepvpr[file_name] - dp = dataCache.pkg_dp[file_name] - - if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p): - latest = (pe, pv, pr) - latest_f = file_name - latest_p = dp - - return (latest, latest_f) - - -def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None): - """ - If there is a PREFERRED_VERSION, find the highest-priority bbfile - providing that version. If not, find the latest version provided by - an bbfile in the highest-priority set. - """ - - sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn) - # Find the highest priority provider with a PREFERRED_VERSION set - (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item) - # Find the latest version of the highest priority provider - (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0]) - - if preferred_file is None: - preferred_file = latest_f - preferred_ver = latest - - return (latest, latest_f, preferred_ver, preferred_file) - - -def _filterProviders(providers, item, cfgData, dataCache): - """ - Take a list of providers and filter/reorder according to the - environment variables and previous build results - """ - eligible = [] - preferred_versions = {} - sortpkg_pn = {} - - # The order of providers depends on the order of the files on the disk - # up to here. Sort pkg_pn to make dependency issues reproducible rather - # than effectively random. - providers.sort() - - # Collate providers by PN - pkg_pn = {} - for p in providers: - pn = dataCache.pkg_fn[p] - if pn not in pkg_pn: - pkg_pn[pn] = [] - pkg_pn[pn].append(p) - - bb.msg.debug(1, bb.msg.domain.Provider, "providers for %s are: %s" % (item, pkg_pn.keys())) - - # First add PREFERRED_VERSIONS - for pn in pkg_pn.keys(): - sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn) - preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item) - if preferred_versions[pn][1]: - eligible.append(preferred_versions[pn][1]) - - # Now add latest verisons - for pn in sortpkg_pn.keys(): - if pn in preferred_versions and preferred_versions[pn][1]: - continue - preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0]) - eligible.append(preferred_versions[pn][1]) - - if len(eligible) == 0: - bb.msg.error(bb.msg.domain.Provider, "no eligible providers for %s" % item) - return 0 - - # If pn == item, give it a slight default preference - # This means PREFERRED_PROVIDER_foobar defaults to foobar if available - for p in providers: - pn = dataCache.pkg_fn[p] - if pn != item: - continue - (newvers, fn) = preferred_versions[pn] - if not fn in eligible: - continue - eligible.remove(fn) - eligible = [fn] + eligible - - return eligible - - -def filterProviders(providers, item, cfgData, dataCache): - """ - Take a list of providers and filter/reorder according to the - environment variables and previous build results - Takes a "normal" target item - """ - - eligible = _filterProviders(providers, item, cfgData, dataCache) - - prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, cfgData, 1) - if prefervar: - dataCache.preferred[item] = prefervar - - foundUnique = False - if item in dataCache.preferred: - for p in eligible: - pn = dataCache.pkg_fn[p] - if dataCache.preferred[item] == pn: - bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item)) - eligible.remove(p) - eligible = [p] + eligible - foundUnique = True - break - - bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible)) - - return eligible, foundUnique - -def filterProvidersRunTime(providers, item, cfgData, dataCache): - """ - Take a list of providers and filter/reorder according to the - environment variables and previous build results - Takes a "runtime" target item - """ - - eligible = _filterProviders(providers, item, cfgData, dataCache) - - # Should use dataCache.preferred here? - preferred = [] - preferred_vars = [] - for p in eligible: - pn = dataCache.pkg_fn[p] - provides = dataCache.pn_provides[pn] - for provide in provides: - bb.msg.note(2, bb.msg.domain.Provider, "checking PREFERRED_PROVIDER_%s" % (provide)) - prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, cfgData, 1) - if prefervar == pn: - var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar) - bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy runtime %s due to %s" % (pn, item, var)) - preferred_vars.append(var) - eligible.remove(p) - eligible = [p] + eligible - preferred.append(p) - break - - numberPreferred = len(preferred) - - if numberPreferred > 1: - bb.msg.error(bb.msg.domain.Provider, "Conflicting PREFERRED_PROVIDER entries were found which resulted in an attempt to select multiple providers (%s) for runtime dependecy %s\nThe entries resulting in this conflict were: %s" % (preferred, item, preferred_vars)) - - bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible)) - - return eligible, numberPreferred - -regexp_cache = {} - -def getRuntimeProviders(dataCache, rdepend): - """ - Return any providers of runtime dependency - """ - rproviders = [] - - if rdepend in dataCache.rproviders: - rproviders += dataCache.rproviders[rdepend] - - if rdepend in dataCache.packages: - rproviders += dataCache.packages[rdepend] - - if rproviders: - return rproviders - - # Only search dynamic packages if we can't find anything in other variables - for pattern in dataCache.packages_dynamic: - pattern = pattern.replace('+', "\+") - if pattern in regexp_cache: - regexp = regexp_cache[pattern] - else: - try: - regexp = re.compile(pattern) - except: - bb.msg.error(bb.msg.domain.Provider, "Error parsing re expression: %s" % pattern) - raise - regexp_cache[pattern] = regexp - if regexp.match(rdepend): - rproviders += dataCache.packages_dynamic[pattern] - - return rproviders diff --git a/bitbake-dev/lib/bb/runqueue.py b/bitbake-dev/lib/bb/runqueue.py deleted file mode 100644 index c3ad442e4..000000000 --- a/bitbake-dev/lib/bb/runqueue.py +++ /dev/null @@ -1,1174 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'RunQueue' implementation - -Handles preparation and execution of a queue of tasks -""" - -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from bb import msg, data, event, mkdirhier, utils -import bb, os, sys -import signal -import stat - -class TaskFailure(Exception): - """Exception raised when a task in a runqueue fails""" - def __init__(self, x): - self.args = x - - -class RunQueueStats: - """ - Holds statistics on the tasks handled by the associated runQueue - """ - def __init__(self, total): - self.completed = 0 - self.skipped = 0 - self.failed = 0 - self.active = 0 - self.total = total - - def taskFailed(self): - self.active = self.active - 1 - self.failed = self.failed + 1 - - def taskCompleted(self, number = 1): - self.active = self.active - number - self.completed = self.completed + number - - def taskSkipped(self, number = 1): - self.active = self.active + number - self.skipped = self.skipped + number - - def taskActive(self): - self.active = self.active + 1 - -# These values indicate the next step due to be run in the -# runQueue state machine -runQueuePrepare = 2 -runQueueRunInit = 3 -runQueueRunning = 4 -runQueueFailed = 6 -runQueueCleanUp = 7 -runQueueComplete = 8 -runQueueChildProcess = 9 - -class RunQueueScheduler: - """ - Control the order tasks are scheduled in. - """ - def __init__(self, runqueue): - """ - The default scheduler just returns the first buildable task (the - priority map is sorted by task numer) - """ - self.rq = runqueue - numTasks = len(self.rq.runq_fnid) - - self.prio_map = [] - self.prio_map.extend(range(numTasks)) - - def next(self): - """ - Return the id of the first task we find that is buildable - """ - for task1 in range(len(self.rq.runq_fnid)): - task = self.prio_map[task1] - if self.rq.runq_running[task] == 1: - continue - if self.rq.runq_buildable[task] == 1: - return task - -class RunQueueSchedulerSpeed(RunQueueScheduler): - """ - A scheduler optimised for speed. The priority map is sorted by task weight, - heavier weighted tasks (tasks needed by the most other tasks) are run first. - """ - def __init__(self, runqueue): - """ - The priority map is sorted by task weight. - """ - from copy import deepcopy - - self.rq = runqueue - - sortweight = deepcopy(self.rq.runq_weight) - sortweight.sort() - copyweight = deepcopy(self.rq.runq_weight) - self.prio_map = [] - - for weight in sortweight: - idx = copyweight.index(weight) - self.prio_map.append(idx) - copyweight[idx] = -1 - - self.prio_map.reverse() - -class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed): - """ - A scheduler optimised to complete .bb files are quickly as possible. The - priority map is sorted by task weight, but then reordered so once a given - .bb file starts to build, its completed as quickly as possible. This works - well where disk space is at a premium and classes like OE's rm_work are in - force. - """ - def __init__(self, runqueue): - RunQueueSchedulerSpeed.__init__(self, runqueue) - from copy import deepcopy - - #FIXME - whilst this groups all fnids together it does not reorder the - #fnid groups optimally. - - basemap = deepcopy(self.prio_map) - self.prio_map = [] - while (len(basemap) > 0): - entry = basemap.pop(0) - self.prio_map.append(entry) - fnid = self.rq.runq_fnid[entry] - todel = [] - for entry in basemap: - entry_fnid = self.rq.runq_fnid[entry] - if entry_fnid == fnid: - todel.append(basemap.index(entry)) - self.prio_map.append(entry) - todel.reverse() - for idx in todel: - del basemap[idx] - -class RunQueue: - """ - BitBake Run Queue implementation - """ - def __init__(self, cooker, cfgData, dataCache, taskData, targets): - self.reset_runqueue() - self.cooker = cooker - self.dataCache = dataCache - self.taskData = taskData - self.cfgData = cfgData - self.targets = targets - - self.number_tasks = int(bb.data.getVar("BB_NUMBER_THREADS", cfgData, 1) or 1) - self.multi_provider_whitelist = (bb.data.getVar("MULTI_PROVIDER_WHITELIST", cfgData, 1) or "").split() - self.scheduler = bb.data.getVar("BB_SCHEDULER", cfgData, 1) or "speed" - self.stamppolicy = bb.data.getVar("BB_STAMP_POLICY", cfgData, 1) or "perfile" - self.stampwhitelist = bb.data.getVar("BB_STAMP_WHITELIST", cfgData, 1) or "" - - def reset_runqueue(self): - self.runq_fnid = [] - self.runq_task = [] - self.runq_depends = [] - self.runq_revdeps = [] - - self.state = runQueuePrepare - - def get_user_idstring(self, task): - fn = self.taskData.fn_index[self.runq_fnid[task]] - taskname = self.runq_task[task] - return "%s, %s" % (fn, taskname) - - def get_task_id(self, fnid, taskname): - for listid in range(len(self.runq_fnid)): - if self.runq_fnid[listid] == fnid and self.runq_task[listid] == taskname: - return listid - return None - - def circular_depchains_handler(self, tasks): - """ - Some tasks aren't buildable, likely due to circular dependency issues. - Identify the circular dependencies and print them in a user readable format. - """ - from copy import deepcopy - - valid_chains = [] - explored_deps = {} - msgs = [] - - def chain_reorder(chain): - """ - Reorder a dependency chain so the lowest task id is first - """ - lowest = 0 - new_chain = [] - for entry in range(len(chain)): - if chain[entry] < chain[lowest]: - lowest = entry - new_chain.extend(chain[lowest:]) - new_chain.extend(chain[:lowest]) - return new_chain - - def chain_compare_equal(chain1, chain2): - """ - Compare two dependency chains and see if they're the same - """ - if len(chain1) != len(chain2): - return False - for index in range(len(chain1)): - if chain1[index] != chain2[index]: - return False - return True - - def chain_array_contains(chain, chain_array): - """ - Return True if chain_array contains chain - """ - for ch in chain_array: - if chain_compare_equal(ch, chain): - return True - return False - - def find_chains(taskid, prev_chain): - prev_chain.append(taskid) - total_deps = [] - total_deps.extend(self.runq_revdeps[taskid]) - for revdep in self.runq_revdeps[taskid]: - if revdep in prev_chain: - idx = prev_chain.index(revdep) - # To prevent duplicates, reorder the chain to start with the lowest taskid - # and search through an array of those we've already printed - chain = prev_chain[idx:] - new_chain = chain_reorder(chain) - if not chain_array_contains(new_chain, valid_chains): - valid_chains.append(new_chain) - msgs.append("Dependency loop #%d found:\n" % len(valid_chains)) - for dep in new_chain: - msgs.append(" Task %s (%s) (depends: %s)\n" % (dep, self.get_user_idstring(dep), self.runq_depends[dep])) - msgs.append("\n") - if len(valid_chains) > 10: - msgs.append("Aborted dependency loops search after 10 matches.\n") - return msgs - continue - scan = False - if revdep not in explored_deps: - scan = True - elif revdep in explored_deps[revdep]: - scan = True - else: - for dep in prev_chain: - if dep in explored_deps[revdep]: - scan = True - if scan: - find_chains(revdep, deepcopy(prev_chain)) - for dep in explored_deps[revdep]: - if dep not in total_deps: - total_deps.append(dep) - - explored_deps[taskid] = total_deps - - for task in tasks: - find_chains(task, []) - - return msgs - - def calculate_task_weights(self, endpoints): - """ - Calculate a number representing the "weight" of each task. Heavier weighted tasks - have more dependencies and hence should be executed sooner for maximum speed. - - This function also sanity checks the task list finding tasks that its not - possible to execute due to circular dependencies. - """ - - numTasks = len(self.runq_fnid) - weight = [] - deps_left = [] - task_done = [] - - for listid in range(numTasks): - task_done.append(False) - weight.append(0) - deps_left.append(len(self.runq_revdeps[listid])) - - for listid in endpoints: - weight[listid] = 1 - task_done[listid] = True - - while 1: - next_points = [] - for listid in endpoints: - for revdep in self.runq_depends[listid]: - weight[revdep] = weight[revdep] + weight[listid] - deps_left[revdep] = deps_left[revdep] - 1 - if deps_left[revdep] == 0: - next_points.append(revdep) - task_done[revdep] = True - endpoints = next_points - if len(next_points) == 0: - break - - # Circular dependency sanity check - problem_tasks = [] - for task in range(numTasks): - if task_done[task] is False or deps_left[task] != 0: - problem_tasks.append(task) - bb.msg.debug(2, bb.msg.domain.RunQueue, "Task %s (%s) is not buildable\n" % (task, self.get_user_idstring(task))) - bb.msg.debug(2, bb.msg.domain.RunQueue, "(Complete marker was %s and the remaining dependency count was %s)\n\n" % (task_done[task], deps_left[task])) - - if problem_tasks: - message = "Unbuildable tasks were found.\n" - message = message + "These are usually caused by circular dependencies and any circular dependency chains found will be printed below. Increase the debug level to see a list of unbuildable tasks.\n\n" - message = message + "Identifying dependency loops (this may take a short while)...\n" - bb.msg.error(bb.msg.domain.RunQueue, message) - - msgs = self.circular_depchains_handler(problem_tasks) - - message = "\n" - for msg in msgs: - message = message + msg - bb.msg.fatal(bb.msg.domain.RunQueue, message) - - return weight - - def prepare_runqueue(self): - """ - Turn a set of taskData into a RunQueue and compute data needed - to optimise the execution order. - """ - - runq_build = [] - recursive_tdepends = {} - runq_recrdepends = [] - tdepends_fnid = {} - - taskData = self.taskData - - if len(taskData.tasks_name) == 0: - # Nothing to do - return - - bb.msg.note(1, bb.msg.domain.RunQueue, "Preparing runqueue") - - # Step A - Work out a list of tasks to run - # - # Taskdata gives us a list of possible providers for every build and run - # target ordered by priority. It also gives information on each of those - # providers. - # - # To create the actual list of tasks to execute we fix the list of - # providers and then resolve the dependencies into task IDs. This - # process is repeated for each type of dependency (tdepends, deptask, - # rdeptast, recrdeptask, idepends). - - def add_build_dependencies(depids, tasknames, depends): - for depid in depids: - # Won't be in build_targets if ASSUME_PROVIDED - if depid not in taskData.build_targets: - continue - depdata = taskData.build_targets[depid][0] - if depdata is None: - continue - dep = taskData.fn_index[depdata] - for taskname in tasknames: - taskid = taskData.gettask_id(dep, taskname, False) - if taskid is not None: - depends.append(taskid) - - def add_runtime_dependencies(depids, tasknames, depends): - for depid in depids: - if depid not in taskData.run_targets: - continue - depdata = taskData.run_targets[depid][0] - if depdata is None: - continue - dep = taskData.fn_index[depdata] - for taskname in tasknames: - taskid = taskData.gettask_id(dep, taskname, False) - if taskid is not None: - depends.append(taskid) - - for task in range(len(taskData.tasks_name)): - depends = [] - recrdepends = [] - fnid = taskData.tasks_fnid[task] - fn = taskData.fn_index[fnid] - task_deps = self.dataCache.task_deps[fn] - - bb.msg.debug(2, bb.msg.domain.RunQueue, "Processing %s:%s" %(fn, taskData.tasks_name[task])) - - if fnid not in taskData.failed_fnids: - - # Resolve task internal dependencies - # - # e.g. addtask before X after Y - depends = taskData.tasks_tdepends[task] - - # Resolve 'deptask' dependencies - # - # e.g. do_sometask[deptask] = "do_someothertask" - # (makes sure sometask runs after someothertask of all DEPENDS) - if 'deptask' in task_deps and taskData.tasks_name[task] in task_deps['deptask']: - tasknames = task_deps['deptask'][taskData.tasks_name[task]].split() - add_build_dependencies(taskData.depids[fnid], tasknames, depends) - - # Resolve 'rdeptask' dependencies - # - # e.g. do_sometask[rdeptask] = "do_someothertask" - # (makes sure sometask runs after someothertask of all RDEPENDS) - if 'rdeptask' in task_deps and taskData.tasks_name[task] in task_deps['rdeptask']: - taskname = task_deps['rdeptask'][taskData.tasks_name[task]] - add_runtime_dependencies(taskData.rdepids[fnid], [taskname], depends) - - # Resolve inter-task dependencies - # - # e.g. do_sometask[depends] = "targetname:do_someothertask" - # (makes sure sometask runs after targetname's someothertask) - if fnid not in tdepends_fnid: - tdepends_fnid[fnid] = set() - idepends = taskData.tasks_idepends[task] - for (depid, idependtask) in idepends: - if depid in taskData.build_targets: - # Won't be in build_targets if ASSUME_PROVIDED - depdata = taskData.build_targets[depid][0] - if depdata is not None: - dep = taskData.fn_index[depdata] - taskid = taskData.gettask_id(dep, idependtask) - depends.append(taskid) - if depdata != fnid: - tdepends_fnid[fnid].add(taskid) - - - # Resolve recursive 'recrdeptask' dependencies (A) - # - # e.g. do_sometask[recrdeptask] = "do_someothertask" - # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively) - # We cover the recursive part of the dependencies below - if 'recrdeptask' in task_deps and taskData.tasks_name[task] in task_deps['recrdeptask']: - for taskname in task_deps['recrdeptask'][taskData.tasks_name[task]].split(): - recrdepends.append(taskname) - add_build_dependencies(taskData.depids[fnid], [taskname], depends) - add_runtime_dependencies(taskData.rdepids[fnid], [taskname], depends) - - # Rmove all self references - if task in depends: - newdep = [] - bb.msg.debug(2, bb.msg.domain.RunQueue, "Task %s (%s %s) contains self reference! %s" % (task, taskData.fn_index[taskData.tasks_fnid[task]], taskData.tasks_name[task], depends)) - for dep in depends: - if task != dep: - newdep.append(dep) - depends = newdep - - self.runq_fnid.append(taskData.tasks_fnid[task]) - self.runq_task.append(taskData.tasks_name[task]) - self.runq_depends.append(set(depends)) - self.runq_revdeps.append(set()) - - runq_build.append(0) - runq_recrdepends.append(recrdepends) - - # - # Build a list of recursive cumulative dependencies for each fnid - # We do this by fnid, since if A depends on some task in B - # we're interested in later tasks B's fnid might have but B itself - # doesn't depend on - # - # Algorithm is O(tasks) + O(tasks)*O(fnids) - # - reccumdepends = {} - for task in range(len(self.runq_fnid)): - fnid = self.runq_fnid[task] - if fnid not in reccumdepends: - if fnid in tdepends_fnid: - reccumdepends[fnid] = tdepends_fnid[fnid] - else: - reccumdepends[fnid] = set() - reccumdepends[fnid].update(self.runq_depends[task]) - for task in range(len(self.runq_fnid)): - taskfnid = self.runq_fnid[task] - for fnid in reccumdepends: - if task in reccumdepends[fnid]: - reccumdepends[fnid].add(task) - if taskfnid in reccumdepends: - reccumdepends[fnid].update(reccumdepends[taskfnid]) - - - # Resolve recursive 'recrdeptask' dependencies (B) - # - # e.g. do_sometask[recrdeptask] = "do_someothertask" - # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively) - for task in range(len(self.runq_fnid)): - if len(runq_recrdepends[task]) > 0: - taskfnid = self.runq_fnid[task] - for dep in reccumdepends[taskfnid]: - # Ignore self references - if dep == task: - continue - for taskname in runq_recrdepends[task]: - if taskData.tasks_name[dep] == taskname: - self.runq_depends[task].add(dep) - - # Step B - Mark all active tasks - # - # Start with the tasks we were asked to run and mark all dependencies - # as active too. If the task is to be 'forced', clear its stamp. Once - # all active tasks are marked, prune the ones we don't need. - - bb.msg.note(2, bb.msg.domain.RunQueue, "Marking Active Tasks") - - def mark_active(listid, depth): - """ - Mark an item as active along with its depends - (calls itself recursively) - """ - - if runq_build[listid] == 1: - return - - runq_build[listid] = 1 - - depends = self.runq_depends[listid] - for depend in depends: - mark_active(depend, depth+1) - - self.target_pairs = [] - for target in self.targets: - targetid = taskData.getbuild_id(target[0]) - - if targetid not in taskData.build_targets: - continue - - if targetid in taskData.failed_deps: - continue - - fnid = taskData.build_targets[targetid][0] - fn = taskData.fn_index[fnid] - self.target_pairs.append((fn, target[1])) - - # Remove stamps for targets if force mode active - if self.cooker.configuration.force: - bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (target[1], fn)) - bb.build.del_stamp(target[1], self.dataCache, fn) - - if fnid in taskData.failed_fnids: - continue - - if target[1] not in taskData.tasks_lookup[fnid]: - bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s does not exist for target %s" % (target[1], target[0])) - - listid = taskData.tasks_lookup[fnid][target[1]] - - mark_active(listid, 1) - - # Step C - Prune all inactive tasks - # - # Once all active tasks are marked, prune the ones we don't need. - - maps = [] - delcount = 0 - for listid in range(len(self.runq_fnid)): - if runq_build[listid-delcount] == 1: - maps.append(listid-delcount) - else: - del self.runq_fnid[listid-delcount] - del self.runq_task[listid-delcount] - del self.runq_depends[listid-delcount] - del runq_build[listid-delcount] - del self.runq_revdeps[listid-delcount] - delcount = delcount + 1 - maps.append(-1) - - # - # Step D - Sanity checks and computation - # - - # Check to make sure we still have tasks to run - if len(self.runq_fnid) == 0: - if not taskData.abort: - bb.msg.fatal(bb.msg.domain.RunQueue, "All buildable tasks have been run but the build is incomplete (--continue mode). Errors for the tasks that failed will have been printed above.") - else: - bb.msg.fatal(bb.msg.domain.RunQueue, "No active tasks and not in --continue mode?! Please report this bug.") - - bb.msg.note(2, bb.msg.domain.RunQueue, "Pruned %s inactive tasks, %s left" % (delcount, len(self.runq_fnid))) - - # Remap the dependencies to account for the deleted tasks - # Check we didn't delete a task we depend on - for listid in range(len(self.runq_fnid)): - newdeps = [] - origdeps = self.runq_depends[listid] - for origdep in origdeps: - if maps[origdep] == -1: - bb.msg.fatal(bb.msg.domain.RunQueue, "Invalid mapping - Should never happen!") - newdeps.append(maps[origdep]) - self.runq_depends[listid] = set(newdeps) - - bb.msg.note(2, bb.msg.domain.RunQueue, "Assign Weightings") - - # Generate a list of reverse dependencies to ease future calculations - for listid in range(len(self.runq_fnid)): - for dep in self.runq_depends[listid]: - self.runq_revdeps[dep].add(listid) - - # Identify tasks at the end of dependency chains - # Error on circular dependency loops (length two) - endpoints = [] - for listid in range(len(self.runq_fnid)): - revdeps = self.runq_revdeps[listid] - if len(revdeps) == 0: - endpoints.append(listid) - for dep in revdeps: - if dep in self.runq_depends[listid]: - #self.dump_data(taskData) - bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) has circular dependency on %s (%s)" % (taskData.fn_index[self.runq_fnid[dep]], self.runq_task[dep] , taskData.fn_index[self.runq_fnid[listid]], self.runq_task[listid])) - - bb.msg.note(2, bb.msg.domain.RunQueue, "Compute totals (have %s endpoint(s))" % len(endpoints)) - - # Calculate task weights - # Check of higher length circular dependencies - self.runq_weight = self.calculate_task_weights(endpoints) - - # Decide what order to execute the tasks in, pick a scheduler - #self.sched = RunQueueScheduler(self) - if self.scheduler == "completion": - self.sched = RunQueueSchedulerCompletion(self) - else: - self.sched = RunQueueSchedulerSpeed(self) - - # Sanity Check - Check for multiple tasks building the same provider - prov_list = {} - seen_fn = [] - for task in range(len(self.runq_fnid)): - fn = taskData.fn_index[self.runq_fnid[task]] - if fn in seen_fn: - continue - seen_fn.append(fn) - for prov in self.dataCache.fn_provides[fn]: - if prov not in prov_list: - prov_list[prov] = [fn] - elif fn not in prov_list[prov]: - prov_list[prov].append(fn) - error = False - for prov in prov_list: - if len(prov_list[prov]) > 1 and prov not in self.multi_provider_whitelist: - error = True - bb.msg.error(bb.msg.domain.RunQueue, "Multiple .bb files are due to be built which each provide %s (%s).\n This usually means one provides something the other doesn't and should." % (prov, " ".join(prov_list[prov]))) - #if error: - # bb.msg.fatal(bb.msg.domain.RunQueue, "Corrupted metadata configuration detected, aborting...") - - - # Create a whitelist usable by the stamp checks - stampfnwhitelist = [] - for entry in self.stampwhitelist.split(): - entryid = self.taskData.getbuild_id(entry) - if entryid not in self.taskData.build_targets: - continue - fnid = self.taskData.build_targets[entryid][0] - fn = self.taskData.fn_index[fnid] - stampfnwhitelist.append(fn) - self.stampfnwhitelist = stampfnwhitelist - - #self.dump_data(taskData) - - self.state = runQueueRunInit - - def check_stamps(self): - unchecked = {} - current = [] - notcurrent = [] - buildable = [] - - if self.stamppolicy == "perfile": - fulldeptree = False - else: - fulldeptree = True - stampwhitelist = [] - if self.stamppolicy == "whitelist": - stampwhitelist = self.self.stampfnwhitelist - - for task in range(len(self.runq_fnid)): - unchecked[task] = "" - if len(self.runq_depends[task]) == 0: - buildable.append(task) - - def check_buildable(self, task, buildable): - for revdep in self.runq_revdeps[task]: - alldeps = 1 - for dep in self.runq_depends[revdep]: - if dep in unchecked: - alldeps = 0 - if alldeps == 1: - if revdep in unchecked: - buildable.append(revdep) - - for task in range(len(self.runq_fnid)): - if task not in unchecked: - continue - fn = self.taskData.fn_index[self.runq_fnid[task]] - taskname = self.runq_task[task] - stampfile = "%s.%s" % (self.dataCache.stamp[fn], taskname) - # If the stamp is missing its not current - if not os.access(stampfile, os.F_OK): - del unchecked[task] - notcurrent.append(task) - check_buildable(self, task, buildable) - continue - # If its a 'nostamp' task, it's not current - taskdep = self.dataCache.task_deps[fn] - if 'nostamp' in taskdep and task in taskdep['nostamp']: - del unchecked[task] - notcurrent.append(task) - check_buildable(self, task, buildable) - continue - - while (len(buildable) > 0): - nextbuildable = [] - for task in buildable: - if task in unchecked: - fn = self.taskData.fn_index[self.runq_fnid[task]] - taskname = self.runq_task[task] - stampfile = "%s.%s" % (self.dataCache.stamp[fn], taskname) - iscurrent = True - - t1 = os.stat(stampfile)[stat.ST_MTIME] - for dep in self.runq_depends[task]: - if iscurrent: - fn2 = self.taskData.fn_index[self.runq_fnid[dep]] - taskname2 = self.runq_task[dep] - stampfile2 = "%s.%s" % (self.dataCache.stamp[fn2], taskname2) - if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist): - if dep in notcurrent: - iscurrent = False - else: - t2 = os.stat(stampfile2)[stat.ST_MTIME] - if t1 < t2: - iscurrent = False - del unchecked[task] - if iscurrent: - current.append(task) - else: - notcurrent.append(task) - - check_buildable(self, task, nextbuildable) - - buildable = nextbuildable - - #for task in range(len(self.runq_fnid)): - # fn = self.taskData.fn_index[self.runq_fnid[task]] - # taskname = self.runq_task[task] - # print "%s %s.%s" % (task, taskname, fn) - - #print "Unchecked: %s" % unchecked - #print "Current: %s" % current - #print "Not current: %s" % notcurrent - - if len(unchecked) > 0: - bb.fatal("check_stamps fatal internal error") - return current - - def check_stamp_task(self, task): - - if self.stamppolicy == "perfile": - fulldeptree = False - else: - fulldeptree = True - stampwhitelist = [] - if self.stamppolicy == "whitelist": - stampwhitelist = self.stampfnwhitelist - - fn = self.taskData.fn_index[self.runq_fnid[task]] - taskname = self.runq_task[task] - stampfile = "%s.%s" % (self.dataCache.stamp[fn], taskname) - # If the stamp is missing its not current - if not os.access(stampfile, os.F_OK): - bb.msg.debug(2, bb.msg.domain.RunQueue, "Stampfile %s not available\n" % stampfile) - return False - # If its a 'nostamp' task, it's not current - taskdep = self.dataCache.task_deps[fn] - if 'nostamp' in taskdep and taskname in taskdep['nostamp']: - bb.msg.debug(2, bb.msg.domain.RunQueue, "%s.%s is nostamp\n" % (fn, taskname)) - return False - - iscurrent = True - t1 = os.stat(stampfile)[stat.ST_MTIME] - for dep in self.runq_depends[task]: - if iscurrent: - fn2 = self.taskData.fn_index[self.runq_fnid[dep]] - taskname2 = self.runq_task[dep] - stampfile2 = "%s.%s" % (self.dataCache.stamp[fn2], taskname2) - if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist): - try: - t2 = os.stat(stampfile2)[stat.ST_MTIME] - if t1 < t2: - bb.msg.debug(2, bb.msg.domain.RunQueue, "Stampfile %s < %s" % (stampfile,stampfile2)) - iscurrent = False - except: - bb.msg.debug(2, bb.msg.domain.RunQueue, "Exception reading %s for %s" % (stampfile2 ,stampfile)) - iscurrent = False - - return iscurrent - - def execute_runqueue(self): - """ - Run the tasks in a queue prepared by prepare_runqueue - Upon failure, optionally try to recover the build using any alternate providers - (if the abort on failure configuration option isn't set) - """ - - if self.state is runQueuePrepare: - self.prepare_runqueue() - - if self.state is runQueueRunInit: - bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue") - self.execute_runqueue_initVars() - - if self.state is runQueueRunning: - self.execute_runqueue_internal() - - if self.state is runQueueCleanUp: - self.finish_runqueue() - - if self.state is runQueueFailed: - if not self.taskData.tryaltconfigs: - raise bb.runqueue.TaskFailure(self.failed_fnids) - for fnid in self.failed_fnids: - self.taskData.fail_fnid(fnid) - self.reset_runqueue() - - if self.state is runQueueComplete: - # All done - bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed)) - return False - - if self.state is runQueueChildProcess: - print "Child process" - return False - - # Loop - return True - - def execute_runqueue_initVars(self): - - self.stats = RunQueueStats(len(self.runq_fnid)) - - self.runq_buildable = [] - self.runq_running = [] - self.runq_complete = [] - self.build_pids = {} - self.build_pipes = {} - self.failed_fnids = [] - - # Mark initial buildable tasks - for task in range(self.stats.total): - self.runq_running.append(0) - self.runq_complete.append(0) - if len(self.runq_depends[task]) == 0: - self.runq_buildable.append(1) - else: - self.runq_buildable.append(0) - - self.state = runQueueRunning - - event.fire(bb.event.StampUpdate(self.target_pairs, self.dataCache.stamp), self.cfgData) - - def task_complete(self, task): - """ - Mark a task as completed - Look at the reverse dependencies and mark any task with - completed dependencies as buildable - """ - self.runq_complete[task] = 1 - for revdep in self.runq_revdeps[task]: - if self.runq_running[revdep] == 1: - continue - if self.runq_buildable[revdep] == 1: - continue - alldeps = 1 - for dep in self.runq_depends[revdep]: - if self.runq_complete[dep] != 1: - alldeps = 0 - if alldeps == 1: - self.runq_buildable[revdep] = 1 - fn = self.taskData.fn_index[self.runq_fnid[revdep]] - taskname = self.runq_task[revdep] - bb.msg.debug(1, bb.msg.domain.RunQueue, "Marking task %s (%s, %s) as buildable" % (revdep, fn, taskname)) - - def task_fail(self, task, exitcode): - """ - Called when a task has failed - Updates the state engine with the failure - """ - bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed with %s" % (task, self.get_user_idstring(task), exitcode)) - self.stats.taskFailed() - fnid = self.runq_fnid[task] - self.failed_fnids.append(fnid) - bb.event.fire(runQueueTaskFailed(task, self.stats, self), self.cfgData) - if self.taskData.abort: - self.state = runQueueCleanup - - def execute_runqueue_internal(self): - """ - Run the tasks in a queue prepared by prepare_runqueue - """ - - if self.stats.total == 0: - # nothing to do - self.state = runQueueCleanup - - while True: - task = None - if self.stats.active < self.number_tasks: - task = self.sched.next() - if task is not None: - fn = self.taskData.fn_index[self.runq_fnid[task]] - - taskname = self.runq_task[task] - if self.check_stamp_task(task): - bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task))) - self.runq_running[task] = 1 - self.runq_buildable[task] = 1 - self.task_complete(task) - self.stats.taskCompleted() - self.stats.taskSkipped() - continue - - sys.stdout.flush() - sys.stderr.flush() - try: - pipein, pipeout = os.pipe() - pid = os.fork() - except OSError, e: - bb.msg.fatal(bb.msg.domain.RunQueue, "fork failed: %d (%s)" % (e.errno, e.strerror)) - if pid == 0: - os.close(pipein) - # Save out the PID so that the event can include it the - # events - bb.event.worker_pid = os.getpid() - bb.event.worker_pipe = pipeout - - self.state = runQueueChildProcess - # Make the child the process group leader - os.setpgid(0, 0) - # No stdin - newsi = os.open('/dev/null', os.O_RDWR) - os.dup2(newsi, sys.stdin.fileno()) - - bb.event.fire(runQueueTaskStarted(task, self.stats, self), self.cfgData) - bb.msg.note(1, bb.msg.domain.RunQueue, - "Running task %d of %d (ID: %s, %s)" % (self.stats.completed + self.stats.active + 1, - self.stats.total, - task, - self.get_user_idstring(task))) - - bb.data.setVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY", self, self.cooker.configuration.data) - try: - self.cooker.tryBuild(fn, taskname[3:]) - except bb.build.EventException: - bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed") - os._exit(1) - except: - bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed") - os._exit(1) - os._exit(0) - - self.build_pids[pid] = task - self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData) - self.runq_running[task] = 1 - self.stats.taskActive() - if self.stats.active < self.number_tasks: - continue - - for pipe in self.build_pipes: - self.build_pipes[pipe].read() - - if self.stats.active > 0: - result = os.waitpid(-1, os.WNOHANG) - if result[0] is 0 and result[1] is 0: - return - task = self.build_pids[result[0]] - del self.build_pids[result[0]] - self.build_pipes[result[0]].close() - del self.build_pipes[result[0]] - if result[1] != 0: - self.task_fail(task, result[1]) - return - self.task_complete(task) - self.stats.taskCompleted() - bb.event.fire(runQueueTaskCompleted(task, self.stats, self), self.cfgData) - continue - - if len(self.failed_fnids) != 0: - self.state = runQueueFailed - return - - # Sanity Checks - for task in range(self.stats.total): - if self.runq_buildable[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never buildable!" % task) - if self.runq_running[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never ran!" % task) - if self.runq_complete[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never completed!" % task) - self.state = runQueueComplete - return - - def finish_runqueue_now(self): - bb.msg.note(1, bb.msg.domain.RunQueue, "Sending SIGINT to remaining %s tasks" % self.stats.active) - for k, v in self.build_pids.iteritems(): - try: - os.kill(-k, signal.SIGINT) - except: - pass - for pipe in self.build_pipes: - self.build_pipes[pipe].read() - - def finish_runqueue(self, now = False): - self.state = runQueueCleanUp - if now: - self.finish_runqueue_now() - try: - while self.stats.active > 0: - bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData) - bb.msg.note(1, bb.msg.domain.RunQueue, "Waiting for %s active tasks to finish" % self.stats.active) - tasknum = 1 - for k, v in self.build_pids.iteritems(): - bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v), k)) - tasknum = tasknum + 1 - result = os.waitpid(-1, os.WNOHANG) - if result[0] is 0 and result[1] is 0: - return - task = self.build_pids[result[0]] - del self.build_pids[result[0]] - self.build_pipes[result[0]].close() - del self.build_pipes[result[0]] - if result[1] != 0: - self.task_fail(task, result[1]) - else: - self.stats.taskCompleted() - bb.event.fire(runQueueTaskCompleted(task, self.stats, self), self.cfgData) - except: - self.finish_runqueue_now() - raise - - if len(self.failed_fnids) != 0: - self.state = runQueueFailed - return - - self.state = runQueueComplete - return - - def dump_data(self, taskQueue): - """ - Dump some debug information on the internal data structures - """ - bb.msg.debug(3, bb.msg.domain.RunQueue, "run_tasks:") - for task in range(len(self.runq_task)): - bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s Deps %s RevDeps %s" % (task, - taskQueue.fn_index[self.runq_fnid[task]], - self.runq_task[task], - self.runq_weight[task], - self.runq_depends[task], - self.runq_revdeps[task])) - - bb.msg.debug(3, bb.msg.domain.RunQueue, "sorted_tasks:") - for task1 in range(len(self.runq_task)): - if task1 in self.prio_map: - task = self.prio_map[task1] - bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s Deps %s RevDeps %s" % (task, - taskQueue.fn_index[self.runq_fnid[task]], - self.runq_task[task], - self.runq_weight[task], - self.runq_depends[task], - self.runq_revdeps[task])) - - -class TaskFailure(Exception): - """ - Exception raised when a task in a runqueue fails - """ - def __init__(self, x): - self.args = x - - -class runQueueExitWait(bb.event.Event): - """ - Event when waiting for task processes to exit - """ - - def __init__(self, remain): - self.remain = remain - self.message = "Waiting for %s active tasks to finish" % remain - bb.event.Event.__init__(self) - -class runQueueEvent(bb.event.Event): - """ - Base runQueue event class - """ - def __init__(self, task, stats, rq): - self.taskid = task - self.taskstring = rq.get_user_idstring(task) - self.stats = stats - bb.event.Event.__init__(self) - -class runQueueTaskStarted(runQueueEvent): - """ - Event notifing a task was started - """ - def __init__(self, task, stats, rq): - runQueueEvent.__init__(self, task, stats, rq) - self.message = "Running task %s (%d of %d) (%s)" % (task, stats.completed + stats.active + 1, self.stats.total, self.taskstring) - -class runQueueTaskFailed(runQueueEvent): - """ - Event notifing a task failed - """ - def __init__(self, task, stats, rq): - runQueueEvent.__init__(self, task, stats, rq) - self.message = "Task %s failed (%s)" % (task, self.taskstring) - -class runQueueTaskCompleted(runQueueEvent): - """ - Event notifing a task completed - """ - def __init__(self, task, stats, rq): - runQueueEvent.__init__(self, task, stats, rq) - self.message = "Task %s completed (%s)" % (task, self.taskstring) - -def check_stamp_fn(fn, taskname, d): - rq = bb.data.getVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY", d) - fnid = rq.taskData.getfn_id(fn) - taskid = rq.get_task_id(fnid, taskname) - if taskid is not None: - return rq.check_stamp_task(taskid) - return None - -class runQueuePipe(): - """ - Abstraction for a pipe between a worker thread and the server - """ - def __init__(self, pipein, pipeout, d): - self.fd = pipein - os.close(pipeout) - self.queue = "" - self.d = d - - def read(self): - start = len(self.queue) - self.queue = self.queue + os.read(self.fd, 1024) - end = len(self.queue) - index = self.queue.find("") - while index != -1: - bb.event.fire_from_worker(self.queue[:index+8], self.d) - self.queue = self.queue[index+8:] - index = self.queue.find("") - return (end > start) - - def close(self): - while self.read(): - continue - if len(self.queue) > 0: - print "Warning, worker left partial message" - os.close(self.fd) - diff --git a/bitbake-dev/lib/bb/server/__init__.py b/bitbake-dev/lib/bb/server/__init__.py deleted file mode 100644 index 1a732236e..000000000 --- a/bitbake-dev/lib/bb/server/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import xmlrpc -import none diff --git a/bitbake-dev/lib/bb/server/none.py b/bitbake-dev/lib/bb/server/none.py deleted file mode 100644 index ebda11158..000000000 --- a/bitbake-dev/lib/bb/server/none.py +++ /dev/null @@ -1,181 +0,0 @@ -# -# BitBake 'dummy' Passthrough Server -# -# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer -# Copyright (C) 2006 - 2008 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" - This module implements an xmlrpc server for BitBake. - - Use this by deriving a class from BitBakeXMLRPCServer and then adding - methods which you want to "export" via XMLRPC. If the methods have the - prefix xmlrpc_, then registering those function will happen automatically, - if not, you need to call register_function. - - Use register_idle_function() to add a function which the xmlrpc server - calls from within server_forever when no requests are pending. Make sure - that those functions are non-blocking or else you will introduce latency - in the server's main loop. -""" - -import time -import bb -from bb.ui import uievent -import xmlrpclib -import pickle - -DEBUG = False - -from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler -import inspect, select - -class BitBakeServerCommands(): - def __init__(self, server, cooker): - self.cooker = cooker - self.server = server - - def runCommand(self, command): - """ - Run a cooker command on the server - """ - #print "Running Command %s" % command - return self.cooker.command.runCommand(command) - - def terminateServer(self): - """ - Trigger the server to quit - """ - self.server.server_exit() - #print "Server (cooker) exitting" - return - - def ping(self): - """ - Dummy method which can be used to check the server is still alive - """ - return True - -eventQueue = [] - -class BBUIEventQueue: - class event: - def __init__(self, parent): - self.parent = parent - @staticmethod - def send(event): - bb.server.none.eventQueue.append(pickle.loads(event)) - @staticmethod - def quit(): - return - - def __init__(self, BBServer): - self.eventQueue = bb.server.none.eventQueue - self.BBServer = BBServer - self.EventHandle = bb.event.register_UIHhandler(self) - - def getEvent(self): - if len(self.eventQueue) == 0: - return None - - return self.eventQueue.pop(0) - - def waitEvent(self, delay): - event = self.getEvent() - if event: - return event - self.BBServer.idle_commands(delay) - return self.getEvent() - - def queue_event(self, event): - self.eventQueue.append(event) - - def system_quit( self ): - bb.event.unregister_UIHhandler(self.EventHandle) - -class BitBakeServer(): - # remove this when you're done with debugging - # allow_reuse_address = True - - def __init__(self, cooker): - self._idlefuns = {} - self.commands = BitBakeServerCommands(self, cooker) - - def register_idle_function(self, function, data): - """Register a function to be called while the server is idle""" - assert callable(function) - self._idlefuns[function] = data - - def idle_commands(self, delay): - #print "Idle queue length %s" % len(self._idlefuns) - #print "Idle timeout, running idle functions" - #if len(self._idlefuns) == 0: - nextsleep = delay - for function, data in self._idlefuns.items(): - try: - retval = function(self, data, False) - #print "Idle function returned %s" % (retval) - if retval is False: - del self._idlefuns[function] - elif retval is True: - nextsleep = None - elif nextsleep is None: - continue - elif retval < nextsleep: - nextsleep = retval - except SystemExit: - raise - except: - import traceback - traceback.print_exc() - pass - if nextsleep is not None: - #print "Sleeping for %s (%s)" % (nextsleep, delay) - time.sleep(nextsleep) - - def server_exit(self): - # Tell idle functions we're exiting - for function, data in self._idlefuns.items(): - try: - retval = function(self, data, True) - except: - pass - -class BitbakeServerInfo(): - def __init__(self, server): - self.server = server - self.commands = server.commands - -class BitBakeServerFork(): - def __init__(self, serverinfo, command, logfile): - serverinfo.forkCommand = command - serverinfo.logfile = logfile - -class BitBakeServerConnection(): - def __init__(self, serverinfo): - self.server = serverinfo.server - self.connection = serverinfo.commands - self.events = bb.server.none.BBUIEventQueue(self.server) - - def terminate(self): - try: - self.events.system_quit() - except: - pass - try: - self.connection.terminateServer() - except: - pass - diff --git a/bitbake-dev/lib/bb/server/xmlrpc.py b/bitbake-dev/lib/bb/server/xmlrpc.py deleted file mode 100644 index 3364918c7..000000000 --- a/bitbake-dev/lib/bb/server/xmlrpc.py +++ /dev/null @@ -1,187 +0,0 @@ -# -# BitBake XMLRPC Server -# -# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer -# Copyright (C) 2006 - 2008 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" - This module implements an xmlrpc server for BitBake. - - Use this by deriving a class from BitBakeXMLRPCServer and then adding - methods which you want to "export" via XMLRPC. If the methods have the - prefix xmlrpc_, then registering those function will happen automatically, - if not, you need to call register_function. - - Use register_idle_function() to add a function which the xmlrpc server - calls from within server_forever when no requests are pending. Make sure - that those functions are non-blocking or else you will introduce latency - in the server's main loop. -""" - -import bb -import xmlrpclib, sys -from bb import daemonize -from bb.ui import uievent - -DEBUG = False - -from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler -import inspect, select - -if sys.hexversion < 0x020600F0: - print "Sorry, python 2.6 or later is required for bitbake's XMLRPC mode" - sys.exit(1) - -class BitBakeServerCommands(): - def __init__(self, server, cooker): - self.cooker = cooker - self.server = server - - def registerEventHandler(self, host, port): - """ - Register a remote UI Event Handler - """ - s = xmlrpclib.Server("http://%s:%d" % (host, port), allow_none=True) - return bb.event.register_UIHhandler(s) - - def unregisterEventHandler(self, handlerNum): - """ - Unregister a remote UI Event Handler - """ - return bb.event.unregister_UIHhandler(handlerNum) - - def runCommand(self, command): - """ - Run a cooker command on the server - """ - return self.cooker.command.runCommand(command) - - def terminateServer(self): - """ - Trigger the server to quit - """ - self.server.quit = True - print "Server (cooker) exitting" - return - - def ping(self): - """ - Dummy method which can be used to check the server is still alive - """ - return True - -class BitBakeServer(SimpleXMLRPCServer): - # remove this when you're done with debugging - # allow_reuse_address = True - - def __init__(self, cooker, interface = ("localhost", 0)): - """ - Constructor - """ - SimpleXMLRPCServer.__init__(self, interface, - requestHandler=SimpleXMLRPCRequestHandler, - logRequests=False, allow_none=True) - self._idlefuns = {} - self.host, self.port = self.socket.getsockname() - #self.register_introspection_functions() - commands = BitBakeServerCommands(self, cooker) - self.autoregister_all_functions(commands, "") - - def autoregister_all_functions(self, context, prefix): - """ - Convenience method for registering all functions in the scope - of this class that start with a common prefix - """ - methodlist = inspect.getmembers(context, inspect.ismethod) - for name, method in methodlist: - if name.startswith(prefix): - self.register_function(method, name[len(prefix):]) - - def register_idle_function(self, function, data): - """Register a function to be called while the server is idle""" - assert callable(function) - self._idlefuns[function] = data - - def serve_forever(self): - """ - Serve Requests. Overloaded to honor a quit command - """ - self.quit = False - self.timeout = 0 # Run Idle calls for our first callback - while not self.quit: - #print "Idle queue length %s" % len(self._idlefuns) - self.handle_request() - #print "Idle timeout, running idle functions" - nextsleep = None - for function, data in self._idlefuns.items(): - try: - retval = function(self, data, False) - if retval is False: - del self._idlefuns[function] - elif retval is True: - nextsleep = 0 - elif nextsleep is 0: - continue - elif nextsleep is None: - nextsleep = retval - elif retval < nextsleep: - nextsleep = retval - except SystemExit: - raise - except: - import traceback - traceback.print_exc() - pass - if nextsleep is None and len(self._idlefuns) > 0: - nextsleep = 0 - self.timeout = nextsleep - # Tell idle functions we're exiting - for function, data in self._idlefuns.items(): - try: - retval = function(self, data, True) - except: - pass - - self.server_close() - return - -class BitbakeServerInfo(): - def __init__(self, server): - self.host = server.host - self.port = server.port - -class BitBakeServerFork(): - def __init__(self, serverinfo, command, logfile): - daemonize.createDaemon(command, logfile) - -class BitBakeServerConnection(): - def __init__(self, serverinfo): - self.connection = xmlrpclib.Server("http://%s:%s" % (serverinfo.host, serverinfo.port), allow_none=True) - self.events = uievent.BBUIEventQueue(self.connection) - - def terminate(self): - # Don't wait for server indefinitely - import socket - socket.setdefaulttimeout(2) - try: - self.events.system_quit() - except: - pass - try: - self.connection.terminateServer() - except: - pass - diff --git a/bitbake-dev/lib/bb/shell.py b/bitbake-dev/lib/bb/shell.py deleted file mode 100644 index 66e51719a..000000000 --- a/bitbake-dev/lib/bb/shell.py +++ /dev/null @@ -1,824 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -########################################################################## -# -# Copyright (C) 2005-2006 Michael 'Mickey' Lauer -# Copyright (C) 2005-2006 Vanille Media -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -########################################################################## -# -# Thanks to: -# * Holger Freyther -# * Justin Patrin -# -########################################################################## - -""" -BitBake Shell - -IDEAS: - * list defined tasks per package - * list classes - * toggle force - * command to reparse just one (or more) bbfile(s) - * automatic check if reparsing is necessary (inotify?) - * frontend for bb file manipulation - * more shell-like features: - - output control, i.e. pipe output into grep, sort, etc. - - job control, i.e. bring running commands into background and foreground - * start parsing in background right after startup - * ncurses interface - -PROBLEMS: - * force doesn't always work - * readline completion for commands with more than one parameters - -""" - -########################################################################## -# Import and setup global variables -########################################################################## - -try: - set -except NameError: - from sets import Set as set -import sys, os, readline, socket, httplib, urllib, commands, popen2, copy, shlex, Queue, fnmatch -from bb import data, parse, build, fatal, cache, taskdata, runqueue, providers as Providers - -__version__ = "0.5.3.1" -__credits__ = """BitBake Shell Version %s (C) 2005 Michael 'Mickey' Lauer -Type 'help' for more information, press CTRL-D to exit.""" % __version__ - -cmds = {} -leave_mainloop = False -last_exception = None -cooker = None -parsed = False -debug = os.environ.get( "BBSHELL_DEBUG", "" ) - -########################################################################## -# Class BitBakeShellCommands -########################################################################## - -class BitBakeShellCommands: - """This class contains the valid commands for the shell""" - - def __init__( self, shell ): - """Register all the commands""" - self._shell = shell - for attr in BitBakeShellCommands.__dict__: - if not attr.startswith( "_" ): - if attr.endswith( "_" ): - command = attr[:-1].lower() - else: - command = attr[:].lower() - method = getattr( BitBakeShellCommands, attr ) - debugOut( "registering command '%s'" % command ) - # scan number of arguments - usage = getattr( method, "usage", "" ) - if usage != "<...>": - numArgs = len( usage.split() ) - else: - numArgs = -1 - shell.registerCommand( command, method, numArgs, "%s %s" % ( command, usage ), method.__doc__ ) - - def _checkParsed( self ): - if not parsed: - print "SHELL: This command needs to parse bbfiles..." - self.parse( None ) - - def _findProvider( self, item ): - self._checkParsed() - # Need to use taskData for this information - preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, cooker.configuration.data, 1 ) - if not preferred: preferred = item - try: - lv, lf, pv, pf = Providers.findBestProvider(preferred, cooker.configuration.data, cooker.status) - except KeyError: - if item in cooker.status.providers: - pf = cooker.status.providers[item][0] - else: - pf = None - return pf - - def alias( self, params ): - """Register a new name for a command""" - new, old = params - if not old in cmds: - print "ERROR: Command '%s' not known" % old - else: - cmds[new] = cmds[old] - print "OK" - alias.usage = " " - - def buffer( self, params ): - """Dump specified output buffer""" - index = params[0] - print self._shell.myout.buffer( int( index ) ) - buffer.usage = "" - - def buffers( self, params ): - """Show the available output buffers""" - commands = self._shell.myout.bufferedCommands() - if not commands: - print "SHELL: No buffered commands available yet. Start doing something." - else: - print "="*35, "Available Output Buffers", "="*27 - for index, cmd in enumerate( commands ): - print "| %s %s" % ( str( index ).ljust( 3 ), cmd ) - print "="*88 - - def build( self, params, cmd = "build" ): - """Build a providee""" - global last_exception - globexpr = params[0] - self._checkParsed() - names = globfilter( cooker.status.pkg_pn.keys(), globexpr ) - if len( names ) == 0: names = [ globexpr ] - print "SHELL: Building %s" % ' '.join( names ) - - td = taskdata.TaskData(cooker.configuration.abort) - localdata = data.createCopy(cooker.configuration.data) - data.update_data(localdata) - data.expandKeys(localdata) - - try: - tasks = [] - for name in names: - td.add_provider(localdata, cooker.status, name) - providers = td.get_provider(name) - - if len(providers) == 0: - raise Providers.NoProvider - - tasks.append([name, "do_%s" % cmd]) - - td.add_unresolved(localdata, cooker.status) - - rq = runqueue.RunQueue(cooker, localdata, cooker.status, td, tasks) - rq.prepare_runqueue() - rq.execute_runqueue() - - except Providers.NoProvider: - print "ERROR: No Provider" - last_exception = Providers.NoProvider - - except runqueue.TaskFailure, fnids: - for fnid in fnids: - print "ERROR: '%s' failed" % td.fn_index[fnid] - last_exception = runqueue.TaskFailure - - except build.EventException, e: - print "ERROR: Couldn't build '%s'" % names - last_exception = e - - - build.usage = "" - - def clean( self, params ): - """Clean a providee""" - self.build( params, "clean" ) - clean.usage = "" - - def compile( self, params ): - """Execute 'compile' on a providee""" - self.build( params, "compile" ) - compile.usage = "" - - def configure( self, params ): - """Execute 'configure' on a providee""" - self.build( params, "configure" ) - configure.usage = "" - - def install( self, params ): - """Execute 'install' on a providee""" - self.build( params, "install" ) - install.usage = "" - - def edit( self, params ): - """Call $EDITOR on a providee""" - name = params[0] - bbfile = self._findProvider( name ) - if bbfile is not None: - os.system( "%s %s" % ( os.environ.get( "EDITOR", "vi" ), bbfile ) ) - else: - print "ERROR: Nothing provides '%s'" % name - edit.usage = "" - - def environment( self, params ): - """Dump out the outer BitBake environment""" - cooker.showEnvironment() - - def exit_( self, params ): - """Leave the BitBake Shell""" - debugOut( "setting leave_mainloop to true" ) - global leave_mainloop - leave_mainloop = True - - def fetch( self, params ): - """Fetch a providee""" - self.build( params, "fetch" ) - fetch.usage = "" - - def fileBuild( self, params, cmd = "build" ): - """Parse and build a .bb file""" - global last_exception - name = params[0] - bf = completeFilePath( name ) - print "SHELL: Calling '%s' on '%s'" % ( cmd, bf ) - - try: - cooker.buildFile(bf, cmd) - except parse.ParseError: - print "ERROR: Unable to open or parse '%s'" % bf - except build.EventException, e: - print "ERROR: Couldn't build '%s'" % name - last_exception = e - - fileBuild.usage = "" - - def fileClean( self, params ): - """Clean a .bb file""" - self.fileBuild( params, "clean" ) - fileClean.usage = "" - - def fileEdit( self, params ): - """Call $EDITOR on a .bb file""" - name = params[0] - os.system( "%s %s" % ( os.environ.get( "EDITOR", "vi" ), completeFilePath( name ) ) ) - fileEdit.usage = "" - - def fileRebuild( self, params ): - """Rebuild (clean & build) a .bb file""" - self.fileBuild( params, "rebuild" ) - fileRebuild.usage = "" - - def fileReparse( self, params ): - """(re)Parse a bb file""" - bbfile = params[0] - print "SHELL: Parsing '%s'" % bbfile - parse.update_mtime( bbfile ) - cooker.bb_cache.cacheValidUpdate(bbfile) - fromCache = cooker.bb_cache.loadData(bbfile, cooker.configuration.data, cooker.status) - cooker.bb_cache.sync() - if False: #fromCache: - print "SHELL: File has not been updated, not reparsing" - else: - print "SHELL: Parsed" - fileReparse.usage = "" - - def abort( self, params ): - """Toggle abort task execution flag (see bitbake -k)""" - cooker.configuration.abort = not cooker.configuration.abort - print "SHELL: Abort Flag is now '%s'" % repr( cooker.configuration.abort ) - - def force( self, params ): - """Toggle force task execution flag (see bitbake -f)""" - cooker.configuration.force = not cooker.configuration.force - print "SHELL: Force Flag is now '%s'" % repr( cooker.configuration.force ) - - def help( self, params ): - """Show a comprehensive list of commands and their purpose""" - print "="*30, "Available Commands", "="*30 - allcmds = cmds.keys() - allcmds.sort() - for cmd in allcmds: - function,numparams,usage,helptext = cmds[cmd] - print "| %s | %s" % (usage.ljust(30), helptext) - print "="*78 - - def lastError( self, params ): - """Show the reason or log that was produced by the last BitBake event exception""" - if last_exception is None: - print "SHELL: No Errors yet (Phew)..." - else: - reason, event = last_exception.args - print "SHELL: Reason for the last error: '%s'" % reason - if ':' in reason: - msg, filename = reason.split( ':' ) - filename = filename.strip() - print "SHELL: Dumping log file for last error:" - try: - print open( filename ).read() - except IOError: - print "ERROR: Couldn't open '%s'" % filename - - def match( self, params ): - """Dump all files or providers matching a glob expression""" - what, globexpr = params - if what == "files": - self._checkParsed() - for key in globfilter( cooker.status.pkg_fn.keys(), globexpr ): print key - elif what == "providers": - self._checkParsed() - for key in globfilter( cooker.status.pkg_pn.keys(), globexpr ): print key - else: - print "Usage: match %s" % self.print_.usage - match.usage = " " - - def new( self, params ): - """Create a new .bb file and open the editor""" - dirname, filename = params - packages = '/'.join( data.getVar( "BBFILES", cooker.configuration.data, 1 ).split('/')[:-2] ) - fulldirname = "%s/%s" % ( packages, dirname ) - - if not os.path.exists( fulldirname ): - print "SHELL: Creating '%s'" % fulldirname - os.mkdir( fulldirname ) - if os.path.exists( fulldirname ) and os.path.isdir( fulldirname ): - if os.path.exists( "%s/%s" % ( fulldirname, filename ) ): - print "SHELL: ERROR: %s/%s already exists" % ( fulldirname, filename ) - return False - print "SHELL: Creating '%s/%s'" % ( fulldirname, filename ) - newpackage = open( "%s/%s" % ( fulldirname, filename ), "w" ) - print >>newpackage,"""DESCRIPTION = "" -SECTION = "" -AUTHOR = "" -HOMEPAGE = "" -MAINTAINER = "" -LICENSE = "GPL" -PR = "r0" - -SRC_URI = "" - -#inherit base - -#do_configure() { -# -#} - -#do_compile() { -# -#} - -#do_stage() { -# -#} - -#do_install() { -# -#} -""" - newpackage.close() - os.system( "%s %s/%s" % ( os.environ.get( "EDITOR" ), fulldirname, filename ) ) - new.usage = " " - - def package( self, params ): - """Execute 'package' on a providee""" - self.build( params, "package" ) - package.usage = "" - - def pasteBin( self, params ): - """Send a command + output buffer to the pastebin at http://rafb.net/paste""" - index = params[0] - contents = self._shell.myout.buffer( int( index ) ) - sendToPastebin( "output of " + params[0], contents ) - pasteBin.usage = "" - - def pasteLog( self, params ): - """Send the last event exception error log (if there is one) to http://rafb.net/paste""" - if last_exception is None: - print "SHELL: No Errors yet (Phew)..." - else: - reason, event = last_exception.args - print "SHELL: Reason for the last error: '%s'" % reason - if ':' in reason: - msg, filename = reason.split( ':' ) - filename = filename.strip() - print "SHELL: Pasting log file to pastebin..." - - file = open( filename ).read() - sendToPastebin( "contents of " + filename, file ) - - def patch( self, params ): - """Execute 'patch' command on a providee""" - self.build( params, "patch" ) - patch.usage = "" - - def parse( self, params ): - """(Re-)parse .bb files and calculate the dependency graph""" - cooker.status = cache.CacheData() - ignore = data.getVar("ASSUME_PROVIDED", cooker.configuration.data, 1) or "" - cooker.status.ignored_dependencies = set( ignore.split() ) - cooker.handleCollections( data.getVar("BBFILE_COLLECTIONS", cooker.configuration.data, 1) ) - - (filelist, masked) = cooker.collect_bbfiles() - cooker.parse_bbfiles(filelist, masked, cooker.myProgressCallback) - cooker.buildDepgraph() - global parsed - parsed = True - print - - def reparse( self, params ): - """(re)Parse a providee's bb file""" - bbfile = self._findProvider( params[0] ) - if bbfile is not None: - print "SHELL: Found bbfile '%s' for '%s'" % ( bbfile, params[0] ) - self.fileReparse( [ bbfile ] ) - else: - print "ERROR: Nothing provides '%s'" % params[0] - reparse.usage = "" - - def getvar( self, params ): - """Dump the contents of an outer BitBake environment variable""" - var = params[0] - value = data.getVar( var, cooker.configuration.data, 1 ) - print value - getvar.usage = "" - - def peek( self, params ): - """Dump contents of variable defined in providee's metadata""" - name, var = params - bbfile = self._findProvider( name ) - if bbfile is not None: - the_data = cooker.bb_cache.loadDataFull(bbfile, cooker.configuration.data) - value = the_data.getVar( var, 1 ) - print value - else: - print "ERROR: Nothing provides '%s'" % name - peek.usage = " " - - def poke( self, params ): - """Set contents of variable defined in providee's metadata""" - name, var, value = params - bbfile = self._findProvider( name ) - if bbfile is not None: - print "ERROR: Sorry, this functionality is currently broken" - #d = cooker.pkgdata[bbfile] - #data.setVar( var, value, d ) - - # mark the change semi persistant - #cooker.pkgdata.setDirty(bbfile, d) - #print "OK" - else: - print "ERROR: Nothing provides '%s'" % name - poke.usage = " " - - def print_( self, params ): - """Dump all files or providers""" - what = params[0] - if what == "files": - self._checkParsed() - for key in cooker.status.pkg_fn.keys(): print key - elif what == "providers": - self._checkParsed() - for key in cooker.status.providers.keys(): print key - else: - print "Usage: print %s" % self.print_.usage - print_.usage = "" - - def python( self, params ): - """Enter the expert mode - an interactive BitBake Python Interpreter""" - sys.ps1 = "EXPERT BB>>> " - sys.ps2 = "EXPERT BB... " - import code - interpreter = code.InteractiveConsole( dict( globals() ) ) - interpreter.interact( "SHELL: Expert Mode - BitBake Python %s\nType 'help' for more information, press CTRL-D to switch back to BBSHELL." % sys.version ) - - def showdata( self, params ): - """Execute 'showdata' on a providee""" - cooker.showEnvironment(None, params) - showdata.usage = "" - - def setVar( self, params ): - """Set an outer BitBake environment variable""" - var, value = params - data.setVar( var, value, cooker.configuration.data ) - print "OK" - setVar.usage = " " - - def rebuild( self, params ): - """Clean and rebuild a .bb file or a providee""" - self.build( params, "clean" ) - self.build( params, "build" ) - rebuild.usage = "" - - def shell( self, params ): - """Execute a shell command and dump the output""" - if params != "": - print commands.getoutput( " ".join( params ) ) - shell.usage = "<...>" - - def stage( self, params ): - """Execute 'stage' on a providee""" - self.build( params, "populate_staging" ) - stage.usage = "" - - def status( self, params ): - """""" - print "-" * 78 - print "building list = '%s'" % cooker.building_list - print "build path = '%s'" % cooker.build_path - print "consider_msgs_cache = '%s'" % cooker.consider_msgs_cache - print "build stats = '%s'" % cooker.stats - if last_exception is not None: print "last_exception = '%s'" % repr( last_exception.args ) - print "memory output contents = '%s'" % self._shell.myout._buffer - - def test( self, params ): - """""" - print "testCommand called with '%s'" % params - - def unpack( self, params ): - """Execute 'unpack' on a providee""" - self.build( params, "unpack" ) - unpack.usage = "" - - def which( self, params ): - """Computes the providers for a given providee""" - # Need to use taskData for this information - item = params[0] - - self._checkParsed() - - preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, cooker.configuration.data, 1 ) - if not preferred: preferred = item - - try: - lv, lf, pv, pf = Providers.findBestProvider(preferred, cooker.configuration.data, cooker.status) - except KeyError: - lv, lf, pv, pf = (None,)*4 - - try: - providers = cooker.status.providers[item] - except KeyError: - print "SHELL: ERROR: Nothing provides", preferred - else: - for provider in providers: - if provider == pf: provider = " (***) %s" % provider - else: provider = " %s" % provider - print provider - which.usage = "" - -########################################################################## -# Common helper functions -########################################################################## - -def completeFilePath( bbfile ): - """Get the complete bbfile path""" - if not cooker.status: return bbfile - if not cooker.status.pkg_fn: return bbfile - for key in cooker.status.pkg_fn.keys(): - if key.endswith( bbfile ): - return key - return bbfile - -def sendToPastebin( desc, content ): - """Send content to http://oe.pastebin.com""" - mydata = {} - mydata["lang"] = "Plain Text" - mydata["desc"] = desc - mydata["cvt_tabs"] = "No" - mydata["nick"] = "%s@%s" % ( os.environ.get( "USER", "unknown" ), socket.gethostname() or "unknown" ) - mydata["text"] = content - params = urllib.urlencode( mydata ) - headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"} - - host = "rafb.net" - conn = httplib.HTTPConnection( "%s:80" % host ) - conn.request("POST", "/paste/paste.php", params, headers ) - - response = conn.getresponse() - conn.close() - - if response.status == 302: - location = response.getheader( "location" ) or "unknown" - print "SHELL: Pasted to http://%s%s" % ( host, location ) - else: - print "ERROR: %s %s" % ( response.status, response.reason ) - -def completer( text, state ): - """Return a possible readline completion""" - debugOut( "completer called with text='%s', state='%d'" % ( text, state ) ) - - if state == 0: - line = readline.get_line_buffer() - if " " in line: - line = line.split() - # we are in second (or more) argument - if line[0] in cmds and hasattr( cmds[line[0]][0], "usage" ): # known command and usage - u = getattr( cmds[line[0]][0], "usage" ).split()[0] - if u == "": - allmatches = cooker.configuration.data.keys() - elif u == "": - if cooker.status.pkg_fn is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] - else: allmatches = [ x.split("/")[-1] for x in cooker.status.pkg_fn.keys() ] - elif u == "": - if cooker.status.pkg_fn is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] - else: allmatches = cooker.status.providers.iterkeys() - else: allmatches = [ "(No tab completion available for this command)" ] - else: allmatches = [ "(No tab completion available for this command)" ] - else: - # we are in first argument - allmatches = cmds.iterkeys() - - completer.matches = [ x for x in allmatches if x[:len(text)] == text ] - #print "completer.matches = '%s'" % completer.matches - if len( completer.matches ) > state: - return completer.matches[state] - else: - return None - -def debugOut( text ): - if debug: - sys.stderr.write( "( %s )\n" % text ) - -def columnize( alist, width = 80 ): - """ - A word-wrap function that preserves existing line breaks - and most spaces in the text. Expects that existing line - breaks are posix newlines (\n). - """ - return reduce(lambda line, word, width=width: '%s%s%s' % - (line, - ' \n'[(len(line[line.rfind('\n')+1:]) - + len(word.split('\n',1)[0] - ) >= width)], - word), - alist - ) - -def globfilter( names, pattern ): - return fnmatch.filter( names, pattern ) - -########################################################################## -# Class MemoryOutput -########################################################################## - -class MemoryOutput: - """File-like output class buffering the output of the last 10 commands""" - def __init__( self, delegate ): - self.delegate = delegate - self._buffer = [] - self.text = [] - self._command = None - - def startCommand( self, command ): - self._command = command - self.text = [] - def endCommand( self ): - if self._command is not None: - if len( self._buffer ) == 10: del self._buffer[0] - self._buffer.append( ( self._command, self.text ) ) - def removeLast( self ): - if self._buffer: - del self._buffer[ len( self._buffer ) - 1 ] - self.text = [] - self._command = None - def lastBuffer( self ): - if self._buffer: - return self._buffer[ len( self._buffer ) -1 ][1] - def bufferedCommands( self ): - return [ cmd for cmd, output in self._buffer ] - def buffer( self, i ): - if i < len( self._buffer ): - return "BB>> %s\n%s" % ( self._buffer[i][0], "".join( self._buffer[i][1] ) ) - else: return "ERROR: Invalid buffer number. Buffer needs to be in (0, %d)" % ( len( self._buffer ) - 1 ) - def write( self, text ): - if self._command is not None and text != "BB>> ": self.text.append( text ) - if self.delegate is not None: self.delegate.write( text ) - def flush( self ): - return self.delegate.flush() - def fileno( self ): - return self.delegate.fileno() - def isatty( self ): - return self.delegate.isatty() - -########################################################################## -# Class BitBakeShell -########################################################################## - -class BitBakeShell: - - def __init__( self ): - """Register commands and set up readline""" - self.commandQ = Queue.Queue() - self.commands = BitBakeShellCommands( self ) - self.myout = MemoryOutput( sys.stdout ) - self.historyfilename = os.path.expanduser( "~/.bbsh_history" ) - self.startupfilename = os.path.expanduser( "~/.bbsh_startup" ) - - readline.set_completer( completer ) - readline.set_completer_delims( " " ) - readline.parse_and_bind("tab: complete") - - try: - readline.read_history_file( self.historyfilename ) - except IOError: - pass # It doesn't exist yet. - - print __credits__ - - def cleanup( self ): - """Write readline history and clean up resources""" - debugOut( "writing command history" ) - try: - readline.write_history_file( self.historyfilename ) - except: - print "SHELL: Unable to save command history" - - def registerCommand( self, command, function, numparams = 0, usage = "", helptext = "" ): - """Register a command""" - if usage == "": usage = command - if helptext == "": helptext = function.__doc__ or "" - cmds[command] = ( function, numparams, usage, helptext ) - - def processCommand( self, command, params ): - """Process a command. Check number of params and print a usage string, if appropriate""" - debugOut( "processing command '%s'..." % command ) - try: - function, numparams, usage, helptext = cmds[command] - except KeyError: - print "SHELL: ERROR: '%s' command is not a valid command." % command - self.myout.removeLast() - else: - if (numparams != -1) and (not len( params ) == numparams): - print "Usage: '%s'" % usage - return - - result = function( self.commands, params ) - debugOut( "result was '%s'" % result ) - - def processStartupFile( self ): - """Read and execute all commands found in $HOME/.bbsh_startup""" - if os.path.exists( self.startupfilename ): - startupfile = open( self.startupfilename, "r" ) - for cmdline in startupfile: - debugOut( "processing startup line '%s'" % cmdline ) - if not cmdline: - continue - if "|" in cmdline: - print "ERROR: '|' in startup file is not allowed. Ignoring line" - continue - self.commandQ.put( cmdline.strip() ) - - def main( self ): - """The main command loop""" - while not leave_mainloop: - try: - if self.commandQ.empty(): - sys.stdout = self.myout.delegate - cmdline = raw_input( "BB>> " ) - sys.stdout = self.myout - else: - cmdline = self.commandQ.get() - if cmdline: - allCommands = cmdline.split( ';' ) - for command in allCommands: - pipecmd = None - # - # special case for expert mode - if command == 'python': - sys.stdout = self.myout.delegate - self.processCommand( command, "" ) - sys.stdout = self.myout - else: - self.myout.startCommand( command ) - if '|' in command: # disable output - command, pipecmd = command.split( '|' ) - delegate = self.myout.delegate - self.myout.delegate = None - tokens = shlex.split( command, True ) - self.processCommand( tokens[0], tokens[1:] or "" ) - self.myout.endCommand() - if pipecmd is not None: # restore output - self.myout.delegate = delegate - - pipe = popen2.Popen4( pipecmd ) - pipe.tochild.write( "\n".join( self.myout.lastBuffer() ) ) - pipe.tochild.close() - sys.stdout.write( pipe.fromchild.read() ) - # - except EOFError: - print - return - except KeyboardInterrupt: - print - -########################################################################## -# Start function - called from the BitBake command line utility -########################################################################## - -def start( aCooker ): - global cooker - cooker = aCooker - bbshell = BitBakeShell() - bbshell.processStartupFile() - bbshell.main() - bbshell.cleanup() - -if __name__ == "__main__": - print "SHELL: Sorry, this program should only be called by BitBake." diff --git a/bitbake-dev/lib/bb/taskdata.py b/bitbake-dev/lib/bb/taskdata.py deleted file mode 100644 index 4a88e75f6..000000000 --- a/bitbake-dev/lib/bb/taskdata.py +++ /dev/null @@ -1,610 +0,0 @@ -#!/usr/bin/env python -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake 'TaskData' implementation - -Task data collection and handling - -""" - -# Copyright (C) 2006 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import bb - -def re_match_strings(target, strings): - """ - Whether or not the string 'target' matches - any one string of the strings which can be regular expression string - """ - import re - - for name in strings: - if (name==target or - re.search(name,target)!=None): - return True - return False - -class TaskData: - """ - BitBake Task Data implementation - """ - def __init__(self, abort = True, tryaltconfigs = False): - self.build_names_index = [] - self.run_names_index = [] - self.fn_index = [] - - self.build_targets = {} - self.run_targets = {} - - self.external_targets = [] - - self.tasks_fnid = [] - self.tasks_name = [] - self.tasks_tdepends = [] - self.tasks_idepends = [] - # Cache to speed up task ID lookups - self.tasks_lookup = {} - - self.depids = {} - self.rdepids = {} - - self.consider_msgs_cache = [] - - self.failed_deps = [] - self.failed_rdeps = [] - self.failed_fnids = [] - - self.abort = abort - self.tryaltconfigs = tryaltconfigs - - def getbuild_id(self, name): - """ - Return an ID number for the build target name. - If it doesn't exist, create one. - """ - if not name in self.build_names_index: - self.build_names_index.append(name) - return len(self.build_names_index) - 1 - - return self.build_names_index.index(name) - - def getrun_id(self, name): - """ - Return an ID number for the run target name. - If it doesn't exist, create one. - """ - if not name in self.run_names_index: - self.run_names_index.append(name) - return len(self.run_names_index) - 1 - - return self.run_names_index.index(name) - - def getfn_id(self, name): - """ - Return an ID number for the filename. - If it doesn't exist, create one. - """ - if not name in self.fn_index: - self.fn_index.append(name) - return len(self.fn_index) - 1 - - return self.fn_index.index(name) - - def gettask_ids(self, fnid): - """ - Return an array of the ID numbers matching a given fnid. - """ - ids = [] - if fnid in self.tasks_lookup: - for task in self.tasks_lookup[fnid]: - ids.append(self.tasks_lookup[fnid][task]) - return ids - - def gettask_id(self, fn, task, create = True): - """ - Return an ID number for the task matching fn and task. - If it doesn't exist, create one by default. - Optionally return None instead. - """ - fnid = self.getfn_id(fn) - - if fnid in self.tasks_lookup: - if task in self.tasks_lookup[fnid]: - return self.tasks_lookup[fnid][task] - - if not create: - return None - - self.tasks_name.append(task) - self.tasks_fnid.append(fnid) - self.tasks_tdepends.append([]) - self.tasks_idepends.append([]) - - listid = len(self.tasks_name) - 1 - - if fnid not in self.tasks_lookup: - self.tasks_lookup[fnid] = {} - self.tasks_lookup[fnid][task] = listid - - return listid - - def add_tasks(self, fn, dataCache): - """ - Add tasks for a given fn to the database - """ - - task_deps = dataCache.task_deps[fn] - - fnid = self.getfn_id(fn) - - if fnid in self.failed_fnids: - bb.msg.fatal(bb.msg.domain.TaskData, "Trying to re-add a failed file? Something is broken...") - - # Check if we've already seen this fn - if fnid in self.tasks_fnid: - return - - for task in task_deps['tasks']: - - # Work out task dependencies - parentids = [] - for dep in task_deps['parents'][task]: - parentid = self.gettask_id(fn, dep) - parentids.append(parentid) - taskid = self.gettask_id(fn, task) - self.tasks_tdepends[taskid].extend(parentids) - - # Touch all intertask dependencies - if 'depends' in task_deps and task in task_deps['depends']: - ids = [] - for dep in task_deps['depends'][task].split(): - if dep: - if ":" not in dep: - bb.msg.fatal(bb.msg.domain.TaskData, "Error, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (depend, fn)) - ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1])) - self.tasks_idepends[taskid].extend(ids) - - # Work out build dependencies - if not fnid in self.depids: - dependids = {} - for depend in dataCache.deps[fn]: - bb.msg.debug(2, bb.msg.domain.TaskData, "Added dependency %s for %s" % (depend, fn)) - dependids[self.getbuild_id(depend)] = None - self.depids[fnid] = dependids.keys() - - # Work out runtime dependencies - if not fnid in self.rdepids: - rdependids = {} - rdepends = dataCache.rundeps[fn] - rrecs = dataCache.runrecs[fn] - for package in rdepends: - for rdepend in bb.utils.explode_deps(rdepends[package]): - bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime dependency %s for %s" % (rdepend, fn)) - rdependids[self.getrun_id(rdepend)] = None - for package in rrecs: - for rdepend in bb.utils.explode_deps(rrecs[package]): - bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime recommendation %s for %s" % (rdepend, fn)) - rdependids[self.getrun_id(rdepend)] = None - self.rdepids[fnid] = rdependids.keys() - - for dep in self.depids[fnid]: - if dep in self.failed_deps: - self.fail_fnid(fnid) - return - for dep in self.rdepids[fnid]: - if dep in self.failed_rdeps: - self.fail_fnid(fnid) - return - - def have_build_target(self, target): - """ - Have we a build target matching this name? - """ - targetid = self.getbuild_id(target) - - if targetid in self.build_targets: - return True - return False - - def have_runtime_target(self, target): - """ - Have we a runtime target matching this name? - """ - targetid = self.getrun_id(target) - - if targetid in self.run_targets: - return True - return False - - def add_build_target(self, fn, item): - """ - Add a build target. - If already present, append the provider fn to the list - """ - targetid = self.getbuild_id(item) - fnid = self.getfn_id(fn) - - if targetid in self.build_targets: - if fnid in self.build_targets[targetid]: - return - self.build_targets[targetid].append(fnid) - return - self.build_targets[targetid] = [fnid] - - def add_runtime_target(self, fn, item): - """ - Add a runtime target. - If already present, append the provider fn to the list - """ - targetid = self.getrun_id(item) - fnid = self.getfn_id(fn) - - if targetid in self.run_targets: - if fnid in self.run_targets[targetid]: - return - self.run_targets[targetid].append(fnid) - return - self.run_targets[targetid] = [fnid] - - def mark_external_target(self, item): - """ - Mark a build target as being externally requested - """ - targetid = self.getbuild_id(item) - - if targetid not in self.external_targets: - self.external_targets.append(targetid) - - def get_unresolved_build_targets(self, dataCache): - """ - Return a list of build targets who's providers - are unknown. - """ - unresolved = [] - for target in self.build_names_index: - if re_match_strings(target, dataCache.ignored_dependencies): - continue - if self.build_names_index.index(target) in self.failed_deps: - continue - if not self.have_build_target(target): - unresolved.append(target) - return unresolved - - def get_unresolved_run_targets(self, dataCache): - """ - Return a list of runtime targets who's providers - are unknown. - """ - unresolved = [] - for target in self.run_names_index: - if re_match_strings(target, dataCache.ignored_dependencies): - continue - if self.run_names_index.index(target) in self.failed_rdeps: - continue - if not self.have_runtime_target(target): - unresolved.append(target) - return unresolved - - def get_provider(self, item): - """ - Return a list of providers of item - """ - targetid = self.getbuild_id(item) - - return self.build_targets[targetid] - - def get_dependees(self, itemid): - """ - Return a list of targets which depend on item - """ - dependees = [] - for fnid in self.depids: - if itemid in self.depids[fnid]: - dependees.append(fnid) - return dependees - - def get_dependees_str(self, item): - """ - Return a list of targets which depend on item as a user readable string - """ - itemid = self.getbuild_id(item) - dependees = [] - for fnid in self.depids: - if itemid in self.depids[fnid]: - dependees.append(self.fn_index[fnid]) - return dependees - - def get_rdependees(self, itemid): - """ - Return a list of targets which depend on runtime item - """ - dependees = [] - for fnid in self.rdepids: - if itemid in self.rdepids[fnid]: - dependees.append(fnid) - return dependees - - def get_rdependees_str(self, item): - """ - Return a list of targets which depend on runtime item as a user readable string - """ - itemid = self.getrun_id(item) - dependees = [] - for fnid in self.rdepids: - if itemid in self.rdepids[fnid]: - dependees.append(self.fn_index[fnid]) - return dependees - - def add_provider(self, cfgData, dataCache, item): - try: - self.add_provider_internal(cfgData, dataCache, item) - except bb.providers.NoProvider: - if self.abort: - if self.get_rdependees_str(item): - bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item))) - else: - bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item)) - raise - targetid = self.getbuild_id(item) - self.remove_buildtarget(targetid) - - self.mark_external_target(item) - - def add_provider_internal(self, cfgData, dataCache, item): - """ - Add the providers of item to the task data - Mark entries were specifically added externally as against dependencies - added internally during dependency resolution - """ - - if re_match_strings(item, dataCache.ignored_dependencies): - return - - if not item in dataCache.providers: - if self.get_rdependees_str(item): - bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item))) - else: - bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item)) - bb.event.fire(bb.event.NoProvider(item), cfgData) - raise bb.providers.NoProvider(item) - - if self.have_build_target(item): - return - - all_p = dataCache.providers[item] - - eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache) - eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids] - - if not eligible: - bb.msg.note(2, bb.msg.domain.Provider, "No buildable provider PROVIDES '%s' but '%s' DEPENDS on or otherwise requires it. Enable debugging and see earlier logs to find unbuildable providers." % (item, self.get_dependees_str(item))) - bb.event.fire(bb.event.NoProvider(item), cfgData) - raise bb.providers.NoProvider(item) - - if len(eligible) > 1 and foundUnique == False: - if item not in self.consider_msgs_cache: - providers_list = [] - for fn in eligible: - providers_list.append(dataCache.pkg_fn[fn]) - bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available for %s (%s);" % (item, ", ".join(providers_list))) - bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item) - bb.event.fire(bb.event.MultipleProviders(item, providers_list), cfgData) - self.consider_msgs_cache.append(item) - - for fn in eligible: - fnid = self.getfn_id(fn) - if fnid in self.failed_fnids: - continue - bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy %s" % (fn, item)) - self.add_build_target(fn, item) - self.add_tasks(fn, dataCache) - - - #item = dataCache.pkg_fn[fn] - - def add_rprovider(self, cfgData, dataCache, item): - """ - Add the runtime providers of item to the task data - (takes item names from RDEPENDS/PACKAGES namespace) - """ - - if re_match_strings(item, dataCache.ignored_dependencies): - return - - if self.have_runtime_target(item): - return - - all_p = bb.providers.getRuntimeProviders(dataCache, item) - - if not all_p: - bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables" % (self.get_rdependees_str(item), item)) - bb.event.fire(bb.event.NoProvider(item, runtime=True), cfgData) - raise bb.providers.NoRProvider(item) - - eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache) - eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids] - - if not eligible: - bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables of any buildable targets.\nEnable debugging and see earlier logs to find unbuildable targets." % (self.get_rdependees_str(item), item)) - bb.event.fire(bb.event.NoProvider(item, runtime=True), cfgData) - raise bb.providers.NoRProvider(item) - - if len(eligible) > 1 and numberPreferred == 0: - if item not in self.consider_msgs_cache: - providers_list = [] - for fn in eligible: - providers_list.append(dataCache.pkg_fn[fn]) - bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available for runtime %s (%s);" % (item, ", ".join(providers_list))) - bb.msg.note(2, bb.msg.domain.Provider, "consider defining a PREFERRED_PROVIDER entry to match runtime %s" % item) - bb.event.fire(bb.event.MultipleProviders(item,providers_list, runtime=True), cfgData) - self.consider_msgs_cache.append(item) - - if numberPreferred > 1: - if item not in self.consider_msgs_cache: - providers_list = [] - for fn in eligible: - providers_list.append(dataCache.pkg_fn[fn]) - bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available for runtime %s (top %s entries preferred) (%s);" % (item, numberPreferred, ", ".join(providers_list))) - bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER entry to match runtime %s" % item) - bb.event.fire(bb.event.MultipleProviders(item,providers_list, runtime=True), cfgData) - self.consider_msgs_cache.append(item) - - # run through the list until we find one that we can build - for fn in eligible: - fnid = self.getfn_id(fn) - if fnid in self.failed_fnids: - continue - bb.msg.debug(2, bb.msg.domain.Provider, "adding '%s' to satisfy runtime '%s'" % (fn, item)) - self.add_runtime_target(fn, item) - self.add_tasks(fn, dataCache) - - def fail_fnid(self, fnid, missing_list = []): - """ - Mark a file as failed (unbuildable) - Remove any references from build and runtime provider lists - - missing_list, A list of missing requirements for this target - """ - if fnid in self.failed_fnids: - return - bb.msg.debug(1, bb.msg.domain.Provider, "File '%s' is unbuildable, removing..." % self.fn_index[fnid]) - self.failed_fnids.append(fnid) - for target in self.build_targets: - if fnid in self.build_targets[target]: - self.build_targets[target].remove(fnid) - if len(self.build_targets[target]) == 0: - self.remove_buildtarget(target, missing_list) - for target in self.run_targets: - if fnid in self.run_targets[target]: - self.run_targets[target].remove(fnid) - if len(self.run_targets[target]) == 0: - self.remove_runtarget(target, missing_list) - - def remove_buildtarget(self, targetid, missing_list = []): - """ - Mark a build target as failed (unbuildable) - Trigger removal of any files that have this as a dependency - """ - if not missing_list: - missing_list = [self.build_names_index[targetid]] - else: - missing_list = [self.build_names_index[targetid]] + missing_list - bb.msg.note(2, bb.msg.domain.Provider, "Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s" % (self.build_names_index[targetid], missing_list)) - self.failed_deps.append(targetid) - dependees = self.get_dependees(targetid) - for fnid in dependees: - self.fail_fnid(fnid, missing_list) - for taskid in range(len(self.tasks_idepends)): - idepends = self.tasks_idepends[taskid] - for (idependid, idependtask) in idepends: - if idependid == targetid: - self.fail_fnid(self.tasks_fnid[taskid], missing_list) - - if self.abort and targetid in self.external_targets: - bb.msg.error(bb.msg.domain.Provider, "Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s" % (self.build_names_index[targetid], missing_list)) - raise bb.providers.NoProvider - - def remove_runtarget(self, targetid, missing_list = []): - """ - Mark a run target as failed (unbuildable) - Trigger removal of any files that have this as a dependency - """ - if not missing_list: - missing_list = [self.run_names_index[targetid]] - else: - missing_list = [self.run_names_index[targetid]] + missing_list - - bb.msg.note(1, bb.msg.domain.Provider, "Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s" % (self.run_names_index[targetid], missing_list)) - self.failed_rdeps.append(targetid) - dependees = self.get_rdependees(targetid) - for fnid in dependees: - self.fail_fnid(fnid, missing_list) - - def add_unresolved(self, cfgData, dataCache): - """ - Resolve all unresolved build and runtime targets - """ - bb.msg.note(1, bb.msg.domain.TaskData, "Resolving any missing task queue dependencies") - while 1: - added = 0 - for target in self.get_unresolved_build_targets(dataCache): - try: - self.add_provider_internal(cfgData, dataCache, target) - added = added + 1 - except bb.providers.NoProvider: - targetid = self.getbuild_id(target) - if self.abort and targetid in self.external_targets: - if self.get_rdependees_str(target): - bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (target, self.get_dependees_str(target))) - else: - bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (target)) - raise - self.remove_buildtarget(targetid) - for target in self.get_unresolved_run_targets(dataCache): - try: - self.add_rprovider(cfgData, dataCache, target) - added = added + 1 - except bb.providers.NoRProvider: - self.remove_runtarget(self.getrun_id(target)) - bb.msg.debug(1, bb.msg.domain.TaskData, "Resolved " + str(added) + " extra dependecies") - if added == 0: - break - # self.dump_data() - - def dump_data(self): - """ - Dump some debug information on the internal data structures - """ - bb.msg.debug(3, bb.msg.domain.TaskData, "build_names:") - bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.build_names_index)) - - bb.msg.debug(3, bb.msg.domain.TaskData, "run_names:") - bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.run_names_index)) - - bb.msg.debug(3, bb.msg.domain.TaskData, "build_targets:") - for buildid in range(len(self.build_names_index)): - target = self.build_names_index[buildid] - targets = "None" - if buildid in self.build_targets: - targets = self.build_targets[buildid] - bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (buildid, target, targets)) - - bb.msg.debug(3, bb.msg.domain.TaskData, "run_targets:") - for runid in range(len(self.run_names_index)): - target = self.run_names_index[runid] - targets = "None" - if runid in self.run_targets: - targets = self.run_targets[runid] - bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (runid, target, targets)) - - bb.msg.debug(3, bb.msg.domain.TaskData, "tasks:") - for task in range(len(self.tasks_name)): - bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s - %s: %s" % ( - task, - self.fn_index[self.tasks_fnid[task]], - self.tasks_name[task], - self.tasks_tdepends[task])) - - bb.msg.debug(3, bb.msg.domain.TaskData, "dependency ids (per fn):") - for fnid in self.depids: - bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.depids[fnid])) - - bb.msg.debug(3, bb.msg.domain.TaskData, "runtime dependency ids (per fn):") - for fnid in self.rdepids: - bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.rdepids[fnid])) - - diff --git a/bitbake-dev/lib/bb/ui/__init__.py b/bitbake-dev/lib/bb/ui/__init__.py deleted file mode 100644 index c6a377a8e..000000000 --- a/bitbake-dev/lib/bb/ui/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# BitBake UI Implementation -# -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - diff --git a/bitbake-dev/lib/bb/ui/crumbs/__init__.py b/bitbake-dev/lib/bb/ui/crumbs/__init__.py deleted file mode 100644 index c6a377a8e..000000000 --- a/bitbake-dev/lib/bb/ui/crumbs/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# BitBake UI Implementation -# -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - diff --git a/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py b/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py deleted file mode 100644 index f89e8eefd..000000000 --- a/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py +++ /dev/null @@ -1,457 +0,0 @@ -# -# BitBake Graphical GTK User Interface -# -# Copyright (C) 2008 Intel Corporation -# -# Authored by Rob Bradford -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gtk -import gobject -import threading -import os -import datetime -import time - -class BuildConfiguration: - """ Represents a potential *or* historic *or* concrete build. It - encompasses all the things that we need to tell bitbake to do to make it - build what we want it to build. - - It also stored the metadata URL and the set of possible machines (and the - distros / images / uris for these. Apart from the metdata URL these are - not serialised to file (since they may be transient). In some ways this - functionality might be shifted to the loader class.""" - - def __init__ (self): - self.metadata_url = None - - # Tuple of (distros, image, urls) - self.machine_options = {} - - self.machine = None - self.distro = None - self.image = None - self.urls = [] - self.extra_urls = [] - self.extra_pkgs = [] - - def get_machines_model (self): - model = gtk.ListStore (gobject.TYPE_STRING) - for machine in self.machine_options.keys(): - model.append ([machine]) - - return model - - def get_distro_and_images_models (self, machine): - distro_model = gtk.ListStore (gobject.TYPE_STRING) - - for distro in self.machine_options[machine][0]: - distro_model.append ([distro]) - - image_model = gtk.ListStore (gobject.TYPE_STRING) - - for image in self.machine_options[machine][1]: - image_model.append ([image]) - - return (distro_model, image_model) - - def get_repos (self): - self.urls = self.machine_options[self.machine][2] - return self.urls - - # It might be a lot lot better if we stored these in like, bitbake conf - # file format. - @staticmethod - def load_from_file (filename): - f = open (filename, "r") - - conf = BuildConfiguration() - for line in f.readlines(): - data = line.split (";")[1] - if (line.startswith ("metadata-url;")): - conf.metadata_url = data.strip() - continue - if (line.startswith ("url;")): - conf.urls += [data.strip()] - continue - if (line.startswith ("extra-url;")): - conf.extra_urls += [data.strip()] - continue - if (line.startswith ("machine;")): - conf.machine = data.strip() - continue - if (line.startswith ("distribution;")): - conf.distro = data.strip() - continue - if (line.startswith ("image;")): - conf.image = data.strip() - continue - - f.close () - return conf - - # Serialise to a file. This is part of the build process and we use this - # to be able to repeat a given build (using the same set of parameters) - # but also so that we can include the details of the image / machine / - # distro in the build manager tree view. - def write_to_file (self, filename): - f = open (filename, "w") - - lines = [] - - if (self.metadata_url): - lines += ["metadata-url;%s\n" % (self.metadata_url)] - - for url in self.urls: - lines += ["url;%s\n" % (url)] - - for url in self.extra_urls: - lines += ["extra-url;%s\n" % (url)] - - if (self.machine): - lines += ["machine;%s\n" % (self.machine)] - - if (self.distro): - lines += ["distribution;%s\n" % (self.distro)] - - if (self.image): - lines += ["image;%s\n" % (self.image)] - - f.writelines (lines) - f.close () - -class BuildResult(gobject.GObject): - """ Represents an historic build. Perhaps not successful. But it includes - things such as the files that are in the directory (the output from the - build) as well as a deserialised BuildConfiguration file that is stored in - ".conf" in the directory for the build. - - This is GObject so that it can be included in the TreeStore.""" - - (STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \ - (0, 1, 2) - - def __init__ (self, parent, identifier): - gobject.GObject.__init__ (self) - self.date = None - - self.files = [] - self.status = None - self.identifier = identifier - self.path = os.path.join (parent, identifier) - - # Extract the date, since the directory name is of the - # format build-- we can easily - # pull it out. - # TODO: Better to stat a file? - (_ , date, revision) = identifier.split ("-") - print date - - year = int (date[0:4]) - month = int (date[4:6]) - day = int (date[6:8]) - - self.date = datetime.date (year, month, day) - - self.conf = None - - # By default builds are STATE_FAILED unless we find a "complete" file - # in which case they are STATE_COMPLETE - self.state = BuildResult.STATE_FAILED - for file in os.listdir (self.path): - if (file.startswith (".conf")): - conffile = os.path.join (self.path, file) - self.conf = BuildConfiguration.load_from_file (conffile) - elif (file.startswith ("complete")): - self.state = BuildResult.STATE_COMPLETE - else: - self.add_file (file) - - def add_file (self, file): - # Just add the file for now. Don't care about the type. - self.files += [(file, None)] - -class BuildManagerModel (gtk.TreeStore): - """ Model for the BuildManagerTreeView. This derives from gtk.TreeStore - but it abstracts nicely what the columns mean and the setup of the columns - in the model. """ - - (COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \ - (0, 1, 2, 3, 4, 5, 6) - - def __init__ (self): - gtk.TreeStore.__init__ (self, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_OBJECT, - gobject.TYPE_INT64, - gobject.TYPE_INT) - -class BuildManager (gobject.GObject): - """ This class manages the historic builds that have been found in the - "results" directory but is also used for starting a new build.""" - - __gsignals__ = { - 'population-finished' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - ()), - 'populate-error' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - ()) - } - - def update_build_result (self, result, iter): - # Convert the date into something we can sort by. - date = long (time.mktime (result.date.timetuple())) - - # Add a top level entry for the build - - self.model.set (iter, - BuildManagerModel.COL_IDENT, result.identifier, - BuildManagerModel.COL_DESC, result.conf.image, - BuildManagerModel.COL_MACHINE, result.conf.machine, - BuildManagerModel.COL_DISTRO, result.conf.distro, - BuildManagerModel.COL_BUILD_RESULT, result, - BuildManagerModel.COL_DATE, date, - BuildManagerModel.COL_STATE, result.state) - - # And then we use the files in the directory as the children for the - # top level iter. - for file in result.files: - self.model.append (iter, (None, file[0], None, None, None, date, -1)) - - # This function is called as an idle by the BuildManagerPopulaterThread - def add_build_result (self, result): - gtk.gdk.threads_enter() - self.known_builds += [result] - - self.update_build_result (result, self.model.append (None)) - - gtk.gdk.threads_leave() - - def notify_build_finished (self): - # This is a bit of a hack. If we have a running build running then we - # will have a row in the model in STATE_ONGOING. Find it and make it - # as if it was a proper historic build (well, it is completed now....) - - # We need to use the iters here rather than the Python iterator - # interface to the model since we need to pass it into - # update_build_result - - iter = self.model.get_iter_first() - - while (iter): - (ident, state) = self.model.get(iter, - BuildManagerModel.COL_IDENT, - BuildManagerModel.COL_STATE) - - if state == BuildResult.STATE_ONGOING: - result = BuildResult (self.results_directory, ident) - self.update_build_result (result, iter) - iter = self.model.iter_next(iter) - - def notify_build_succeeded (self): - # Write the "complete" file so that when we create the BuildResult - # object we put into the model - - complete_file_path = os.path.join (self.cur_build_directory, "complete") - f = file (complete_file_path, "w") - f.close() - self.notify_build_finished() - - def notify_build_failed (self): - # Without a "complete" file then this will mark the build as failed: - self.notify_build_finished() - - # This function is called as an idle - def emit_population_finished_signal (self): - gtk.gdk.threads_enter() - self.emit ("population-finished") - gtk.gdk.threads_leave() - - class BuildManagerPopulaterThread (threading.Thread): - def __init__ (self, manager, directory): - threading.Thread.__init__ (self) - self.manager = manager - self.directory = directory - - def run (self): - # For each of the "build-<...>" directories .. - - if os.path.exists (self.directory): - for directory in os.listdir (self.directory): - - if not directory.startswith ("build-"): - continue - - build_result = BuildResult (self.directory, directory) - self.manager.add_build_result (build_result) - - gobject.idle_add (BuildManager.emit_population_finished_signal, - self.manager) - - def __init__ (self, server, results_directory): - gobject.GObject.__init__ (self) - - # The builds that we've found from walking the result directory - self.known_builds = [] - - # Save out the bitbake server, we need this for issuing commands to - # the cooker: - self.server = server - - # The TreeStore that we use - self.model = BuildManagerModel () - - # The results directory is where we create (and look for) the - # build-- directories. We need to populate ourselves from - # directory - self.results_directory = results_directory - self.populate_from_directory (self.results_directory) - - def populate_from_directory (self, directory): - thread = BuildManager.BuildManagerPopulaterThread (self, directory) - thread.start() - - # Come up with the name for the next build ident by combining "build-" - # with the date formatted as yyyymmdd and then an ordinal. We do this by - # an optimistic algorithm incrementing the ordinal if we find that it - # already exists. - def get_next_build_ident (self): - today = datetime.date.today () - datestr = str (today.year) + str (today.month) + str (today.day) - - revision = 0 - test_name = "build-%s-%d" % (datestr, revision) - test_path = os.path.join (self.results_directory, test_name) - - while (os.path.exists (test_path)): - revision += 1 - test_name = "build-%s-%d" % (datestr, revision) - test_path = os.path.join (self.results_directory, test_name) - - return test_name - - # Take a BuildConfiguration and then try and build it based on the - # parameters of that configuration. S - def do_build (self, conf): - server = self.server - - # Work out the build directory. Note we actually create the - # directories here since we need to write the ".conf" file. Otherwise - # we could have relied on bitbake's builder thread to actually make - # the directories as it proceeds with the build. - ident = self.get_next_build_ident () - build_directory = os.path.join (self.results_directory, - ident) - self.cur_build_directory = build_directory - os.makedirs (build_directory) - - conffile = os.path.join (build_directory, ".conf") - conf.write_to_file (conffile) - - # Add a row to the model representing this ongoing build. It's kinda a - # fake entry. If this build completes or fails then this gets updated - # with the real stuff like the historic builds - date = long (time.time()) - self.model.append (None, (ident, conf.image, conf.machine, conf.distro, - None, date, BuildResult.STATE_ONGOING)) - try: - server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1]) - server.runCommand(["setVariable", "MACHINE", conf.machine]) - server.runCommand(["setVariable", "DISTRO", conf.distro]) - server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"]) - server.runCommand(["setVariable", "BBFILES", \ - """${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""]) - server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"]) - server.runCommand(["setVariable", "IPK_FEED_URIS", \ - " ".join(conf.get_repos())]) - server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE", - build_directory]) - server.runCommand(["buildTargets", [conf.image], "rootfs"]) - - except Exception, e: - print e - -class BuildManagerTreeView (gtk.TreeView): - """ The tree view for the build manager. This shows the historic builds - and so forth. """ - - # We use this function to control what goes in the cell since we store - # the date in the model as seconds since the epoch (for sorting) and so we - # need to make it human readable. - def date_format_custom_cell_data_func (self, col, cell, model, iter): - date = model.get (iter, BuildManagerModel.COL_DATE)[0] - datestr = time.strftime("%A %d %B %Y", time.localtime(date)) - cell.set_property ("text", datestr) - - # This format function controls what goes in the cell. We use this to map - # the integer state to a string and also to colourise the text - def state_format_custom_cell_data_fun (self, col, cell, model, iter): - state = model.get (iter, BuildManagerModel.COL_STATE)[0] - - if (state == BuildResult.STATE_ONGOING): - cell.set_property ("text", "Active") - cell.set_property ("foreground", "#000000") - elif (state == BuildResult.STATE_FAILED): - cell.set_property ("text", "Failed") - cell.set_property ("foreground", "#ff0000") - elif (state == BuildResult.STATE_COMPLETE): - cell.set_property ("text", "Complete") - cell.set_property ("foreground", "#00ff00") - else: - cell.set_property ("text", "") - - def __init__ (self): - gtk.TreeView.__init__(self) - - # Misc descriptiony thing - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn (None, renderer, - text=BuildManagerModel.COL_DESC) - self.append_column (col) - - # Machine - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn ("Machine", renderer, - text=BuildManagerModel.COL_MACHINE) - self.append_column (col) - - # distro - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn ("Distribution", renderer, - text=BuildManagerModel.COL_DISTRO) - self.append_column (col) - - # date (using a custom function for formatting the cell contents it - # takes epoch -> human readable string) - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn ("Date", renderer, - text=BuildManagerModel.COL_DATE) - self.append_column (col) - col.set_cell_data_func (renderer, - self.date_format_custom_cell_data_func) - - # For status. - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn ("Status", renderer, - text = BuildManagerModel.COL_STATE) - self.append_column (col) - col.set_cell_data_func (renderer, - self.state_format_custom_cell_data_fun) - diff --git a/bitbake-dev/lib/bb/ui/crumbs/puccho.glade b/bitbake-dev/lib/bb/ui/crumbs/puccho.glade deleted file mode 100644 index d7553a6e1..000000000 --- a/bitbake-dev/lib/bb/ui/crumbs/puccho.glade +++ /dev/null @@ -1,606 +0,0 @@ - - - - - - Start a build - GTK_WIN_POS_CENTER_ON_PARENT - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - 6 - 7 - 3 - 5 - 6 - - - True - 12 - - - 6 - - - True - True - 0 - gtk-dialog-error - - - False - False - - - - - True - 0 - If you see this text something is wrong... - True - True - - - 1 - - - - - - - 3 - 2 - 3 - - - - - True - 0 - <b>Build configuration</b> - True - - - 3 - 3 - 4 - - - - - - True - False - - - 1 - 2 - 6 - 7 - - - - - - True - False - 0 - 12 - Image: - - - 6 - 7 - - - - - - True - False - - - 1 - 2 - 5 - 6 - - - - - - True - False - 0 - 12 - Distribution: - - - 5 - 6 - - - - - - True - False - - - 1 - 2 - 4 - 5 - - - - - - True - False - 0 - 12 - Machine: - - - 4 - 5 - - - - - - True - False - True - True - gtk-refresh - True - 0 - - - 2 - 3 - 1 - 2 - - - - - - True - True - 32 - - - 1 - 2 - 1 - 2 - - - - - - True - 0 - 12 - Location: - - - 1 - 2 - - - - - - True - 0 - <b>Repository</b> - True - - - 3 - - - - - - True - - - - - - 2 - 3 - 4 - 5 - - - - - - True - - - - - - 2 - 3 - 5 - 6 - - - - - - True - - - - - - 2 - 3 - 6 - 7 - - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - - - - - - - - - - False - GTK_PACK_END - - - - - - - GTK_WIN_POS_CENTER_ON_PARENT - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - 6 - 7 - 3 - 6 - 6 - - - True - 0 - <b>Repositories</b> - True - - - 3 - - - - - - True - 0 - 12 - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - True - - - - - - - 3 - 2 - 3 - - - - - - True - True - - - 1 - 3 - 1 - 2 - - - - - - True - 0 - <b>Additional packages</b> - True - - - 3 - 4 - 5 - - - - - - True - 0 - 0 - - - True - 0 - 0 - 12 - Location: - - - - - 1 - 2 - - - - - - True - 1 - 0 - - - True - 5 - - - True - True - True - gtk-remove - True - 0 - - - - - True - True - True - gtk-edit - True - 0 - - - 1 - - - - - True - True - True - gtk-add - True - 0 - - - 2 - - - - - - - 1 - 3 - 3 - 4 - - - - - - True - - - - - - 3 - 4 - - - - - - True - 0 - 0 - 12 - Search: - - - 5 - 6 - - - - - - True - True - - - 1 - 3 - 5 - 6 - - - - - - True - 0 - 12 - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - True - - - - - - - 3 - 6 - 7 - - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-close - True - 0 - - - - - False - GTK_PACK_END - - - - - - - - - True - - - True - - - True - Build - gtk-execute - - - False - - - - - False - - - - - True - True - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - - - - False - True - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - - - - True - True - - - - - 1 - - - - - - diff --git a/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py b/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py deleted file mode 100644 index 401559255..000000000 --- a/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py +++ /dev/null @@ -1,180 +0,0 @@ -# -# BitBake Graphical GTK User Interface -# -# Copyright (C) 2008 Intel Corporation -# -# Authored by Rob Bradford -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gtk -import gobject - -class RunningBuildModel (gtk.TreeStore): - (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5) - def __init__ (self): - gtk.TreeStore.__init__ (self, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_BOOLEAN) - -class RunningBuild (gobject.GObject): - __gsignals__ = { - 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - ()), - 'build-failed' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - ()) - } - pids_to_task = {} - tasks_to_iter = {} - - def __init__ (self): - gobject.GObject.__init__ (self) - self.model = RunningBuildModel() - - def handle_event (self, event): - # Handle an event from the event queue, this may result in updating - # the model and thus the UI. Or it may be to tell us that the build - # has finished successfully (or not, as the case may be.) - - parent = None - pid = 0 - package = None - task = None - - # If we have a pid attached to this message/event try and get the - # (package, task) pair for it. If we get that then get the parent iter - # for the message. - if hassattr(event, 'pid'): - pid = event.pid - if self.pids_to_task.has_key(pid): - (package, task) = self.pids_to_task[pid] - parent = self.tasks_to_iter[(package, task)] - - if isinstance(event, bb.msg.Msg): - # Set a pretty icon for the message based on it's type. - if isinstance(event, bb.msg.MsgWarn): - icon = "dialog-warning" - elif isinstance(event, bb.msg.MsgErr): - icon = "dialog-error" - else: - icon = None - - # Ignore the "Running task i of n .." messages - if (event._message.startswith ("Running task")): - return - - # Add the message to the tree either at the top level if parent is - # None otherwise as a descendent of a task. - self.model.append (parent, - (event.__name__.split()[-1], # e.g. MsgWarn, MsgError - package, - task, - event._message, - icon, - False)) - elif isinstance(event, bb.build.TaskStarted): - (package, task) = (event._package, event._task) - - # Save out this PID. - self.pids_to_task[pid] = (package,task) - - # Check if we already have this package in our model. If so then - # that can be the parent for the task. Otherwise we create a new - # top level for the package. - if (self.tasks_to_iter.has_key ((package, None))): - parent = self.tasks_to_iter[(package, None)] - else: - parent = self.model.append (None, (None, - package, - None, - "Package: %s" % (package), - None, - False)) - self.tasks_to_iter[(package, None)] = parent - - # Because this parent package now has an active child mark it as - # such. - self.model.set(parent, self.model.COL_ICON, "gtk-execute") - - # Add an entry in the model for this task - i = self.model.append (parent, (None, - package, - task, - "Task: %s" % (task), - None, - False)) - - # Save out the iter so that we can find it when we have a message - # that we need to attach to a task. - self.tasks_to_iter[(package, task)] = i - - # Mark this task as active. - self.model.set(i, self.model.COL_ICON, "gtk-execute") - - elif isinstance(event, bb.build.Task): - - if isinstance(event, bb.build.TaskFailed): - # Mark the task as failed - i = self.tasks_to_iter[(package, task)] - self.model.set(i, self.model.COL_ICON, "dialog-error") - - # Mark the parent package as failed - i = self.tasks_to_iter[(package, None)] - self.model.set(i, self.model.COL_ICON, "dialog-error") - else: - # Mark the task as inactive - i = self.tasks_to_iter[(package, task)] - self.model.set(i, self.model.COL_ICON, None) - - # Mark the parent package as inactive - i = self.tasks_to_iter[(package, None)] - self.model.set(i, self.model.COL_ICON, None) - - - # Clear the iters and the pids since when the task goes away the - # pid will no longer be used for messages - del self.tasks_to_iter[(package, task)] - del self.pids_to_task[pid] - - elif isinstance(event, bb.event.BuildCompleted): - failures = int (event._failures) - - # Emit the appropriate signal depending on the number of failures - if (failures > 1): - self.emit ("build-failed") - else: - self.emit ("build-succeeded") - -class RunningBuildTreeView (gtk.TreeView): - def __init__ (self): - gtk.TreeView.__init__ (self) - - # The icon that indicates whether we're building or failed. - renderer = gtk.CellRendererPixbuf () - col = gtk.TreeViewColumn ("Status", renderer) - col.add_attribute (renderer, "icon-name", 4) - self.append_column (col) - - # The message of the build. - renderer = gtk.CellRendererText () - col = gtk.TreeViewColumn ("Message", renderer, text=3) - self.append_column (col) - - diff --git a/bitbake-dev/lib/bb/ui/depexp.py b/bitbake-dev/lib/bb/ui/depexp.py deleted file mode 100644 index cfa5b6564..000000000 --- a/bitbake-dev/lib/bb/ui/depexp.py +++ /dev/null @@ -1,272 +0,0 @@ -# -# BitBake Graphical GTK based Dependency Explorer -# -# Copyright (C) 2007 Ross Burton -# Copyright (C) 2007 - 2008 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gobject -import gtk -import threading -import xmlrpclib - -# Package Model -(COL_PKG_NAME) = (0) - -# Dependency Model -(TYPE_DEP, TYPE_RDEP) = (0, 1) -(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2) - -class PackageDepView(gtk.TreeView): - def __init__(self, model, dep_type, label): - gtk.TreeView.__init__(self) - self.current = None - self.dep_type = dep_type - self.filter_model = model.filter_new() - self.filter_model.set_visible_func(self._filter) - self.set_model(self.filter_model) - #self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) - self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE)) - - def _filter(self, model, iter): - (this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT) - if this_type != self.dep_type: return False - return package == self.current - - def set_current_package(self, package): - self.current = package - self.filter_model.refilter() - -class PackageReverseDepView(gtk.TreeView): - def __init__(self, model, label): - gtk.TreeView.__init__(self) - self.current = None - self.filter_model = model.filter_new() - self.filter_model.set_visible_func(self._filter) - self.set_model(self.filter_model) - self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT)) - - def _filter(self, model, iter): - package = model.get_value(iter, COL_DEP_PACKAGE) - return package == self.current - - def set_current_package(self, package): - self.current = package - self.filter_model.refilter() - -class DepExplorer(gtk.Window): - def __init__(self): - gtk.Window.__init__(self) - self.set_title("Dependency Explorer") - self.set_default_size(500, 500) - self.connect("delete-event", gtk.main_quit) - - # Create the data models - self.pkg_model = gtk.ListStore(gobject.TYPE_STRING) - self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING) - - pane = gtk.HPaned() - pane.set_position(250) - self.add(pane) - - # The master list of packages - scrolled = gtk.ScrolledWindow() - scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled.set_shadow_type(gtk.SHADOW_IN) - self.pkg_treeview = gtk.TreeView(self.pkg_model) - self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed) - self.pkg_treeview.append_column(gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)) - pane.add1(scrolled) - scrolled.add(self.pkg_treeview) - - box = gtk.VBox(homogeneous=True, spacing=4) - - # Runtime Depends - scrolled = gtk.ScrolledWindow() - scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled.set_shadow_type(gtk.SHADOW_IN) - self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends") - self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) - scrolled.add(self.rdep_treeview) - box.add(scrolled) - - # Build Depends - scrolled = gtk.ScrolledWindow() - scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled.set_shadow_type(gtk.SHADOW_IN) - self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends") - self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) - scrolled.add(self.dep_treeview) - box.add(scrolled) - pane.add2(box) - - # Reverse Depends - scrolled = gtk.ScrolledWindow() - scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled.set_shadow_type(gtk.SHADOW_IN) - self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends") - self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT) - scrolled.add(self.revdep_treeview) - box.add(scrolled) - pane.add2(box) - - self.show_all() - - def on_package_activated(self, treeview, path, column, data_col): - model = treeview.get_model() - package = model.get_value(model.get_iter(path), data_col) - - pkg_path = [] - def finder(model, path, iter, needle): - package = model.get_value(iter, COL_PKG_NAME) - if package == needle: - pkg_path.append(path) - return True - else: - return False - self.pkg_model.foreach(finder, package) - if pkg_path: - self.pkg_treeview.get_selection().select_path(pkg_path[0]) - self.pkg_treeview.scroll_to_cell(pkg_path[0]) - - def on_cursor_changed(self, selection): - (model, it) = selection.get_selected() - if iter is None: - current_package = None - else: - current_package = model.get_value(it, COL_PKG_NAME) - self.rdep_treeview.set_current_package(current_package) - self.dep_treeview.set_current_package(current_package) - self.revdep_treeview.set_current_package(current_package) - - -def parse(depgraph, pkg_model, depends_model): - - for package in depgraph["pn"]: - pkg_model.set(pkg_model.append(), COL_PKG_NAME, package) - - for package in depgraph["depends"]: - for depend in depgraph["depends"][package]: - depends_model.set (depends_model.append(), - COL_DEP_TYPE, TYPE_DEP, - COL_DEP_PARENT, package, - COL_DEP_PACKAGE, depend) - - for package in depgraph["rdepends-pn"]: - for rdepend in depgraph["rdepends-pn"][package]: - depends_model.set (depends_model.append(), - COL_DEP_TYPE, TYPE_RDEP, - COL_DEP_PARENT, package, - COL_DEP_PACKAGE, rdepend) - -class ProgressBar(gtk.Window): - def __init__(self): - - gtk.Window.__init__(self) - self.set_title("Parsing .bb files, please wait...") - self.set_default_size(500, 0) - self.connect("delete-event", gtk.main_quit) - - self.progress = gtk.ProgressBar() - self.add(self.progress) - self.show_all() - -class gtkthread(threading.Thread): - quit = threading.Event() - def __init__(self, shutdown): - threading.Thread.__init__(self) - self.setDaemon(True) - self.shutdown = shutdown - - def run(self): - gobject.threads_init() - gtk.gdk.threads_init() - gtk.main() - gtkthread.quit.set() - -def init(server, eventHandler): - - try: - cmdline = server.runCommand(["getCmdLineAction"]) - if not cmdline or cmdline[0] != "generateDotGraph": - print "This UI is only compatible with the -g option" - return - ret = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]]) - if ret != True: - print "Couldn't run command! %s" % ret - return - except xmlrpclib.Fault, x: - print "XMLRPC Fault getting commandline:\n %s" % x - return - - shutdown = 0 - - gtkgui = gtkthread(shutdown) - gtkgui.start() - - gtk.gdk.threads_enter() - pbar = ProgressBar() - dep = DepExplorer() - gtk.gdk.threads_leave() - - while True: - try: - event = eventHandler.waitEvent(0.25) - if gtkthread.quit.isSet(): - break - - if event is None: - continue - if isinstance(event, bb.event.ParseProgress): - x = event.sofar - y = event.total - if x == y: - print("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors." - % ( event.cached, event.parsed, event.skipped, event.masked, event.errors)) - pbar.hide() - gtk.gdk.threads_enter() - pbar.progress.set_fraction(float(x)/float(y)) - pbar.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y)) - gtk.gdk.threads_leave() - continue - - if isinstance(event, bb.event.DepTreeGenerated): - gtk.gdk.threads_enter() - parse(event._depgraph, dep.pkg_model, dep.depends_model) - gtk.gdk.threads_leave() - - if isinstance(event, bb.command.CookerCommandCompleted): - continue - if isinstance(event, bb.command.CookerCommandFailed): - print "Command execution failed: %s" % event.error - break - if isinstance(event, bb.cooker.CookerExit): - break - - continue - - except KeyboardInterrupt: - if shutdown == 2: - print "\nThird Keyboard Interrupt, exit.\n" - break - if shutdown == 1: - print "\nSecond Keyboard Interrupt, stopping...\n" - server.runCommand(["stateStop"]) - if shutdown == 0: - print "\nKeyboard Interrupt, closing down...\n" - server.runCommand(["stateShutdown"]) - shutdown = shutdown + 1 - pass - diff --git a/bitbake-dev/lib/bb/ui/goggle.py b/bitbake-dev/lib/bb/ui/goggle.py deleted file mode 100644 index 94995d82d..000000000 --- a/bitbake-dev/lib/bb/ui/goggle.py +++ /dev/null @@ -1,77 +0,0 @@ -# -# BitBake Graphical GTK User Interface -# -# Copyright (C) 2008 Intel Corporation -# -# Authored by Rob Bradford -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gobject -import gtk -import xmlrpclib -from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild - -def event_handle_idle_func (eventHandler, build): - - # Consume as many messages as we can in the time available to us - event = eventHandler.getEvent() - while event: - build.handle_event (event) - event = eventHandler.getEvent() - - return True - -class MainWindow (gtk.Window): - def __init__ (self): - gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL) - - # Setup tree view and the scrolled window - scrolled_window = gtk.ScrolledWindow () - self.add (scrolled_window) - self.cur_build_tv = RunningBuildTreeView() - scrolled_window.add (self.cur_build_tv) - -def init (server, eventHandler): - gobject.threads_init() - gtk.gdk.threads_init() - - window = MainWindow () - window.show_all () - - # Create the object for the current build - running_build = RunningBuild () - window.cur_build_tv.set_model (running_build.model) - try: - cmdline = server.runCommand(["getCmdLineAction"]) - print cmdline - if not cmdline: - return 1 - ret = server.runCommand(cmdline) - if ret != True: - print "Couldn't get default commandline! %s" % ret - return 1 - except xmlrpclib.Fault, x: - print "XMLRPC Fault getting commandline:\n %s" % x - return 1 - - # Use a timeout function for probing the event queue to find out if we - # have a message waiting for us. - gobject.timeout_add (200, - event_handle_idle_func, - eventHandler, - running_build) - - gtk.main() - diff --git a/bitbake-dev/lib/bb/ui/knotty.py b/bitbake-dev/lib/bb/ui/knotty.py deleted file mode 100644 index c69fd6ca6..000000000 --- a/bitbake-dev/lib/bb/ui/knotty.py +++ /dev/null @@ -1,162 +0,0 @@ -# -# BitBake (No)TTY UI Implementation -# -# Handling output to TTYs or files (no TTY) -# -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import os - -import sys -import itertools -import xmlrpclib - -parsespin = itertools.cycle( r'|/-\\' ) - -def init(server, eventHandler): - - # Get values of variables which control our output - includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"]) - loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) - - try: - cmdline = server.runCommand(["getCmdLineAction"]) - #print cmdline - if not cmdline: - return 1 - ret = server.runCommand(cmdline) - if ret != True: - print "Couldn't get default commandline! %s" % ret - return 1 - except xmlrpclib.Fault, x: - print "XMLRPC Fault getting commandline:\n %s" % x - return 1 - - shutdown = 0 - return_value = 0 - while True: - try: - event = eventHandler.waitEvent(0.25) - if event is None: - continue - #print event - if isinstance(event, bb.msg.MsgPlain): - print event._message - continue - if isinstance(event, bb.msg.MsgDebug): - print 'DEBUG: ' + event._message - continue - if isinstance(event, bb.msg.MsgNote): - print 'NOTE: ' + event._message - continue - if isinstance(event, bb.msg.MsgWarn): - print 'WARNING: ' + event._message - continue - if isinstance(event, bb.msg.MsgError): - return_value = 1 - print 'ERROR: ' + event._message - continue - if isinstance(event, bb.msg.MsgFatal): - return_value = 1 - print 'FATAL: ' + event._message - break - if isinstance(event, bb.build.TaskFailed): - return_value = 1 - logfile = event.logfile - if logfile: - print "ERROR: Logfile of failure stored in %s." % logfile - if 1 or includelogs: - print "Log data follows:" - f = open(logfile, "r") - lines = [] - while True: - l = f.readline() - if l == '': - break - l = l.rstrip() - if loglines: - lines.append(' | %s' % l) - if len(lines) > int(loglines): - lines.pop(0) - else: - print '| %s' % l - f.close() - if lines: - for line in lines: - print line - if isinstance(event, bb.build.TaskBase): - print "NOTE: %s" % event._message - continue - if isinstance(event, bb.event.ParseProgress): - x = event.sofar - y = event.total - if os.isatty(sys.stdout.fileno()): - sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) - sys.stdout.flush() - else: - if x == 1: - sys.stdout.write("Parsing .bb files, please wait...") - sys.stdout.flush() - if x == y: - sys.stdout.write("done.") - sys.stdout.flush() - if x == y: - print("\nParsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." - % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors)) - continue - - if isinstance(event, bb.command.CookerCommandCompleted): - break - if isinstance(event, bb.command.CookerCommandSetExitCode): - return_value = event.exitcode - continue - if isinstance(event, bb.command.CookerCommandFailed): - return_value = 1 - print "Command execution failed: %s" % event.error - break - if isinstance(event, bb.cooker.CookerExit): - break - - # ignore - if isinstance(event, bb.event.BuildStarted): - continue - if isinstance(event, bb.event.BuildCompleted): - continue - if isinstance(event, bb.event.MultipleProviders): - continue - if isinstance(event, bb.runqueue.runQueueEvent): - continue - if isinstance(event, bb.event.StampUpdate): - continue - if isinstance(event, bb.event.ConfigParsed): - continue - if isinstance(event, bb.event.RecipeParsed): - continue - print "Unknown Event: %s" % event - - except KeyboardInterrupt: - if shutdown == 2: - print "\nThird Keyboard Interrupt, exit.\n" - break - if shutdown == 1: - print "\nSecond Keyboard Interrupt, stopping...\n" - server.runCommand(["stateStop"]) - if shutdown == 0: - print "\nKeyboard Interrupt, closing down...\n" - server.runCommand(["stateShutdown"]) - shutdown = shutdown + 1 - pass - return return_value diff --git a/bitbake-dev/lib/bb/ui/ncurses.py b/bitbake-dev/lib/bb/ui/ncurses.py deleted file mode 100644 index 14310dc12..000000000 --- a/bitbake-dev/lib/bb/ui/ncurses.py +++ /dev/null @@ -1,335 +0,0 @@ -# -# BitBake Curses UI Implementation -# -# Implements an ncurses frontend for the BitBake utility. -# -# Copyright (C) 2006 Michael 'Mickey' Lauer -# Copyright (C) 2006-2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" - We have the following windows: - - 1.) Main Window: Shows what we are ultimately building and how far we are. Includes status bar - 2.) Thread Activity Window: Shows one status line for every concurrent bitbake thread. - 3.) Command Line Window: Contains an interactive command line where you can interact w/ Bitbake. - - Basic window layout is like that: - - |---------------------------------------------------------| - |
| | - | | 0: foo do_compile complete| - | Building Gtk+-2.6.10 | 1: bar do_patch complete | - | Status: 60% | ... | - | | ... | - | | ... | - |---------------------------------------------------------| - | | - |>>> which virtual/kernel | - |openzaurus-kernel | - |>>> _ | - |---------------------------------------------------------| - -""" - -import os, sys, curses, itertools, time -import bb -import xmlrpclib -from bb import ui -from bb.ui import uihelper - -parsespin = itertools.cycle( r'|/-\\' ) - -X = 0 -Y = 1 -WIDTH = 2 -HEIGHT = 3 - -MAXSTATUSLENGTH = 32 - -class NCursesUI: - """ - NCurses UI Class - """ - class Window: - """Base Window Class""" - def __init__( self, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ): - self.win = curses.newwin( height, width, y, x ) - self.dimensions = ( x, y, width, height ) - """ - if curses.has_colors(): - color = 1 - curses.init_pair( color, fg, bg ) - self.win.bkgdset( ord(' '), curses.color_pair(color) ) - else: - self.win.bkgdset( ord(' '), curses.A_BOLD ) - """ - self.erase() - self.setScrolling() - self.win.noutrefresh() - - def erase( self ): - self.win.erase() - - def setScrolling( self, b = True ): - self.win.scrollok( b ) - self.win.idlok( b ) - - def setBoxed( self ): - self.boxed = True - self.win.box() - self.win.noutrefresh() - - def setText( self, x, y, text, *args ): - self.win.addstr( y, x, text, *args ) - self.win.noutrefresh() - - def appendText( self, text, *args ): - self.win.addstr( text, *args ) - self.win.noutrefresh() - - def drawHline( self, y ): - self.win.hline( y, 0, curses.ACS_HLINE, self.dimensions[WIDTH] ) - self.win.noutrefresh() - - class DecoratedWindow( Window ): - """Base class for windows with a box and a title bar""" - def __init__( self, title, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ): - NCursesUI.Window.__init__( self, x+1, y+3, width-2, height-4, fg, bg ) - self.decoration = NCursesUI.Window( x, y, width, height, fg, bg ) - self.decoration.setBoxed() - self.decoration.win.hline( 2, 1, curses.ACS_HLINE, width-2 ) - self.setTitle( title ) - - def setTitle( self, title ): - self.decoration.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) - - #-------------------------------------------------------------------------# -# class TitleWindow( Window ): - #-------------------------------------------------------------------------# -# """Title Window""" -# def __init__( self, x, y, width, height ): -# NCursesUI.Window.__init__( self, x, y, width, height ) -# version = bb.__version__ -# title = "BitBake %s" % version -# credit = "(C) 2003-2007 Team BitBake" -# #self.win.hline( 2, 1, curses.ACS_HLINE, width-2 ) -# self.win.border() -# self.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) -# self.setText( 1, 2, credit.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) - - #-------------------------------------------------------------------------# - class ThreadActivityWindow( DecoratedWindow ): - #-------------------------------------------------------------------------# - """Thread Activity Window""" - def __init__( self, x, y, width, height ): - NCursesUI.DecoratedWindow.__init__( self, "Thread Activity", x, y, width, height ) - - def setStatus( self, thread, text ): - line = "%02d: %s" % ( thread, text ) - width = self.dimensions[WIDTH] - if ( len(line) > width ): - line = line[:width-3] + "..." - else: - line = line.ljust( width ) - self.setText( 0, thread, line ) - - #-------------------------------------------------------------------------# - class MainWindow( DecoratedWindow ): - #-------------------------------------------------------------------------# - """Main Window""" - def __init__( self, x, y, width, height ): - self.StatusPosition = width - MAXSTATUSLENGTH - NCursesUI.DecoratedWindow.__init__( self, None, x, y, width, height ) - curses.nl() - - def setTitle( self, title ): - title = "BitBake %s" % bb.__version__ - self.decoration.setText( 2, 1, title, curses.A_BOLD ) - self.decoration.setText( self.StatusPosition - 8, 1, "Status:", curses.A_BOLD ) - - def setStatus(self, status): - while len(status) < MAXSTATUSLENGTH: - status = status + " " - self.decoration.setText( self.StatusPosition, 1, status, curses.A_BOLD ) - - - #-------------------------------------------------------------------------# - class ShellOutputWindow( DecoratedWindow ): - #-------------------------------------------------------------------------# - """Interactive Command Line Output""" - def __init__( self, x, y, width, height ): - NCursesUI.DecoratedWindow.__init__( self, "Command Line Window", x, y, width, height ) - - #-------------------------------------------------------------------------# - class ShellInputWindow( Window ): - #-------------------------------------------------------------------------# - """Interactive Command Line Input""" - def __init__( self, x, y, width, height ): - NCursesUI.Window.__init__( self, x, y, width, height ) - -# put that to the top again from curses.textpad import Textbox -# self.textbox = Textbox( self.win ) -# t = threading.Thread() -# t.run = self.textbox.edit -# t.start() - - #-------------------------------------------------------------------------# - def main(self, stdscr, server, eventHandler): - #-------------------------------------------------------------------------# - height, width = stdscr.getmaxyx() - - # for now split it like that: - # MAIN_y + THREAD_y = 2/3 screen at the top - # MAIN_x = 2/3 left, THREAD_y = 1/3 right - # CLI_y = 1/3 of screen at the bottom - # CLI_x = full - - main_left = 0 - main_top = 0 - main_height = ( height / 3 * 2 ) - main_width = ( width / 3 ) * 2 - clo_left = main_left - clo_top = main_top + main_height - clo_height = height - main_height - main_top - 1 - clo_width = width - cli_left = main_left - cli_top = clo_top + clo_height - cli_height = 1 - cli_width = width - thread_left = main_left + main_width - thread_top = main_top - thread_height = main_height - thread_width = width - main_width - - #tw = self.TitleWindow( 0, 0, width, main_top ) - mw = self.MainWindow( main_left, main_top, main_width, main_height ) - taw = self.ThreadActivityWindow( thread_left, thread_top, thread_width, thread_height ) - clo = self.ShellOutputWindow( clo_left, clo_top, clo_width, clo_height ) - cli = self.ShellInputWindow( cli_left, cli_top, cli_width, cli_height ) - cli.setText( 0, 0, "BB>" ) - - mw.setStatus("Idle") - - helper = uihelper.BBUIHelper() - shutdown = 0 - - try: - cmdline = server.runCommand(["getCmdLineAction"]) - if not cmdline: - return - ret = server.runCommand(cmdline) - if ret != True: - print "Couldn't get default commandlind! %s" % ret - return - except xmlrpclib.Fault, x: - print "XMLRPC Fault getting commandline:\n %s" % x - return - - exitflag = False - while not exitflag: - try: - event = eventHandler.waitEvent(0.25) - if not event: - continue - helper.eventHandler(event) - #mw.appendText("%s\n" % event[0]) - if isinstance(event, bb.build.Task): - mw.appendText("NOTE: %s\n" % event._message) - if isinstance(event, bb.msg.MsgDebug): - mw.appendText('DEBUG: ' + event._message + '\n') - if isinstance(event, bb.msg.MsgNote): - mw.appendText('NOTE: ' + event._message + '\n') - if isinstance(event, bb.msg.MsgWarn): - mw.appendText('WARNING: ' + event._message + '\n') - if isinstance(event, bb.msg.MsgError): - mw.appendText('ERROR: ' + event._message + '\n') - if isinstance(event, bb.msg.MsgFatal): - mw.appendText('FATAL: ' + event._message + '\n') - if isinstance(event, bb.event.ParseProgress): - x = event.sofar - y = event.total - if x == y: - mw.setStatus("Idle") - mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked." - % ( event.cached, event.parsed, event.skipped, event.masked )) - else: - mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) -# if isinstance(event, bb.build.TaskFailed): -# if event.logfile: -# if data.getVar("BBINCLUDELOGS", d): -# bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile) -# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d) -# if number_of_lines: -# os.system('tail -n%s %s' % (number_of_lines, logfile)) -# else: -# f = open(logfile, "r") -# while True: -# l = f.readline() -# if l == '': -# break -# l = l.rstrip() -# print '| %s' % l -# f.close() -# else: -# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile) - - if isinstance(event, bb.command.CookerCommandCompleted): - exitflag = True - if isinstance(event, bb.command.CookerCommandFailed): - mw.appendText("Command execution failed: %s" % event.error) - time.sleep(2) - exitflag = True - if isinstance(event, bb.cooker.CookerExit): - exitflag = True - - if helper.needUpdate: - activetasks, failedtasks = helper.getTasks() - taw.erase() - taw.setText(0, 0, "") - if activetasks: - taw.appendText("Active Tasks:\n") - for task in activetasks: - taw.appendText(task) - if failedtasks: - taw.appendText("Failed Tasks:\n") - for task in failedtasks: - taw.appendText(task) - - curses.doupdate() - except KeyboardInterrupt: - if shutdown == 2: - mw.appendText("Third Keyboard Interrupt, exit.\n") - exitflag = True - if shutdown == 1: - mw.appendText("Second Keyboard Interrupt, stopping...\n") - server.runCommand(["stateStop"]) - if shutdown == 0: - mw.appendText("Keyboard Interrupt, closing down...\n") - server.runCommand(["stateShutdown"]) - shutdown = shutdown + 1 - pass - -def init(server, eventHandler): - if not os.isatty(sys.stdout.fileno()): - print "FATAL: Unable to run 'ncurses' UI without a TTY." - return - ui = NCursesUI() - try: - curses.wrapper(ui.main, server, eventHandler) - except: - import traceback - traceback.print_exc() - diff --git a/bitbake-dev/lib/bb/ui/puccho.py b/bitbake-dev/lib/bb/ui/puccho.py deleted file mode 100644 index 713aa1f4a..000000000 --- a/bitbake-dev/lib/bb/ui/puccho.py +++ /dev/null @@ -1,425 +0,0 @@ -# -# BitBake Graphical GTK User Interface -# -# Copyright (C) 2008 Intel Corporation -# -# Authored by Rob Bradford -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gtk -import gobject -import gtk.glade -import threading -import urllib2 -import os - -from bb.ui.crumbs.buildmanager import BuildManager, BuildConfiguration -from bb.ui.crumbs.buildmanager import BuildManagerTreeView - -from bb.ui.crumbs.runningbuild import RunningBuild, RunningBuildTreeView - -# The metadata loader is used by the BuildSetupDialog to download the -# available options to populate the dialog -class MetaDataLoader(gobject.GObject): - """ This class provides the mechanism for loading the metadata (the - fetching and parsing) from a given URL. The metadata encompasses details - on what machines are available. The distribution and images available for - the machine and the the uris to use for building the given machine.""" - __gsignals__ = { - 'success' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - ()), - 'error' : (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - (gobject.TYPE_STRING,)) - } - - # We use these little helper functions to ensure that we take the gdk lock - # when emitting the signal. These functions are called as idles (so that - # they happen in the gtk / main thread's main loop. - def emit_error_signal (self, remark): - gtk.gdk.threads_enter() - self.emit ("error", remark) - gtk.gdk.threads_leave() - - def emit_success_signal (self): - gtk.gdk.threads_enter() - self.emit ("success") - gtk.gdk.threads_leave() - - def __init__ (self): - gobject.GObject.__init__ (self) - - class LoaderThread(threading.Thread): - """ This class provides an asynchronous loader for the metadata (by - using threads and signals). This is useful since the metadata may be - at a remote URL.""" - class LoaderImportException (Exception): - pass - - def __init__(self, loader, url): - threading.Thread.__init__ (self) - self.url = url - self.loader = loader - - def run (self): - result = {} - try: - f = urllib2.urlopen (self.url) - - # Parse the metadata format. The format is.... - # ;|...;|...;|... - for line in f.readlines(): - components = line.split(";") - if (len (components) < 4): - raise MetaDataLoader.LoaderThread.LoaderImportException - machine = components[0] - distros = components[1].split("|") - images = components[2].split("|") - urls = components[3].split("|") - - result[machine] = (distros, images, urls) - - # Create an object representing this *potential* - # configuration. It can become concrete if the machine, distro - # and image are all chosen in the UI - configuration = BuildConfiguration() - configuration.metadata_url = self.url - configuration.machine_options = result - self.loader.configuration = configuration - - # Emit that we've actually got a configuration - gobject.idle_add (MetaDataLoader.emit_success_signal, - self.loader) - - except MetaDataLoader.LoaderThread.LoaderImportException, e: - gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader, - "Repository metadata corrupt") - except Exception, e: - gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader, - "Unable to download repository metadata") - print e - - def try_fetch_from_url (self, url): - # Try and download the metadata. Firing a signal if successful - thread = MetaDataLoader.LoaderThread(self, url) - thread.start() - -class BuildSetupDialog (gtk.Dialog): - RESPONSE_BUILD = 1 - - # A little helper method that just sets the states on the widgets based on - # whether we've got good metadata or not. - def set_configurable (self, configurable): - if (self.configurable == configurable): - return - - self.configurable = configurable - for widget in self.conf_widgets: - widget.set_sensitive (configurable) - - if not configurable: - self.machine_combo.set_active (-1) - self.distribution_combo.set_active (-1) - self.image_combo.set_active (-1) - - # GTK widget callbacks - def refresh_button_clicked (self, button): - # Refresh button clicked. - - url = self.location_entry.get_chars (0, -1) - self.loader.try_fetch_from_url(url) - - def repository_entry_editable_changed (self, entry): - if (len (entry.get_chars (0, -1)) > 0): - self.refresh_button.set_sensitive (True) - else: - self.refresh_button.set_sensitive (False) - self.clear_status_message() - - # If we were previously configurable we are no longer since the - # location entry has been changed - self.set_configurable (False) - - def machine_combo_changed (self, combobox): - active_iter = combobox.get_active_iter() - - if not active_iter: - return - - model = combobox.get_model() - - if model: - chosen_machine = model.get (active_iter, 0)[0] - - (distros_model, images_model) = \ - self.loader.configuration.get_distro_and_images_models (chosen_machine) - - self.distribution_combo.set_model (distros_model) - self.image_combo.set_model (images_model) - - # Callbacks from the loader - def loader_success_cb (self, loader): - self.status_image.set_from_icon_name ("info", - gtk.ICON_SIZE_BUTTON) - self.status_image.show() - self.status_label.set_label ("Repository metadata successfully downloaded") - - # Set the models on the combo boxes based on the models generated from - # the configuration that the loader has created - - # We just need to set the machine here, that then determines the - # distro and image options. Cunning huh? :-) - - self.configuration = self.loader.configuration - model = self.configuration.get_machines_model () - self.machine_combo.set_model (model) - - self.set_configurable (True) - - def loader_error_cb (self, loader, message): - self.status_image.set_from_icon_name ("error", - gtk.ICON_SIZE_BUTTON) - self.status_image.show() - self.status_label.set_text ("Error downloading repository metadata") - for widget in self.conf_widgets: - widget.set_sensitive (False) - - def clear_status_message (self): - self.status_image.hide() - self.status_label.set_label ( - """Enter the repository location and press _Refresh""") - - def __init__ (self): - gtk.Dialog.__init__ (self) - - # Cancel - self.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) - - # Build - button = gtk.Button ("_Build", None, True) - image = gtk.Image () - image.set_from_stock (gtk.STOCK_EXECUTE,gtk.ICON_SIZE_BUTTON) - button.set_image (image) - self.add_action_widget (button, BuildSetupDialog.RESPONSE_BUILD) - button.show_all () - - # Pull in *just* the table from the Glade XML data. - gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade", - root = "build_table") - table = gxml.get_widget ("build_table") - self.vbox.pack_start (table, True, False, 0) - - # Grab all the widgets that we need to turn on/off when we refresh... - self.conf_widgets = [] - self.conf_widgets += [gxml.get_widget ("machine_label")] - self.conf_widgets += [gxml.get_widget ("distribution_label")] - self.conf_widgets += [gxml.get_widget ("image_label")] - self.conf_widgets += [gxml.get_widget ("machine_combo")] - self.conf_widgets += [gxml.get_widget ("distribution_combo")] - self.conf_widgets += [gxml.get_widget ("image_combo")] - - # Grab the status widgets - self.status_image = gxml.get_widget ("status_image") - self.status_label = gxml.get_widget ("status_label") - - # Grab the refresh button and connect to the clicked signal - self.refresh_button = gxml.get_widget ("refresh_button") - self.refresh_button.connect ("clicked", self.refresh_button_clicked) - - # Grab the location entry and connect to editable::changed - self.location_entry = gxml.get_widget ("location_entry") - self.location_entry.connect ("changed", - self.repository_entry_editable_changed) - - # Grab the machine combo and hook onto the changed signal. This then - # allows us to populate the distro and image combos - self.machine_combo = gxml.get_widget ("machine_combo") - self.machine_combo.connect ("changed", self.machine_combo_changed) - - # Setup the combo - cell = gtk.CellRendererText() - self.machine_combo.pack_start(cell, True) - self.machine_combo.add_attribute(cell, 'text', 0) - - # Grab the distro and image combos. We need these to populate with - # models once the machine is chosen - self.distribution_combo = gxml.get_widget ("distribution_combo") - cell = gtk.CellRendererText() - self.distribution_combo.pack_start(cell, True) - self.distribution_combo.add_attribute(cell, 'text', 0) - - self.image_combo = gxml.get_widget ("image_combo") - cell = gtk.CellRendererText() - self.image_combo.pack_start(cell, True) - self.image_combo.add_attribute(cell, 'text', 0) - - # Put the default descriptive text in the status box - self.clear_status_message() - - # Mark as non-configurable, this is just greys out the widgets the - # user can't yet use - self.configurable = False - self.set_configurable(False) - - # Show the table - table.show_all () - - # The loader and some signals connected to it to update the status - # area - self.loader = MetaDataLoader() - self.loader.connect ("success", self.loader_success_cb) - self.loader.connect ("error", self.loader_error_cb) - - def update_configuration (self): - """ A poorly named function but it updates the internal configuration - from the widgets. This can make that configuration concrete and can - thus be used for building """ - # Extract the chosen machine from the combo - model = self.machine_combo.get_model() - active_iter = self.machine_combo.get_active_iter() - if (active_iter): - self.configuration.machine = model.get(active_iter, 0)[0] - - # Extract the chosen distro from the combo - model = self.distribution_combo.get_model() - active_iter = self.distribution_combo.get_active_iter() - if (active_iter): - self.configuration.distro = model.get(active_iter, 0)[0] - - # Extract the chosen image from the combo - model = self.image_combo.get_model() - active_iter = self.image_combo.get_active_iter() - if (active_iter): - self.configuration.image = model.get(active_iter, 0)[0] - -# This function operates to pull events out from the event queue and then push -# them into the RunningBuild (which then drives the RunningBuild which then -# pushes through and updates the progress tree view.) -# -# TODO: Should be a method on the RunningBuild class -def event_handle_timeout (eventHandler, build): - # Consume as many messages as we can ... - event = eventHandler.getEvent() - while event: - build.handle_event (event) - event = eventHandler.getEvent() - return True - -class MainWindow (gtk.Window): - - # Callback that gets fired when the user hits a button in the - # BuildSetupDialog. - def build_dialog_box_response_cb (self, dialog, response_id): - conf = None - if (response_id == BuildSetupDialog.RESPONSE_BUILD): - dialog.update_configuration() - print dialog.configuration.machine, dialog.configuration.distro, \ - dialog.configuration.image - conf = dialog.configuration - - dialog.destroy() - - if conf: - self.manager.do_build (conf) - - def build_button_clicked_cb (self, button): - dialog = BuildSetupDialog () - - # For some unknown reason Dialog.run causes nice little deadlocks ... :-( - dialog.connect ("response", self.build_dialog_box_response_cb) - dialog.show() - - def __init__ (self): - gtk.Window.__init__ (self) - - # Pull in *just* the main vbox from the Glade XML data and then pack - # that inside the window - gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade", - root = "main_window_vbox") - vbox = gxml.get_widget ("main_window_vbox") - self.add (vbox) - - # Create the tree views for the build manager view and the progress view - self.build_manager_view = BuildManagerTreeView() - self.running_build_view = RunningBuildTreeView() - - # Grab the scrolled windows that we put the tree views into - self.results_scrolledwindow = gxml.get_widget ("results_scrolledwindow") - self.progress_scrolledwindow = gxml.get_widget ("progress_scrolledwindow") - - # Put the tree views inside ... - self.results_scrolledwindow.add (self.build_manager_view) - self.progress_scrolledwindow.add (self.running_build_view) - - # Hook up the build button... - self.build_button = gxml.get_widget ("main_toolbutton_build") - self.build_button.connect ("clicked", self.build_button_clicked_cb) - -# I'm not very happy about the current ownership of the RunningBuild. I have -# my suspicions that this object should be held by the BuildManager since we -# care about the signals in the manager - -def running_build_succeeded_cb (running_build, manager): - # Notify the manager that a build has succeeded. This is necessary as part - # of the 'hack' that we use for making the row in the model / view - # representing the ongoing build change into a row representing the - # completed build. Since we know only one build can be running a time then - # we can handle this. - - # FIXME: Refactor all this so that the RunningBuild is owned by the - # BuildManager. It can then hook onto the signals directly and drive - # interesting things it cares about. - manager.notify_build_succeeded () - print "build succeeded" - -def running_build_failed_cb (running_build, manager): - # As above - print "build failed" - manager.notify_build_failed () - -def init (server, eventHandler): - # Initialise threading... - gobject.threads_init() - gtk.gdk.threads_init() - - main_window = MainWindow () - main_window.show_all () - - # Set up the build manager stuff in general - builds_dir = os.path.join (os.getcwd(), "results") - manager = BuildManager (server, builds_dir) - main_window.build_manager_view.set_model (manager.model) - - # Do the running build setup - running_build = RunningBuild () - main_window.running_build_view.set_model (running_build.model) - running_build.connect ("build-succeeded", running_build_succeeded_cb, - manager) - running_build.connect ("build-failed", running_build_failed_cb, manager) - - # We need to save the manager into the MainWindow so that the toolbar - # button can use it. - # FIXME: Refactor ? - main_window.manager = manager - - # Use a timeout function for probing the event queue to find out if we - # have a message waiting for us. - gobject.timeout_add (200, - event_handle_timeout, - eventHandler, - running_build) - - gtk.main() diff --git a/bitbake-dev/lib/bb/ui/uievent.py b/bitbake-dev/lib/bb/ui/uievent.py deleted file mode 100644 index 36302f4da..000000000 --- a/bitbake-dev/lib/bb/ui/uievent.py +++ /dev/null @@ -1,125 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer -# Copyright (C) 2006 - 2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -""" -Use this class to fork off a thread to recieve event callbacks from the bitbake -server and queue them for the UI to process. This process must be used to avoid -client/server deadlocks. -""" - -import socket, threading, pickle -from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler - -class BBUIEventQueue: - def __init__(self, BBServer): - - self.eventQueue = [] - self.eventQueueLock = threading.Lock() - self.eventQueueNotify = threading.Event() - - self.BBServer = BBServer - - self.t = threading.Thread() - self.t.setDaemon(True) - self.t.run = self.startCallbackHandler - self.t.start() - - def getEvent(self): - - self.eventQueueLock.acquire() - - if len(self.eventQueue) == 0: - self.eventQueueLock.release() - return None - - item = self.eventQueue.pop(0) - - if len(self.eventQueue) == 0: - self.eventQueueNotify.clear() - - self.eventQueueLock.release() - return item - - def waitEvent(self, delay): - self.eventQueueNotify.wait(delay) - return self.getEvent() - - def queue_event(self, event): - self.eventQueueLock.acquire() - self.eventQueue.append(pickle.loads(event)) - self.eventQueueNotify.set() - self.eventQueueLock.release() - - def startCallbackHandler(self): - - server = UIXMLRPCServer() - self.host, self.port = server.socket.getsockname() - - server.register_function( self.system_quit, "event.quit" ) - server.register_function( self.queue_event, "event.send" ) - server.socket.settimeout(1) - - self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port) - - self.server = server - while not server.quit: - server.handle_request() - server.server_close() - - def system_quit( self ): - """ - Shut down the callback thread - """ - try: - self.BBServer.unregisterEventHandler(self.EventHandle) - except: - pass - self.server.quit = True - -class UIXMLRPCServer (SimpleXMLRPCServer): - - def __init__( self, interface = ("localhost", 0) ): - self.quit = False - SimpleXMLRPCServer.__init__( self, - interface, - requestHandler=SimpleXMLRPCRequestHandler, - logRequests=False, allow_none=True) - - def get_request(self): - while not self.quit: - try: - sock, addr = self.socket.accept() - sock.settimeout(1) - return (sock, addr) - except socket.timeout: - pass - return (None,None) - - def close_request(self, request): - if request is None: - return - SimpleXMLRPCServer.close_request(self, request) - - def process_request(self, request, client_address): - if request is None: - return - SimpleXMLRPCServer.process_request(self, request, client_address) - - diff --git a/bitbake-dev/lib/bb/ui/uihelper.py b/bitbake-dev/lib/bb/ui/uihelper.py deleted file mode 100644 index 151ffc585..000000000 --- a/bitbake-dev/lib/bb/ui/uihelper.py +++ /dev/null @@ -1,49 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -# -# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer -# Copyright (C) 2006 - 2007 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -class BBUIHelper: - def __init__(self): - self.needUpdate = False - self.running_tasks = {} - self.failed_tasks = {} - - def eventHandler(self, event): - if isinstance(event, bb.build.TaskStarted): - self.running_tasks["%s %s\n" % (event._package, event._task)] = "" - self.needUpdate = True - if isinstance(event, bb.build.TaskSucceeded): - del self.running_tasks["%s %s\n" % (event._package, event._task)] - self.needUpdate = True - if isinstance(event, bb.build.TaskFailed): - del self.running_tasks["%s %s\n" % (event._package, event._task)] - self.failed_tasks["%s %s\n" % (event._package, event._task)] = "" - self.needUpdate = True - - # Add runqueue event handling - #if isinstance(event, bb.runqueue.runQueueTaskCompleted): - # a = 1 - #if isinstance(event, bb.runqueue.runQueueTaskStarted): - # a = 1 - #if isinstance(event, bb.runqueue.runQueueTaskFailed): - # a = 1 - #if isinstance(event, bb.runqueue.runQueueExitWait): - # a = 1 - - def getTasks(self): - return (self.running_tasks, self.failed_tasks) diff --git a/bitbake-dev/lib/bb/utils.py b/bitbake-dev/lib/bb/utils.py deleted file mode 100644 index 5fc1463e6..000000000 --- a/bitbake-dev/lib/bb/utils.py +++ /dev/null @@ -1,431 +0,0 @@ -# ex:ts=4:sw=4:sts=4:et -# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -""" -BitBake Utility Functions -""" - -# Copyright (C) 2004 Michael Lauer -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -digits = "0123456789" -ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -separators = ".-" - -import re, fcntl, os, types - -def explode_version(s): - r = [] - alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$') - numeric_regexp = re.compile('^(\d+)(.*)$') - while (s != ''): - if s[0] in digits: - m = numeric_regexp.match(s) - r.append(int(m.group(1))) - s = m.group(2) - continue - if s[0] in ascii_letters: - m = alpha_regexp.match(s) - r.append(m.group(1)) - s = m.group(2) - continue - r.append(s[0]) - s = s[1:] - return r - -def vercmp_part(a, b): - va = explode_version(a) - vb = explode_version(b) - sa = False - sb = False - while True: - if va == []: - ca = None - else: - ca = va.pop(0) - if vb == []: - cb = None - else: - cb = vb.pop(0) - if ca == None and cb == None: - return 0 - - if type(ca) is types.StringType: - sa = ca in separators - if type(cb) is types.StringType: - sb = cb in separators - if sa and not sb: - return -1 - if not sa and sb: - return 1 - - if ca > cb: - return 1 - if ca < cb: - return -1 - -def vercmp(ta, tb): - (ea, va, ra) = ta - (eb, vb, rb) = tb - - r = int(ea)-int(eb) - if (r == 0): - r = vercmp_part(va, vb) - if (r == 0): - r = vercmp_part(ra, rb) - return r - -def explode_deps(s): - """ - Take an RDEPENDS style string of format: - "DEPEND1 (optional version) DEPEND2 (optional version) ..." - and return a list of dependencies. - Version information is ignored. - """ - r = [] - l = s.split() - flag = False - for i in l: - if i[0] == '(': - flag = True - #j = [] - if not flag: - r.append(i) - #else: - # j.append(i) - if flag and i.endswith(')'): - flag = False - # Ignore version - #r[-1] += ' ' + ' '.join(j) - return r - -def explode_dep_versions(s): - """ - Take an RDEPENDS style string of format: - "DEPEND1 (optional version) DEPEND2 (optional version) ..." - and return a dictonary of dependencies and versions. - """ - r = {} - l = s.split() - lastdep = None - lastver = "" - inversion = False - for i in l: - if i[0] == '(': - inversion = True - lastver = i[1:] or "" - #j = [] - elif inversion and i.endswith(')'): - inversion = False - lastver = lastver + " " + (i[:-1] or "") - r[lastdep] = lastver - elif not inversion: - r[i] = None - lastdep = i - lastver = "" - elif inversion: - lastver = lastver + " " + i - - return r - -def _print_trace(body, line): - """ - Print the Environment of a Text Body - """ - import bb - - # print the environment of the method - bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function") - min_line = max(1,line-4) - max_line = min(line+4,len(body)-1) - for i in range(min_line,max_line+1): - bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) ) - - -def better_compile(text, file, realfile): - """ - A better compile method. This method - will print the offending lines. - """ - try: - return compile(text, file, "exec") - except Exception, e: - import bb,sys - - # split the text into lines again - body = text.split('\n') - bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile) - bb.msg.error(bb.msg.domain.Util, "The lines resulting into this error were:") - bb.msg.error(bb.msg.domain.Util, "\t%d:%s:'%s'" % (e.lineno, e.__class__.__name__, body[e.lineno-1])) - - _print_trace(body, e.lineno) - - # exit now - sys.exit(1) - -def better_exec(code, context, text, realfile): - """ - Similiar to better_compile, better_exec will - print the lines that are responsible for the - error. - """ - import bb,sys - try: - exec code in context - except: - (t,value,tb) = sys.exc_info() - - if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: - raise - - # print the Header of the Error Message - bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile) - bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) ) - - # let us find the line number now - while tb.tb_next: - tb = tb.tb_next - - import traceback - line = traceback.tb_lineno(tb) - - _print_trace( text.split('\n'), line ) - - raise - -def Enum(*names): - """ - A simple class to give Enum support - """ - - assert names, "Empty enums are not supported" - - class EnumClass(object): - __slots__ = names - def __iter__(self): return iter(constants) - def __len__(self): return len(constants) - def __getitem__(self, i): return constants[i] - def __repr__(self): return 'Enum' + str(names) - def __str__(self): return 'enum ' + str(constants) - - class EnumValue(object): - __slots__ = ('__value') - def __init__(self, value): self.__value = value - Value = property(lambda self: self.__value) - EnumType = property(lambda self: EnumType) - def __hash__(self): return hash(self.__value) - def __cmp__(self, other): - # C fans might want to remove the following assertion - # to make all enums comparable by ordinal value {;)) - assert self.EnumType is other.EnumType, "Only values from the same enum are comparable" - return cmp(self.__value, other.__value) - def __invert__(self): return constants[maximum - self.__value] - def __nonzero__(self): return bool(self.__value) - def __repr__(self): return str(names[self.__value]) - - maximum = len(names) - 1 - constants = [None] * len(names) - for i, each in enumerate(names): - val = EnumValue(i) - setattr(EnumClass, each, val) - constants[i] = val - constants = tuple(constants) - EnumType = EnumClass() - return EnumType - -def lockfile(name): - """ - Use the file fn as a lock file, return when the lock has been acquired. - Returns a variable to pass to unlockfile(). - """ - path = os.path.dirname(name) - if not os.path.isdir(path): - import bb, sys - bb.msg.error(bb.msg.domain.Util, "Error, lockfile path does not exist!: %s" % path) - sys.exit(1) - - while True: - # If we leave the lockfiles lying around there is no problem - # but we should clean up after ourselves. This gives potential - # for races though. To work around this, when we acquire the lock - # we check the file we locked was still the lock file on disk. - # by comparing inode numbers. If they don't match or the lockfile - # no longer exists, we start again. - - # This implementation is unfair since the last person to request the - # lock is the most likely to win it. - - try: - lf = open(name, "a+") - fcntl.flock(lf.fileno(), fcntl.LOCK_EX) - statinfo = os.fstat(lf.fileno()) - if os.path.exists(lf.name): - statinfo2 = os.stat(lf.name) - if statinfo.st_ino == statinfo2.st_ino: - return lf - # File no longer exists or changed, retry - lf.close - except Exception, e: - continue - -def unlockfile(lf): - """ - Unlock a file locked using lockfile() - """ - os.unlink(lf.name) - fcntl.flock(lf.fileno(), fcntl.LOCK_UN) - lf.close - -def md5_file(filename): - """ - Return the hex string representation of the MD5 checksum of filename. - """ - try: - import hashlib - m = hashlib.md5() - except ImportError: - import md5 - m = md5.new() - - for line in open(filename): - m.update(line) - return m.hexdigest() - -def sha256_file(filename): - """ - Return the hex string representation of the 256-bit SHA checksum of - filename. On Python 2.4 this will return None, so callers will need to - handle that by either skipping SHA checks, or running a standalone sha256sum - binary. - """ - try: - import hashlib - except ImportError: - return None - - s = hashlib.sha256() - for line in open(filename): - s.update(line) - return s.hexdigest() - -def preserved_envvars_list(): - return [ - 'BBPATH', - 'BB_PRESERVE_ENV', - 'BB_ENV_WHITELIST', - 'BB_ENV_EXTRAWHITE', - 'COLORTERM', - 'DBUS_SESSION_BUS_ADDRESS', - 'DESKTOP_SESSION', - 'DESKTOP_STARTUP_ID', - 'DISPLAY', - 'GNOME_KEYRING_PID', - 'GNOME_KEYRING_SOCKET', - 'GPG_AGENT_INFO', - 'GTK_RC_FILES', - 'HOME', - 'LANG', - 'LOGNAME', - 'PATH', - 'PWD', - 'SESSION_MANAGER', - 'SHELL', - 'SSH_AUTH_SOCK', - 'TERM', - 'USER', - 'USERNAME', - '_', - 'XAUTHORITY', - 'XDG_DATA_DIRS', - 'XDG_SESSION_COOKIE', - ] - -def filter_environment(good_vars): - """ - Create a pristine environment for bitbake. This will remove variables that - are not known and may influence the build in a negative way. - """ - - import bb - - removed_vars = [] - for key in os.environ.keys(): - if key in good_vars: - continue - - removed_vars.append(key) - os.unsetenv(key) - del os.environ[key] - - if len(removed_vars): - bb.debug(1, "Removed the following variables from the environment:", ",".join(removed_vars)) - - return removed_vars - -def clean_environment(): - """ - Clean up any spurious environment variables. This will remove any - variables the user hasn't chose to preserve. - """ - if 'BB_PRESERVE_ENV' not in os.environ: - if 'BB_ENV_WHITELIST' in os.environ: - good_vars = os.environ['BB_ENV_WHITELIST'].split() - else: - good_vars = preserved_envvars_list() - if 'BB_ENV_EXTRAWHITE' in os.environ: - good_vars.extend(os.environ['BB_ENV_EXTRAWHITE'].split()) - filter_environment(good_vars) - -def empty_environment(): - """ - Remove all variables from the environment. - """ - for s in os.environ.keys(): - os.unsetenv(s) - del os.environ[s] - -def build_environment(d): - """ - Build an environment from all exported variables. - """ - import bb - for var in bb.data.keys(d): - export = bb.data.getVarFlag(var, "export", d) - if export: - os.environ[var] = bb.data.getVar(var, d, True) - -def prunedir(topdir): - # Delete everything reachable from the directory named in 'topdir'. - # CAUTION: This is dangerous! - for root, dirs, files in os.walk(topdir, topdown=False): - for name in files: - os.remove(os.path.join(root, name)) - for name in dirs: - if os.path.islink(os.path.join(root, name)): - os.remove(os.path.join(root, name)) - else: - os.rmdir(os.path.join(root, name)) - os.rmdir(topdir) - -# -# Could also use return re.compile("(%s)" % "|".join(map(re.escape, suffixes))).sub(lambda mo: "", var) -# but thats possibly insane and suffixes is probably going to be small -# -def prune_suffix(var, suffixes, d): - # See if var ends with any of the suffixes listed and - # remove it if found - for suffix in suffixes: - if var.endswith(suffix): - return var.replace(suffix, "") - return var diff --git a/bitbake/AUTHORS b/bitbake/AUTHORS index 9d592608b..a4014b1e3 100644 --- a/bitbake/AUTHORS +++ b/bitbake/AUTHORS @@ -2,7 +2,7 @@ Tim Ansell Phil Blundell Seb Frankengul Holger Freyther -Marcin Juszkiewicz +Marcin Juszkiewicz Chris Larson Ulrich Luckas Mickey Lauer diff --git a/bitbake/ChangeLog b/bitbake/ChangeLog index 9fe3bf3d8..22124cb7e 100644 --- a/bitbake/ChangeLog +++ b/bitbake/ChangeLog @@ -1,8 +1,99 @@ -Changes in BitBake 1.8.x: - - Add bb.utils.prune_suffix function - -Changes in BitBake 1.8.12: - - Fix -f (force) in conjunction with -b +Changes in Bitbake 1.9.x: + - Add PE (Package Epoch) support from Philipp Zabel (pH5) + - Treat python functions the same as shell functions for logging + - Use TMPDIR/anonfunc as a __anonfunc temp directory (T) + - Catch truncated cache file errors + - Allow operations other than assignment on flag variables + - Add code to handle inter-task dependencies + - Fix cache errors when generation dotGraphs + - Make sure __inherit_cache is updated before calling include() (from Michael Krelin) + - Fix bug when target was in ASSUME_PROVIDED (#2236) + - Raise ParseError for filenames with multiple underscores instead of infinitely looping (#2062) + - Fix invalid regexp in BBMASK error handling (missing import) (#1124) + - Promote certain warnings from debug to note 2 level + - Update manual + - Correctly redirect stdin when forking + - If parsing errors are found, exit, too many users miss the errors + - Remove supriours PREFERRED_PROVIDER warnings + - svn fetcher: Add _buildsvncommand function + - Improve certain error messages + - Rewrite svn fetcher to make adding extra operations easier + as part of future SRCDATE="now" fixes + (requires new FETCHCMD_svn definition in bitbake.conf) + - Change SVNDIR layout to be more unique (fixes #2644 and #2624) + - Add ConfigParsed Event after configuration parsing is complete + - Add SRCREV support for svn fetcher + - data.emit_var() - only call getVar if we need the variable + - Stop generating the A variable (seems to be legacy code) + - Make sure intertask depends get processed correcting in recursive depends + - Add pn-PN to overrides when evaluating PREFERRED_VERSION + - Improve the progress indicator by skipping tasks that have + already run before starting the build rather than during it + - Add profiling option (-P) + - Add BB_SRCREV_POLICY variable (clear or cache) to control SRCREV cache + - Add SRCREV_FORMAT support + - Fix local fetcher's localpath return values + - Apply OVERRIDES before performing immediate expansions + - Allow the -b -e option combination to take regular expressions + - Fix handling of variables with expansion in the name using _append/_prepend + e.g. RRECOMMENDS_${PN}_append_xyz = "abc" + - Add plain message function to bb.msg + - Sort the list of providers before processing so dependency problems are + reproducible rather than effectively random + - Fix/improve bitbake -s output + - Add locking for fetchers so only one tries to fetch a given file at a given time + - Fix int(0)/None confusion in runqueue.py which causes random gaps in dependency chains + - Expand data in addtasks + - Print the list of missing DEPENDS,RDEPENDS for the "No buildable providers available for required...." + error message. + - Rework add_task to be more efficient (6% speedup, 7% number of function calls reduction) + - Sort digraph output to make builds more reproducible + - Split expandKeys into two for loops to benefit from the expand_cache (12% speedup) + - runqueue.py: Fix idepends handling to avoid dependency errors + - Clear the terminal TOSTOP flag if set (and warn the user) + - Fix regression from r653 and make SRCDATE/CVSDATE work for packages again + - Fix a bug in bb.decodeurl where http://some.where.com/somefile.tgz decoded to host="" (#1530) + - Warn about malformed PREFERRED_PROVIDERS (#1072) + - Add support for BB_NICE_LEVEL option (#1627) + - Psyco is used only on x86 as there is no support for other architectures. + - Sort initial providers list by default preference (#1145, #2024) + - Improve provider sorting so prefered versions have preference over latest versions (#768) + - Detect builds of tasks with overlapping providers and warn (will become a fatal error) (#1359) + - Add MULTI_PROVIDER_WHITELIST variable to allow known safe multiple providers to be listed + - Handle paths in svn fetcher module parameter + - Support the syntax "export VARIABLE" + - Add bzr fetcher + - Add support for cleaning directories before a task in the form: + do_taskname[cleandirs] = "dir" + - bzr fetcher tweaks from Robert Schuster (#2913) + - Add mercurial (hg) fetcher from Robert Schuster (#2913) + - Don't add duplicates to BBPATH + - Fix preferred_version return values (providers.py) + - Fix 'depends' flag splitting + - Fix unexport handling (#3135) + - Add bb.copyfile function similar to bb.movefile (and improve movefile error reporting) + - Allow multiple options for deptask flag + - Use git-fetch instead of git-pull removing any need for merges when + fetching (we don't care about the index). Fixes fetch errors. + - Add BB_GENERATE_MIRROR_TARBALLS option, set to 0 to make git fetches + faster at the expense of not creating mirror tarballs. + - SRCREV handling updates, improvements and fixes from Poky + - Add bb.utils.lockfile() and bb.utils.unlockfile() from Poky + - Add support for task selfstamp and lockfiles flags + - Disable task number acceleration since it can allow the tasks to run + out of sequence + - Improve runqueue code comments + - Add task scheduler abstraction and some example schedulers + - Improve circular dependency chain debugging code and user feedback + - Don't give a stacktrace for invalid tasks, have a user friendly message (#3431) + - Add support for "-e target" (#3432) + - Fix shell showdata command (#3259) + - Fix shell data updating problems (#1880) + - Properly raise errors for invalid source URI protocols + - Change the wget fetcher failure handling to avoid lockfile problems + - Add support for branches in git fetcher (Otavio Salvador, Michael Lauer) + - Make taskdata and runqueue errors more user friendly + - Add norecurse and fullpath options to cvs fetcher - Fix exit code for build failures in --continue mode - Fix git branch tags fetching - Change parseConfigurationFile so it works on real data, not a copy @@ -27,8 +118,10 @@ Changes in BitBake 1.8.12: how extensively stamps are looked at for validity - When handling build target failures make sure idepends are checked and failed where needed. Fixes --continue mode crashes. + - Fix -f (force) in conjunction with -b - Fix problems with recrdeptask handling where some idepends weren't handled correctly. + - Handle exit codes correctly (from pH5) - Work around refs/HEAD issues with git over http (#3410) - Add proxy support to the CVS fetcher (from Cyril Chemparathy) - Improve runfetchcmd so errors are seen and various GIT variables are exported @@ -44,7 +137,6 @@ Changes in BitBake 1.8.12: - Add PERSISTENT_DIR to store the PersistData in a persistent directory != the cache dir. - Add md5 and sha256 checksum generation functions to utils.py - - Make sure Build Completed events are generated even when tasks fail - Correctly handle '-' characters in class names (#2958) - Make sure expandKeys has been called on the data dictonary before running tasks - Correctly add a task override in the form task-TASKNAME. @@ -63,6 +155,7 @@ Changes in BitBake 1.8.12: used instead of the internal bitbake one. Alternatively, BB_ENV_EXTRAWHITE can be used to extend the internal whitelist. - Perforce fetcher fix to use commandline options instead of being overriden by the environment + - bb.utils.prunedir can cope with symlinks to directoriees without exceptions - use @rev when doing a svn checkout - Add osc fetcher (from Joshua Lock in Poky) - When SRCREV autorevisioning for a recipe is in use, don't cache the recipe @@ -76,109 +169,15 @@ Changes in BitBake 1.8.12: proxies to work better. (from Poky) - Also allow user and pswd options in SRC_URIs globally (from Poky) - Improve proxy handling when using mirrors (from Poky) - -Changes in BitBake 1.8.10: - - Psyco is available only for x86 - do not use it on other architectures. - - Fix a bug in bb.decodeurl where http://some.where.com/somefile.tgz decoded to host="" (#1530) - - Warn about malformed PREFERRED_PROVIDERS (#1072) - - Add support for BB_NICE_LEVEL option (#1627) - - Sort initial providers list by default preference (#1145, #2024) - - Improve provider sorting so prefered versions have preference over latest versions (#768) - - Detect builds of tasks with overlapping providers and warn (will become a fatal error) (#1359) - - Add MULTI_PROVIDER_WHITELIST variable to allow known safe multiple providers to be listed - - Handle paths in svn fetcher module parameter - - Support the syntax "export VARIABLE" - - Add bzr fetcher - - Add support for cleaning directories before a task in the form: - do_taskname[cleandirs] = "dir" - - bzr fetcher tweaks from Robert Schuster (#2913) - - Add mercurial (hg) fetcher from Robert Schuster (#2913) - - Fix bogus preferred_version return values - - Fix 'depends' flag splitting - - Fix unexport handling (#3135) - - Add bb.copyfile function similar to bb.movefile (and improve movefile error reporting) - - Allow multiple options for deptask flag - - Use git-fetch instead of git-pull removing any need for merges when - fetching (we don't care about the index). Fixes fetch errors. - - Add BB_GENERATE_MIRROR_TARBALLS option, set to 0 to make git fetches - faster at the expense of not creating mirror tarballs. - - SRCREV handling updates, improvements and fixes from Poky - - Add bb.utils.lockfile() and bb.utils.unlockfile() from Poky - - Add support for task selfstamp and lockfiles flags - - Disable task number acceleration since it can allow the tasks to run - out of sequence - - Improve runqueue code comments - - Add task scheduler abstraction and some example schedulers - - Improve circular dependency chain debugging code and user feedback - - Don't give a stacktrace for invalid tasks, have a user friendly message (#3431) - - Add support for "-e target" (#3432) - - Fix shell showdata command (#3259) - - Fix shell data updating problems (#1880) - - Properly raise errors for invalid source URI protocols - - Change the wget fetcher failure handling to avoid lockfile problems - - Add git branch support - - Add support for branches in git fetcher (Otavio Salvador, Michael Lauer) - - Make taskdata and runqueue errors more user friendly - - Add norecurse and fullpath options to cvs fetcher - - bb.utils.prunedir can cope with symlinks to directories without exceptions - -Changes in Bitbake 1.8.8: - - Rewrite svn fetcher to make adding extra operations easier - as part of future SRCDATE="now" fixes - (requires new FETCHCMD_svn definition in bitbake.conf) - - Change SVNDIR layout to be more unique (fixes #2644 and #2624) - - Import persistent data store from trunk - - Sync fetcher code with that in trunk, adding SRCREV support for svn - - Add ConfigParsed Event after configuration parsing is complete - - data.emit_var() - only call getVar if we need the variable - - Stop generating the A variable (seems to be legacy code) - - Make sure intertask depends get processed correcting in recursive depends - - Add pn-PN to overrides when evaluating PREFERRED_VERSION - - Improve the progress indicator by skipping tasks that have - already run before starting the build rather than during it - - Add profiling option (-P) - - Add BB_SRCREV_POLICY variable (clear or cache) to control SRCREV cache - - Add SRCREV_FORMAT support - - Fix local fetcher's localpath return values - - Apply OVERRIDES before performing immediate expansions - - Allow the -b -e option combination to take regular expressions - - Add plain message function to bb.msg - - Sort the list of providers before processing so dependency problems are - reproducible rather than effectively random - - Add locking for fetchers so only one tries to fetch a given file at a given time - - Fix int(0)/None confusion in runqueue.py which causes random gaps in dependency chains - - Fix handling of variables with expansion in the name using _append/_prepend - e.g. RRECOMMENDS_${PN}_append_xyz = "abc" - - Expand data in addtasks - - Print the list of missing DEPENDS,RDEPENDS for the "No buildable providers available for required...." - error message. - - Rework add_task to be more efficient (6% speedup, 7% number of function calls reduction) - - Sort digraph output to make builds more reproducible - - Split expandKeys into two for loops to benefit from the expand_cache (12% speedup) - - runqueue.py: Fix idepends handling to avoid dependency errors - - Clear the terminal TOSTOP flag if set (and warn the user) - - Fix regression from r653 and make SRCDATE/CVSDATE work for packages again - -Changes in Bitbake 1.8.6: - - Correctly redirect stdin when forking - - If parsing errors are found, exit, too many users miss the errors - - Remove supriours PREFERRED_PROVIDER warnings - -Changes in Bitbake 1.8.4: - - Make sure __inherit_cache is updated before calling include() (from Michael Krelin) - - Fix bug when target was in ASSUME_PROVIDED (#2236) - - Raise ParseError for filenames with multiple underscores instead of infinitely looping (#2062) - - Fix invalid regexp in BBMASK error handling (missing import) (#1124) - - Don't run build sanity checks on incomplete builds - - Promote certain warnings from debug to note 2 level - - Update manual - -Changes in Bitbake 1.8.2: - - Catch truncated cache file errors - - Add PE (Package Epoch) support from Philipp Zabel (pH5) - - Add code to handle inter-task dependencies - - Allow operations other than assignment on flag variables - - Fix cache errors when generation dotGraphs + - Add bb.utils.prune_suffix function + - Fix hg checkouts of specific revisions (from Poky) + - Fix wget fetching of urls with parameters specified (from Poky) + - Add username handling to git fetcher (from Poky) + - Set HOME environmental variable when running fetcher commands (from Poky) + - Make sure allowed variables inherited from the environment are exported again (from Poky) + - When running a stage task in bbshell, run populate_staging, not the stage task (from Poky) + - Fix + character escaping from PACKAGES_DYNAMIC (thanks Otavio Salvador) + - Addition of BBCLASSEXTEND support for allowing one recipe to provide multiple targets (from Poky) Changes in Bitbake 1.8.0: - Release 1.7.x as a stable series diff --git a/bitbake/MANIFEST b/bitbake/MANIFEST deleted file mode 100644 index 39e801775..000000000 --- a/bitbake/MANIFEST +++ /dev/null @@ -1,53 +0,0 @@ -AUTHORS -COPYING -ChangeLog -MANIFEST -setup.py -bin/bitdoc -bin/bbimage -bin/bitbake -lib/bb/__init__.py -lib/bb/build.py -lib/bb/cache.py -lib/bb/cooker.py -lib/bb/COW.py -lib/bb/data.py -lib/bb/data_smart.py -lib/bb/event.py -lib/bb/fetch/__init__.py -lib/bb/fetch/bzr.py -lib/bb/fetch/cvs.py -lib/bb/fetch/git.py -lib/bb/fetch/hg.py -lib/bb/fetch/local.py -lib/bb/fetch/osc.py -lib/bb/fetch/perforce.py -lib/bb/fetch/ssh.py -lib/bb/fetch/svk.py -lib/bb/fetch/svn.py -lib/bb/fetch/wget.py -lib/bb/manifest.py -lib/bb/methodpool.py -lib/bb/msg.py -lib/bb/parse/__init__.py -lib/bb/parse/parse_py/__init__.py -lib/bb/parse/parse_py/BBHandler.py -lib/bb/parse/parse_py/ConfHandler.py -lib/bb/persist_data.py -lib/bb/providers.py -lib/bb/runqueue.py -lib/bb/shell.py -lib/bb/taskdata.py -lib/bb/utils.py -setup.py -doc/COPYING.GPL -doc/COPYING.MIT -doc/bitbake.1 -doc/manual/html.css -doc/manual/Makefile -doc/manual/usermanual.xml -contrib/bbdev.sh -contrib/vim/syntax/bitbake.vim -contrib/vim/ftdetect/bitbake.vim -conf/bitbake.conf -classes/base.bbclass diff --git a/bitbake/bin/bitbake b/bitbake/bin/bitbake index 842ba0441..23c9d73ee 100755 --- a/bitbake/bin/bitbake +++ b/bitbake/bin/bitbake @@ -22,12 +22,18 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import sys, os, getopt, re, time, optparse +import sys, os, getopt, re, time, optparse, xmlrpclib sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib')) import bb from bb import cooker +from bb import ui -__version__ = "1.8.13" + +__version__ = "1.9.0" + +if sys.hexversion < 0x020500F0: + print "Sorry, python 2.5 or later is required for this version of bitbake" + sys.exit(1) #============================================================================# # BBOptions @@ -41,11 +47,28 @@ class BBConfiguration( object ): setattr( self, key, val ) +def print_exception(exc, value, tb): + """ + Print the exception to stderr, only showing the traceback if bitbake + debugging is enabled. + """ + if not bb.msg.debug_level['default']: + tb = None + + sys.__excepthook__(exc, value, tb) + + #============================================================================# # main #============================================================================# def main(): + return_value = 0 + pythonver = sys.version_info + if pythonver[0] < 2 or (pythonver[0] == 2 and pythonver[1] < 5): + print "Sorry, bitbake needs python 2.5 or later." + sys.exit(1) + parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ), usage = """%prog [options] [package ...] @@ -99,8 +122,8 @@ Default BBFILES are the .bb files in the current directory.""" ) parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax", action = "store_true", dest = "dot_graph", default = False ) - parser.add_option( "-I", "--ignore-deps", help = """Stop processing at the given list of dependencies when generating dependency graphs. This can help to make the graph more appealing""", - action = "append", dest = "ignored_dot_deps", default = [] ) + parser.add_option( "-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""", + action = "append", dest = "extra_assume_provided", default = [] ) parser.add_option( "-l", "--log-domains", help = """Show debug logging for the specified logging domains""", action = "append", dest = "debug_domains", default = [] ) @@ -108,6 +131,9 @@ Default BBFILES are the .bb files in the current directory.""" ) parser.add_option( "-P", "--profile", help = "profile the command and print a report", action = "store_true", dest = "profile", default = False ) + parser.add_option( "-u", "--ui", help = "userinterface to use", + action = "store", dest = "ui") + parser.add_option( "", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not", action = "store_true", dest = "revisions_changed", default = False ) @@ -117,30 +143,53 @@ Default BBFILES are the .bb files in the current directory.""" ) configuration.pkgs_to_build = [] configuration.pkgs_to_build.extend(args[1:]) - cooker = bb.cooker.BBCooker(configuration) + #server = bb.server.xmlrpc + server = bb.server.none + + # Save a logfile for cooker into the current working directory. When the + # server is daemonized this logfile will be truncated. + cooker_logfile = os.path.join (os.getcwd(), "cooker.log") + + cooker = bb.cooker.BBCooker(configuration, server) # Clear away any spurious environment variables. But don't wipe the - # environment totally. + # environment totally. This is necessary to ensure the correct operation + # of the UIs (e.g. for DISPLAY, etc.) bb.utils.clean_environment() - cooker.parseConfiguration() - - if configuration.profile: - try: - import cProfile as profile - except: - import profile - - profile.runctx("cooker.cook()", globals(), locals(), "profile.log") - import pstats - p = pstats.Stats('profile.log') - p.sort_stats('time') - p.print_stats() - p.print_callers() - p.sort_stats('cumulative') - p.print_stats() + cooker.parseCommandLine() + + serverinfo = server.BitbakeServerInfo(cooker.server) + + server.BitBakeServerFork(serverinfo, cooker.serve, cooker_logfile) + del cooker + + sys.excepthook = print_exception + + # Setup a connection to the server (cooker) + serverConnection = server.BitBakeServerConnection(serverinfo) + + # Launch the UI + if configuration.ui: + ui = configuration.ui else: - cooker.cook() + ui = "knotty" + + try: + # Dynamically load the UI based on the ui name. Although we + # suggest a fixed set this allows you to have flexibility in which + # ones are available. + exec "from bb.ui import " + ui + exec "return_value = " + ui + ".init(serverConnection.connection, serverConnection.events)" + except ImportError: + print "FATAL: Invalid user interface '%s' specified. " % ui + print "Valid interfaces are 'ncurses', 'depexp' or the default, 'knotty'." + except Exception, e: + print "FATAL: Unable to start to '%s' UI: %s." % (configuration.ui, e.message) + finally: + serverConnection.terminate() + return return_value if __name__ == "__main__": - main() + ret = main() + sys.exit(ret) diff --git a/bitbake/bin/bitdoc b/bitbake/bin/bitdoc index 3bcc9b344..4940f660a 100755 --- a/bitbake/bin/bitdoc +++ b/bitbake/bin/bitdoc @@ -453,6 +453,8 @@ def main(): except bb.parse.ParseError: bb.fatal( "Unable to parse %s" % config_file ) + if isinstance(documentation, dict): + documentation = documentation[""] # Assuming we've the file loaded now, we will initialize the 'tree' doc = Documentation() diff --git a/bitbake/contrib/vim/syntax/bitbake.vim b/bitbake/contrib/vim/syntax/bitbake.vim index 43a1990b0..be55980b3 100644 --- a/bitbake/contrib/vim/syntax/bitbake.vim +++ b/bitbake/contrib/vim/syntax/bitbake.vim @@ -16,12 +16,17 @@ endif syn case match - " Catch incorrect syntax (only matches if nothing else does) " syn match bbUnmatched "." +syn include @python syntax/python.vim +if exists("b:current_syntax") + unlet b:current_syntax +endif + + " Other syn match bbComment "^#.*$" display contains=bbTodo @@ -34,21 +39,25 @@ syn match bbArrayBrackets "[\[\]]" contained " BitBake strings syn match bbContinue "\\$" -syn region bbString matchgroup=bbQuote start=/"/ skip=/\\$/ excludenl end=/"/ contained keepend contains=bbTodo,bbContinue,bbVarDeref -syn region bbString matchgroup=bbQuote start=/'/ skip=/\\$/ excludenl end=/'/ contained keepend contains=bbTodo,bbContinue,bbVarDeref - +syn region bbString matchgroup=bbQuote start=/"/ skip=/\\$/ excludenl end=/"/ contained keepend contains=bbTodo,bbContinue,bbVarInlinePy,bbVarDeref +syn region bbString matchgroup=bbQuote start=/'/ skip=/\\$/ excludenl end=/'/ contained keepend contains=bbTodo,bbContinue,bbVarInlinePy,bbVarDeref " BitBake variable metadata +syn match bbVarBraces "[\${}]" +syn region bbVarDeref matchgroup=bbVarBraces start="${" end="}" contained +" syn region bbVarDeref start="${" end="}" contained +" syn region bbVarInlinePy start="${@" end="}" contained contains=@python +syn region bbVarInlinePy matchgroup=bbVarBraces start="${@" end="}" contained contains=@python + syn keyword bbExportFlag export contained nextgroup=bbIdentifier skipwhite -syn match bbVarDeref "${[a-zA-Z0-9\-_\.]\+}" contained -syn match bbVarDef "^\(export\s*\)\?\([a-zA-Z0-9\-_\.]\+\(_[${}a-zA-Z0-9\-_\.]\+\)\?\)\s*\(:=\|+=\|=+\|\.=\|=\.\|?=\|=\)\@=" contains=bbExportFlag,bbIdentifier,bbVarDeref nextgroup=bbVarEq +" syn match bbVarDeref "${[a-zA-Z0-9\-_\.]\+}" contained +syn match bbVarDef "^\(export\s*\)\?\([a-zA-Z0-9\-_\.]\+\(_[${}a-zA/-Z0-9\-_\.]\+\)\?\)\s*\(:=\|+=\|=+\|\.=\|=\.\|?=\|=\)\@=" contains=bbExportFlag,bbIdentifier,bbVarDeref nextgroup=bbVarEq -syn match bbIdentifier "[a-zA-Z0-9\-_\.]\+" display contained +syn match bbIdentifier "[a-zA-Z0-9\-_\./]\+" display contained "syn keyword bbVarEq = display contained nextgroup=bbVarValue syn match bbVarEq "\(:=\|+=\|=+\|\.=\|=\.\|?=\|=\)" contained nextgroup=bbVarValue -syn match bbVarValue ".*$" contained contains=bbString,bbVarDeref - +syn match bbVarValue ".*$" contained contains=bbString " BitBake variable metadata flags syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag @@ -61,10 +70,6 @@ syn match bbFunction "\h\w*" display contained " BitBake python metadata -syn include @python syntax/python.vim -if exists("b:current_syntax") - unlet b:current_syntax -endif syn keyword bbPythonFlag python contained nextgroup=bbFunction syn match bbPythonFuncDef "^\(python\s\+\)\(\w\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPythonFlag,bbFunction,bbDelimiter nextgroup=bbPythonFuncRegion skipwhite @@ -98,7 +103,6 @@ syn match bbStatementRest ".*$" contained contains=bbString,bbVarDeref " hi def link bbArrayBrackets Statement hi def link bbUnmatched Error -hi def link bbVarDeref String hi def link bbContinue Special hi def link bbDef Statement hi def link bbPythonFlag Type @@ -116,5 +120,8 @@ hi def link bbIdentifier Identifier hi def link bbVarEq Operator hi def link bbQuote String hi def link bbVarValue String +" hi def link bbVarInlinePy PreProc +hi def link bbVarDeref PreProc +hi def link bbVarBraces PreProc let b:current_syntax = "bb" diff --git a/bitbake/doc/bitbake.1 b/bitbake/doc/bitbake.1 index e687f0a42..036402e8a 100644 --- a/bitbake/doc/bitbake.1 +++ b/bitbake/doc/bitbake.1 @@ -32,7 +32,7 @@ command. \fBbitbake\fP is a program that executes the specified task (default is 'build') for a given set of BitBake files. .br -It expects that BBFILES is defined, which is a space seperated list of files to +It expects that BBFILES is defined, which is a space separated list of files to be executed. BBFILES does support wildcards. .br Default BBFILES are the .bb files in the current directory. @@ -67,7 +67,7 @@ drop into the interactive mode also called the BitBake shell. Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). -Depending on the base.bbclass a listtaks tasks is defined and will show +Depending on the base.bbclass a listtasks task is defined and will show available tasks. .TP .B \-rFILE, \-\-read=FILE diff --git a/bitbake/doc/manual/usermanual.xml b/bitbake/doc/manual/usermanual.xml index a01801e03..cdd05998a 100644 --- a/bitbake/doc/manual/usermanual.xml +++ b/bitbake/doc/manual/usermanual.xml @@ -119,7 +119,7 @@ will be introduced.
Conditional metadata set - OVERRIDES is a : seperated variable containing each item you want to satisfy conditions. So, if you have a variable which is conditional on arm, and arm is in OVERRIDES, then the arm specific version of the variable is used rather than the non-conditional version. Example: + OVERRIDES is a : separated variable containing each item you want to satisfy conditions. So, if you have a variable which is conditional on arm, and arm is in OVERRIDES, then the arm specific version of the variable is used rather than the non-conditional version. Example: OVERRIDES = "architecture:os:machine" TEST = "defaultvalue" TEST_os = "osspecificvalue" @@ -184,7 +184,7 @@ include directive.
Inheritance NOTE: This is only supported in .bb and .bbclass files. - The inherit directive is a means of specifying what classes of functionality your .bb requires. It is a rudamentary form of inheritence. For example, you can easily abstract out the tasks involved in building a package that uses autoconf and automake, and put that into a bbclass for your packages to make use of. A given bbclass is located by searching for classes/filename.oeclass in BBPATH, where filename is what you inherited. + The inherit directive is a means of specifying what classes of functionality your .bb requires. It is a rudimentary form of inheritance. For example, you can easily abstract out the tasks involved in building a package that uses autoconf and automake, and put that into a bbclass for your packages to make use of. A given bbclass is located by searching for classes/filename.oeclass in BBPATH, where filename is what you inherited.
Tasks @@ -263,11 +263,11 @@ of the event and the content of the FILE variable.
Classes - BitBake classes are our rudamentary inheritence mechanism. As briefly mentioned in the metadata introduction, they're parsed when an inherit directive is encountered, and they are located in classes/ relative to the dirs in BBPATH. + BitBake classes are our rudimentary inheritance mechanism. As briefly mentioned in the metadata introduction, they're parsed when an inherit directive is encountered, and they are located in classes/ relative to the dirs in BBPATH.
.bb Files - A BitBake (.bb) file is a logical unit of tasks to be executed. Normally this is a package to be built. Inter-.bb dependencies are obeyed. The files themselves are located via the BBFILES variable, which is set to a space seperated list of .bb files, and does handle wildcards. + A BitBake (.bb) file is a logical unit of tasks to be executed. Normally this is a package to be built. Inter-.bb dependencies are obeyed. The files themselves are located via the BBFILES variable, which is set to a space separated list of .bb files, and does handle wildcards.
@@ -352,15 +352,7 @@ will be tried first when fetching a file if that fails the actual file will be t - Commands -
- bbread - bbread is a command for displaying BitBake metadata. When run with no arguments, it has the core parse 'conf/bitbake.conf', as located in BBPATH, and displays that. If you supply a file on the commandline, such as a .bb, then it parses that afterwards, using the aforementioned configuration metadata. - NOTE: the stand a lone bbread command was removed. Instead of bbread use bitbake -e. - -
-
- bitbake + The bitbake command
Introduction bitbake is the primary command in the system. It facilitates executing tasks in a single .bb file, or executing a given task on a set of multiple .bb files, accounting for interdependencies amongst them. @@ -372,7 +364,7 @@ will be tried first when fetching a file if that fails the actual file will be t usage: bitbake [options] [package ...] Executes the specified task (default is 'build') for a given set of BitBake files. -It expects that BBFILES is defined, which is a space seperated list of files to +It expects that BBFILES is defined, which is a space separated list of files to be executed. BBFILES does support wildcards. Default BBFILES are the .bb files in the current directory. @@ -394,7 +386,7 @@ options: it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a - listtasks tasks is defined and will show available + listtasks task is defined and will show available tasks -r FILE, --read=FILE read the specified file before bitbake.conf -v, --verbose output more chit-chat to the terminal @@ -417,6 +409,7 @@ options: Show debug logging for the specified logging domains -P, --profile profile the command and print a report + @@ -462,12 +455,12 @@ Two files will be written into the current working directory, depends.
Metadata - As you may have seen in the usage information, or in the information about .bb files, the BBFILES variable is how the bitbake tool locates its files. This variable is a space seperated list of files that are available, and supports wildcards. + As you may have seen in the usage information, or in the information about .bb files, the BBFILES variable is how the bitbake tool locates its files. This variable is a space separated list of files that are available, and supports wildcards. Setting BBFILES BBFILES = "/path/to/bbfiles/*.bb" - With regard to dependencies, it expects the .bb to define a DEPENDS variable, which contains a space seperated list of package names, which themselves are the PN variable. The PN variable is, in general, by default, set to a component of the .bb filename. + With regard to dependencies, it expects the .bb to define a DEPENDS variable, which contains a space separated list of package names, which themselves are the PN variable. The PN variable is, in general, by default, set to a component of the .bb filename. Depending on another .bb a.bb: @@ -514,6 +507,5 @@ BBFILE_PRIORITY_upstream = "5" BBFILE_PRIORITY_local = "10"
-
diff --git a/bitbake/lib/bb/__init__.py b/bitbake/lib/bb/__init__.py index b8f7c7f59..f2f8f656d 100644 --- a/bitbake/lib/bb/__init__.py +++ b/bitbake/lib/bb/__init__.py @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -__version__ = "1.8.13" +__version__ = "1.9.0" __all__ = [ @@ -54,6 +54,7 @@ __all__ = [ # modules "parse", "data", + "command", "event", "build", "fetch", diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py index 1d6742b6e..6d80b4b54 100644 --- a/bitbake/lib/bb/build.py +++ b/bitbake/lib/bb/build.py @@ -25,8 +25,8 @@ # #Based on functions from the base bb module, Copyright 2003 Holger Schurig -from bb import data, fetch, event, mkdirhier, utils -import bb, os +from bb import data, event, mkdirhier, utils +import bb, os, sys # When we execute a python function we'd like certain things # in all namespaces, hence we add them to __builtins__ @@ -37,7 +37,11 @@ __builtins__['os'] = os # events class FuncFailed(Exception): - """Executed function failed""" + """ + Executed function failed + First parameter a message + Second paramter is a logfile (optional) + """ class EventException(Exception): """Exception which is associated with an Event.""" @@ -50,7 +54,9 @@ class TaskBase(event.Event): def __init__(self, t, d ): self._task = t - event.Event.__init__(self, d) + self._package = bb.data.getVar("PF", d, 1) + event.Event.__init__(self) + self._message = "package %s: task %s: %s" % (bb.data.getVar("PF", d, 1), t, bb.event.getName(self)[4:]) def getTask(self): return self._task @@ -68,6 +74,10 @@ class TaskSucceeded(TaskBase): class TaskFailed(TaskBase): """Task execution failed""" + def __init__(self, msg, logfile, t, d ): + self.logfile = logfile + self.msg = msg + TaskBase.__init__(self, t, d) class InvalidTask(TaskBase): """Invalid Task""" @@ -104,42 +114,116 @@ def exec_func(func, d, dirs = None): else: adir = data.getVar('B', d, 1) + # Save current directory try: prevdir = os.getcwd() except OSError: prevdir = data.getVar('TOPDIR', d, True) + + # Setup logfiles + t = data.getVar('T', d, 1) + if not t: + bb.msg.fatal(bb.msg.domain.Build, "T not set") + mkdirhier(t) + # Gross hack, FIXME + import random + logfile = "%s/log.%s.%s.%s" % (t, func, str(os.getpid()),random.random()) + runfile = "%s/run.%s.%s" % (t, func, str(os.getpid())) + + # Change to correct directory (if specified) if adir and os.access(adir, os.F_OK): os.chdir(adir) + # Handle logfiles + si = file('/dev/null', 'r') + try: + if bb.msg.debug_level['default'] > 0 or ispython: + so = os.popen("tee \"%s\"" % logfile, "w") + else: + so = file(logfile, 'w') + except OSError, e: + bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e) + pass + + se = so + + # Dup the existing fds so we dont lose them + osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()] + oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()] + ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()] + + # Replace those fds with our own + os.dup2(si.fileno(), osi[1]) + os.dup2(so.fileno(), oso[1]) + os.dup2(se.fileno(), ose[1]) + locks = [] lockfiles = (data.expand(flags['lockfiles'], d) or "").split() for lock in lockfiles: locks.append(bb.utils.lockfile(lock)) - if flags['python']: - exec_func_python(func, d) - else: - exec_func_shell(func, d, flags) + try: + # Run the function + if ispython: + exec_func_python(func, d, runfile, logfile) + else: + exec_func_shell(func, d, runfile, logfile, flags) + + # Restore original directory + try: + os.chdir(prevdir) + except: + pass - for lock in locks: - bb.utils.unlockfile(lock) + finally: - if os.path.exists(prevdir): - os.chdir(prevdir) + # Unlock any lockfiles + for lock in locks: + bb.utils.unlockfile(lock) + + # Restore the backup fds + os.dup2(osi[0], osi[1]) + os.dup2(oso[0], oso[1]) + os.dup2(ose[0], ose[1]) + + # Close our logs + si.close() + so.close() + se.close() -def exec_func_python(func, d): + if os.path.exists(logfile) and os.path.getsize(logfile) == 0: + bb.msg.debug(2, bb.msg.domain.Build, "Zero size logfile %s, removing" % logfile) + os.remove(logfile) + + # Close the backup fds + os.close(osi[0]) + os.close(oso[0]) + os.close(ose[0]) + +def exec_func_python(func, d, runfile, logfile): """Execute a python BB 'function'""" - import re + import re, os bbfile = bb.data.getVar('FILE', d, 1) tmp = "def " + func + "():\n%s" % data.getVar(func, d) tmp += '\n' + func + '()' + + f = open(runfile, "w") + f.write(tmp) comp = utils.better_compile(tmp, func, bbfile) g = {} # globals g['d'] = d - utils.better_exec(comp, g, tmp, bbfile) + try: + utils.better_exec(comp, g, tmp, bbfile) + except: + (t,value,tb) = sys.exc_info() + + if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: + raise + bb.msg.error(bb.msg.domain.Build, "Function %s failed" % func) + raise FuncFailed("function %s failed" % func, logfile) -def exec_func_shell(func, d, flags): +def exec_func_shell(func, d, runfile, logfile, flags): """Execute a shell BB 'function' Returns true if execution was successful. For this, it creates a bash shell script in the tmp dectory, writes the local @@ -149,23 +233,13 @@ def exec_func_shell(func, d, flags): of the directories you need created prior to execution. The last item in the list is where we will chdir/cd to. """ - import sys deps = flags['deps'] check = flags['check'] - interact = flags['interactive'] if check in globals(): if globals()[check](func, deps): return - global logfile - t = data.getVar('T', d, 1) - if not t: - return 0 - mkdirhier(t) - logfile = "%s/log.%s.%s" % (t, func, str(os.getpid())) - runfile = "%s/run.%s.%s" % (t, func, str(os.getpid())) - f = open(runfile, "w") f.write("#!/bin/sh -e\n") if bb.msg.debug_level['default'] > 0: f.write("set -x\n") @@ -177,91 +251,21 @@ def exec_func_shell(func, d, flags): os.chmod(runfile, 0775) if not func: bb.msg.error(bb.msg.domain.Build, "Function not specified") - raise FuncFailed() - - # open logs - si = file('/dev/null', 'r') - try: - if bb.msg.debug_level['default'] > 0: - so = os.popen("tee \"%s\"" % logfile, "w") - else: - so = file(logfile, 'w') - except OSError, e: - bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e) - pass - - se = so - - if not interact: - # dup the existing fds so we dont lose them - osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()] - oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()] - ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()] - - # replace those fds with our own - os.dup2(si.fileno(), osi[1]) - os.dup2(so.fileno(), oso[1]) - os.dup2(se.fileno(), ose[1]) + raise FuncFailed("Function not specified for exec_func_shell") # execute function - prevdir = os.getcwd() if flags['fakeroot']: maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1) else: maybe_fakeroot = '' lang_environment = "LC_ALL=C " ret = os.system('%s%ssh -e %s' % (lang_environment, maybe_fakeroot, runfile)) - try: - os.chdir(prevdir) - except: - pass - - if not interact: - # restore the backups - os.dup2(osi[0], osi[1]) - os.dup2(oso[0], oso[1]) - os.dup2(ose[0], ose[1]) - # close our logs - si.close() - so.close() - se.close() - - if os.path.exists(logfile) and os.path.getsize(logfile) == 0: - bb.msg.debug(2, bb.msg.domain.Build, "Zero size logfile %s, removing" % logfile) - os.remove(logfile) - - # close the backup fds - os.close(osi[0]) - os.close(oso[0]) - os.close(ose[0]) - - if ret==0: - if bb.msg.debug_level['default'] > 0: - os.remove(runfile) -# os.remove(logfile) + if ret == 0: return - else: - bb.msg.error(bb.msg.domain.Build, "function %s failed" % func) - if data.getVar("BBINCLUDELOGS", d): - bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile) - number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d) - if number_of_lines: - os.system('tail -n%s %s' % (number_of_lines, logfile)) - elif os.path.exists(logfile): - f = open(logfile, "r") - while True: - l = f.readline() - if l == '': - break - l = l.rstrip() - print '| %s' % l - f.close() - else: - bb.msg.error(bb.msg.domain.Build, "There was no logfile output") - else: - bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile) - raise FuncFailed( logfile ) + + bb.msg.error(bb.msg.domain.Build, "Function %s failed" % func) + raise FuncFailed("function %s failed" % func, logfile) def exec_task(task, d): @@ -282,14 +286,20 @@ def exec_task(task, d): data.setVar('OVERRIDES', 'task-%s:%s' % (task[3:], old_overrides), localdata) data.update_data(localdata) data.expandKeys(localdata) - event.fire(TaskStarted(task, localdata)) + event.fire(TaskStarted(task, localdata), localdata) exec_func(task, localdata) - event.fire(TaskSucceeded(task, localdata)) - except FuncFailed, reason: - bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % reason ) - failedevent = TaskFailed(task, d) - event.fire(failedevent) - raise EventException("Function failed in task: %s" % reason, failedevent) + event.fire(TaskSucceeded(task, localdata), localdata) + except FuncFailed, message: + # Try to extract the optional logfile + try: + (msg, logfile) = message + except: + logfile = None + msg = message + bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % message ) + failedevent = TaskFailed(msg, logfile, task, d) + event.fire(failedevent, d) + raise EventException("Function failed in task: %s" % message, failedevent) # make stamp, or cause event and raise exception if not data.getVarFlag(task, 'nostamp', d) and not data.getVarFlag(task, 'selfstamp', d): diff --git a/bitbake/lib/bb/cache.py b/bitbake/lib/bb/cache.py index d30d57d33..2f1b8fa60 100644 --- a/bitbake/lib/bb/cache.py +++ b/bitbake/lib/bb/cache.py @@ -134,7 +134,18 @@ class Cache: self.data = data # Make sure __depends makes the depends_cache - self.getVar("__depends", virtualfn, True) + # If we're a virtual class we need to make sure all our depends are appended + # to the depends of fn. + depends = self.getVar("__depends", virtualfn, True) or [] + if "__depends" not in self.depends_cache[fn] or not self.depends_cache[fn]["__depends"]: + self.depends_cache[fn]["__depends"] = depends + for dep in depends: + if dep not in self.depends_cache[fn]["__depends"]: + self.depends_cache[fn]["__depends"].append(dep) + + # Make sure BBCLASSEXTEND always makes the cache too + self.getVar('BBCLASSEXTEND', virtualfn, True) + self.depends_cache[virtualfn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn) def virtualfn2realfn(self, virtualfn): @@ -170,11 +181,8 @@ class Cache: bb.msg.debug(1, bb.msg.domain.Cache, "Parsing %s (full)" % fn) - bb_data, skipped = self.load_bbfile(fn, cfgData) - if isinstance(bb_data, dict): - return bb_data[cls] - - return bb_data + bb_data = self.load_bbfile(fn, cfgData) + return bb_data[cls] def loadData(self, fn, cfgData, cacheData): """ @@ -184,42 +192,39 @@ class Cache: to record the variables accessed. Return the cache status and whether the file was skipped when parsed """ + skipped = 0 + virtuals = 0 + if fn not in self.checked: self.cacheValidUpdate(fn) + if self.cacheValid(fn): - if "SKIPPED" in self.depends_cache[fn]: - return True, True - self.handle_data(fn, cacheData) multi = self.getVar('BBCLASSEXTEND', fn, True) - if multi: - for cls in multi.split(): - virtualfn = self.realfn2virtual(fn, cls) - # Pretend we're clean so getVar works - self.clean[virtualfn] = "" - self.handle_data(virtualfn, cacheData) - return True, False + for cls in (multi or "").split() + [""]: + virtualfn = self.realfn2virtual(fn, cls) + if self.depends_cache[virtualfn]["__SKIPPED"]: + skipped += 1 + bb.msg.debug(1, bb.msg.domain.Cache, "Skipping %s" % virtualfn) + continue + self.handle_data(virtualfn, cacheData) + virtuals += 1 + return True, skipped, virtuals bb.msg.debug(1, bb.msg.domain.Cache, "Parsing %s" % fn) - bb_data, skipped = self.load_bbfile(fn, cfgData) - - if skipped: - if isinstance(bb_data, dict): - self.setData(fn, fn, bb_data[""]) - else: - self.setData(fn, fn, bb_data) - return False, skipped + bb_data = self.load_bbfile(fn, cfgData) - if isinstance(bb_data, dict): - for data in bb_data: - virtualfn = self.realfn2virtual(fn, data) - self.setData(virtualfn, fn, bb_data[data]) + for data in bb_data: + virtualfn = self.realfn2virtual(fn, data) + self.setData(virtualfn, fn, bb_data[data]) + if self.getVar("__SKIPPED", virtualfn, True): + skipped += 1 + bb.msg.debug(1, bb.msg.domain.Cache, "Skipping %s" % virtualfn) + else: self.handle_data(virtualfn, cacheData) - return False, skipped + virtuals += 1 + return False, skipped, virtuals - self.setData(fn, fn, bb_data) - self.handle_data(fn, cacheData) - return False, skipped def cacheValid(self, fn): """ @@ -286,16 +291,13 @@ class Cache: if not fn in self.clean: self.clean[fn] = "" - return True + # Mark extended class data as clean too + multi = self.getVar('BBCLASSEXTEND', fn, True) + for cls in (multi or "").split(): + virtualfn = self.realfn2virtual(fn, cls) + self.clean[virtualfn] = "" - def skip(self, fn): - """ - Mark a fn as skipped - Called from the parser - """ - if not fn in self.depends_cache: - self.depends_cache[fn] = {} - self.depends_cache[fn]["SKIPPED"] = "1" + return True def remove(self, fn): """ @@ -462,10 +464,7 @@ class Cache: try: bb_data = parse.handle(bbfile, bb_data) # read .bb data os.chdir(oldpath) - return bb_data, False - except bb.parse.SkipPackage: - os.chdir(oldpath) - return bb_data, True + return bb_data except: os.chdir(oldpath) raise diff --git a/bitbake/lib/bb/command.py b/bitbake/lib/bb/command.py new file mode 100644 index 000000000..2bb5365c0 --- /dev/null +++ b/bitbake/lib/bb/command.py @@ -0,0 +1,271 @@ +""" +BitBake 'Command' module + +Provide an interface to interact with the bitbake server through 'commands' +""" + +# Copyright (C) 2006-2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +The bitbake server takes 'commands' from its UI/commandline. +Commands are either synchronous or asynchronous. +Async commands return data to the client in the form of events. +Sync commands must only return data through the function return value +and must not trigger events, directly or indirectly. +Commands are queued in a CommandQueue +""" + +import bb + +async_cmds = {} +sync_cmds = {} + +class Command: + """ + A queue of asynchronous commands for bitbake + """ + def __init__(self, cooker): + + self.cooker = cooker + self.cmds_sync = CommandsSync() + self.cmds_async = CommandsAsync() + + # FIXME Add lock for this + self.currentAsyncCommand = None + + for attr in CommandsSync.__dict__: + command = attr[:].lower() + method = getattr(CommandsSync, attr) + sync_cmds[command] = (method) + + for attr in CommandsAsync.__dict__: + command = attr[:].lower() + method = getattr(CommandsAsync, attr) + async_cmds[command] = (method) + + def runCommand(self, commandline): + try: + command = commandline.pop(0) + if command in CommandsSync.__dict__: + # Can run synchronous commands straight away + return getattr(CommandsSync, command)(self.cmds_sync, self, commandline) + if self.currentAsyncCommand is not None: + return "Busy (%s in progress)" % self.currentAsyncCommand[0] + if command not in CommandsAsync.__dict__: + return "No such command" + self.currentAsyncCommand = (command, commandline) + self.cooker.server.register_idle_function(self.cooker.runCommands, self.cooker) + return True + except: + import traceback + return traceback.format_exc() + + def runAsyncCommand(self): + try: + if self.currentAsyncCommand is not None: + (command, options) = self.currentAsyncCommand + commandmethod = getattr(CommandsAsync, command) + needcache = getattr( commandmethod, "needcache" ) + if needcache and self.cooker.cookerState != bb.cooker.cookerParsed: + self.cooker.updateCache() + return True + else: + commandmethod(self.cmds_async, self, options) + return False + else: + return False + except: + import traceback + self.finishAsyncCommand(traceback.format_exc()) + return False + + def finishAsyncCommand(self, error = None): + if error: + bb.event.fire(bb.command.CookerCommandFailed(error), self.cooker.configuration.event_data) + else: + bb.event.fire(bb.command.CookerCommandCompleted(), self.cooker.configuration.event_data) + self.currentAsyncCommand = None + + +class CommandsSync: + """ + A class of synchronous commands + These should run quickly so as not to hurt interactive performance. + These must not influence any running synchronous command. + """ + + def stateShutdown(self, command, params): + """ + Trigger cooker 'shutdown' mode + """ + command.cooker.cookerAction = bb.cooker.cookerShutdown + + def stateStop(self, command, params): + """ + Stop the cooker + """ + command.cooker.cookerAction = bb.cooker.cookerStop + + def getCmdLineAction(self, command, params): + """ + Get any command parsed from the commandline + """ + return command.cooker.commandlineAction + + def getVariable(self, command, params): + """ + Read the value of a variable from configuration.data + """ + varname = params[0] + expand = True + if len(params) > 1: + expand = params[1] + + return bb.data.getVar(varname, command.cooker.configuration.data, expand) + + def setVariable(self, command, params): + """ + Set the value of variable in configuration.data + """ + varname = params[0] + value = params[1] + bb.data.setVar(varname, value, command.cooker.configuration.data) + + +class CommandsAsync: + """ + A class of asynchronous commands + These functions communicate via generated events. + Any function that requires metadata parsing should be here. + """ + + def buildFile(self, command, params): + """ + Build a single specified .bb file + """ + bfile = params[0] + task = params[1] + + command.cooker.buildFile(bfile, task) + buildFile.needcache = False + + def buildTargets(self, command, params): + """ + Build a set of targets + """ + pkgs_to_build = params[0] + task = params[1] + + command.cooker.buildTargets(pkgs_to_build, task) + buildTargets.needcache = True + + def generateDepTreeEvent(self, command, params): + """ + Generate an event containing the dependency information + """ + pkgs_to_build = params[0] + task = params[1] + + command.cooker.generateDepTreeEvent(pkgs_to_build, task) + command.finishAsyncCommand() + generateDepTreeEvent.needcache = True + + def generateDotGraph(self, command, params): + """ + Dump dependency information to disk as .dot files + """ + pkgs_to_build = params[0] + task = params[1] + + command.cooker.generateDotGraphFiles(pkgs_to_build, task) + command.finishAsyncCommand() + generateDotGraph.needcache = True + + def showVersions(self, command, params): + """ + Show the currently selected versions + """ + command.cooker.showVersions() + command.finishAsyncCommand() + showVersions.needcache = True + + def showEnvironmentTarget(self, command, params): + """ + Print the environment of a target recipe + (needs the cache to work out which recipe to use) + """ + pkg = params[0] + + command.cooker.showEnvironment(None, pkg) + command.finishAsyncCommand() + showEnvironmentTarget.needcache = True + + def showEnvironment(self, command, params): + """ + Print the standard environment + or if specified the environment for a specified recipe + """ + bfile = params[0] + + command.cooker.showEnvironment(bfile) + command.finishAsyncCommand() + showEnvironment.needcache = False + + def parseFiles(self, command, params): + """ + Parse the .bb files + """ + command.cooker.updateCache() + command.finishAsyncCommand() + parseFiles.needcache = True + + def compareRevisions(self, command, params): + """ + Parse the .bb files + """ + command.cooker.compareRevisions() + command.finishAsyncCommand() + compareRevisions.needcache = True + +# +# Events +# +class CookerCommandCompleted(bb.event.Event): + """ + Cooker command completed + """ + def __init__(self): + bb.event.Event.__init__(self) + + +class CookerCommandFailed(bb.event.Event): + """ + Cooker command completed + """ + def __init__(self, error): + bb.event.Event.__init__(self) + self.error = error + +class CookerCommandSetExitCode(bb.event.Event): + """ + Set the exit code for a cooker command + """ + def __init__(self, exitcode): + bb.event.Event.__init__(self) + self.exitcode = int(exitcode) + + + diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py index 14ccfb59a..8036d7e9d 100644 --- a/bitbake/lib/bb/cooker.py +++ b/bitbake/lib/bb/cooker.py @@ -7,7 +7,7 @@ # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer # Copyright (C) 2005 Holger Hans Peter Freyther # Copyright (C) 2005 ROAD GmbH -# Copyright (C) 2006 Richard Purdie +# Copyright (C) 2006 - 2007 Richard Purdie # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -25,9 +25,35 @@ import sys, os, getopt, glob, copy, os.path, re, time import bb from bb import utils, data, parse, event, cache, providers, taskdata, runqueue +from bb import command +import bb.server.xmlrpc import itertools, sre_constants -parsespin = itertools.cycle( r'|/-\\' ) +class MultipleMatches(Exception): + """ + Exception raised when multiple file matches are found + """ + +class ParsingErrorsFound(Exception): + """ + Exception raised when parsing errors are found + """ + +class NothingToBuild(Exception): + """ + Exception raised when there is nothing to build + """ + + +# Different states cooker can be in +cookerClean = 1 +cookerParsing = 2 +cookerParsed = 3 + +# Different action states the cooker can be in +cookerRun = 1 # Cooker is running normally +cookerShutdown = 2 # Active tasks should be brought to a controlled stop +cookerStop = 3 # Stop, now! #============================================================================# # BBCooker @@ -37,12 +63,14 @@ class BBCooker: Manages one bitbake build run """ - def __init__(self, configuration): + def __init__(self, configuration, server): self.status = None self.cache = None self.bb_cache = None + self.server = server.BitBakeServer(self) + self.configuration = configuration if self.configuration.verbose: @@ -58,17 +86,15 @@ class BBCooker: self.configuration.data = bb.data.init() - def parseConfiguration(self): - bb.data.inheritFromOS(self.configuration.data) - # Add conf/bitbake.conf to the list of configuration files to read - self.configuration.file.append( os.path.join( "conf", "bitbake.conf" ) ) + for f in self.configuration.file: + self.parseConfigurationFile( f ) - self.parseConfigurationFile(self.configuration.file) + self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) ) if not self.configuration.cmd: - self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data) or "build" + self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data, True) or "build" bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, True) if bbpkgs and len(self.configuration.pkgs_to_build) == 0: @@ -80,9 +106,7 @@ class BBCooker: self.configuration.event_data = bb.data.createCopy(self.configuration.data) bb.data.update_data(self.configuration.event_data) - # # TOSTOP must not be set or our children will hang when they output - # fd = sys.stdout.fileno() if os.isatty(fd): import termios @@ -92,40 +116,91 @@ class BBCooker: tcattr[3] = tcattr[3] & ~termios.TOSTOP termios.tcsetattr(fd, termios.TCSANOW, tcattr) + self.command = bb.command.Command(self) + self.cookerState = cookerClean + self.cookerAction = cookerRun + + def parseConfiguration(self): + + # Change nice level if we're asked to nice = bb.data.getVar("BB_NICE_LEVEL", self.configuration.data, True) if nice: curnice = os.nice(0) nice = int(nice) - curnice bb.msg.note(2, bb.msg.domain.Build, "Renice to %s " % os.nice(nice)) - + + def parseCommandLine(self): + # Parse any commandline into actions + if self.configuration.show_environment: + self.commandlineAction = None + + if 'world' in self.configuration.pkgs_to_build: + bb.error("'world' is not a valid target for --environment.") + elif len(self.configuration.pkgs_to_build) > 1: + bb.error("Only one target can be used with the --environment option.") + elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0: + bb.error("No target should be used with the --environment and --buildfile options.") + elif len(self.configuration.pkgs_to_build) > 0: + self.commandlineAction = ["showEnvironmentTarget", self.configuration.pkgs_to_build] + else: + self.commandlineAction = ["showEnvironment", self.configuration.buildfile] + elif self.configuration.buildfile is not None: + self.commandlineAction = ["buildFile", self.configuration.buildfile, self.configuration.cmd] + elif self.configuration.revisions_changed: + self.commandlineAction = ["compareRevisions"] + elif self.configuration.show_versions: + self.commandlineAction = ["showVersions"] + elif self.configuration.parse_only: + self.commandlineAction = ["parseFiles"] + # FIXME - implement + #elif self.configuration.interactive: + # self.interactiveMode() + elif self.configuration.dot_graph: + if self.configuration.pkgs_to_build: + self.commandlineAction = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd] + else: + self.commandlineAction = None + bb.error("Please specify a package name for dependency graph generation.") + else: + if self.configuration.pkgs_to_build: + self.commandlineAction = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd] + else: + self.commandlineAction = None + bb.error("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") + + def runCommands(self, server, data, abort): + """ + Run any queued asynchronous command + This is done by the idle handler so it runs in true context rather than + tied to any UI. + """ + + return self.command.runAsyncCommand() def tryBuildPackage(self, fn, item, task, the_data): """ Build one task of a package, optionally build following task depends """ - bb.event.fire(bb.event.PkgStarted(item, the_data)) try: if not self.configuration.dry_run: bb.build.exec_task('do_%s' % task, the_data) - bb.event.fire(bb.event.PkgSucceeded(item, the_data)) return True except bb.build.FuncFailed: bb.msg.error(bb.msg.domain.Build, "task stack execution failed") - bb.event.fire(bb.event.PkgFailed(item, the_data)) raise except bb.build.EventException, e: event = e.args[1] bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event)) - bb.event.fire(bb.event.PkgFailed(item, the_data)) raise - def tryBuild(self, fn): + def tryBuild(self, fn, task): """ Build a provider and its dependencies. build_depends is a list of previous build dependencies (not runtime) If build_depends is empty, we're dealing with a runtime depends """ + the_data = self.bb_cache.loadDataFull(fn, self.configuration.data) item = self.status.pkg_fn[fn] @@ -133,9 +208,13 @@ class BBCooker: #if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data): # return True - return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data) + return self.tryBuildPackage(fn, item, task, the_data) def showVersions(self): + + # Need files parsed + self.updateCache() + pkg_pn = self.status.pkg_pn preferred_versions = {} latest_versions = {} @@ -149,43 +228,36 @@ class BBCooker: pkg_list = pkg_pn.keys() pkg_list.sort() + bb.msg.plain("%-35s %25s %25s" % ("Package Name", "Latest Version", "Preferred Version")) + bb.msg.plain("%-35s %25s %25s\n" % ("============", "==============", "=================")) + for p in pkg_list: pref = preferred_versions[p] latest = latest_versions[p] - if pref != latest: - prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2] - else: + prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2] + lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2] + + if pref == latest: prefstr = "" - print "%-30s %20s %20s" % (p, latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2], - prefstr) + bb.msg.plain("%-35s %25s %25s" % (p, lateststr, prefstr)) + def compareRevisions(self): + ret = bb.fetch.fetcher_compare_revisons(self.configuration.data) + bb.event.fire(bb.command.CookerCommandSetExitCode(ret), self.configuration.event_data) - def showEnvironment(self , buildfile = None, pkgs_to_build = []): + def showEnvironment(self, buildfile = None, pkgs_to_build = []): """ Show the outer or per-package environment """ fn = None envdata = None - if 'world' in pkgs_to_build: - print "'world' is not a valid target for --environment." - sys.exit(1) - - if len(pkgs_to_build) > 1: - print "Only one target can be used with the --environment option." - sys.exit(1) - if buildfile: - if len(pkgs_to_build) > 0: - print "No target should be used with the --environment and --buildfile options." - sys.exit(1) self.cb = None self.bb_cache = bb.cache.init(self) fn = self.matchFile(buildfile) - if not fn: - sys.exit(1) elif len(pkgs_to_build) == 1: self.updateCache() @@ -193,13 +265,9 @@ class BBCooker: bb.data.update_data(localdata) bb.data.expandKeys(localdata) - taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) - - try: - taskdata.add_provider(localdata, self.status, pkgs_to_build[0]) - taskdata.add_unresolved(localdata, self.status) - except bb.providers.NoProvider: - sys.exit(1) + taskdata = bb.taskdata.TaskData(self.configuration.abort) + taskdata.add_provider(localdata, self.status, pkgs_to_build[0]) + taskdata.add_unresolved(localdata, self.status) targetid = taskdata.getbuild_id(pkgs_to_build[0]) fnid = taskdata.build_targets[targetid][0] @@ -211,55 +279,69 @@ class BBCooker: try: envdata = self.bb_cache.loadDataFull(fn, self.configuration.data) except IOError, e: - bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e)) + bb.msg.error(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e)) + raise except Exception, e: - bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) + bb.msg.error(bb.msg.domain.Parsing, "%s" % e) + raise + + class dummywrite: + def __init__(self): + self.writebuf = "" + def write(self, output): + self.writebuf = self.writebuf + output # emit variables and shell functions try: - data.update_data( envdata ) - data.emit_env(sys.__stdout__, envdata, True) + data.update_data(envdata) + wb = dummywrite() + data.emit_env(wb, envdata, True) + bb.msg.plain(wb.writebuf) except Exception, e: bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) # emit the metadata which isnt valid shell - data.expandKeys( envdata ) + data.expandKeys(envdata) for e in envdata.keys(): if data.getVarFlag( e, 'python', envdata ): - sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1))) + bb.msg.plain("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1))) - def generateDotGraph( self, pkgs_to_build, ignore_deps ): + def generateDepTreeData(self, pkgs_to_build, task): """ - Generate a task dependency graph. - - pkgs_to_build A list of packages that needs to be built - ignore_deps A list of names where processing of dependencies - should be stopped. e.g. dependencies that get + Create a dependency tree of pkgs_to_build, returning the data. """ - for dep in ignore_deps: - self.status.ignored_dependencies.add(dep) + # Need files parsed + self.updateCache() + + # If we are told to do the None task then query the default task + if (task == None): + task = self.configuration.cmd + + pkgs_to_build = self.checkPackages(pkgs_to_build) localdata = data.createCopy(self.configuration.data) bb.data.update_data(localdata) bb.data.expandKeys(localdata) - taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) + taskdata = bb.taskdata.TaskData(self.configuration.abort) runlist = [] - try: - for k in pkgs_to_build: - taskdata.add_provider(localdata, self.status, k) - runlist.append([k, "do_%s" % self.configuration.cmd]) - taskdata.add_unresolved(localdata, self.status) - except bb.providers.NoProvider: - sys.exit(1) + for k in pkgs_to_build: + taskdata.add_provider(localdata, self.status, k) + runlist.append([k, "do_%s" % task]) + taskdata.add_unresolved(localdata, self.status) + rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) rq.prepare_runqueue() seen_fnids = [] - depends_file = file('depends.dot', 'w' ) - tdepends_file = file('task-depends.dot', 'w' ) - print >> depends_file, "digraph depends {" - print >> tdepends_file, "digraph depends {" + depend_tree = {} + depend_tree["depends"] = {} + depend_tree["tdepends"] = {} + depend_tree["pn"] = {} + depend_tree["rdepends-pn"] = {} + depend_tree["packages"] = {} + depend_tree["rdepends-pkg"] = {} + depend_tree["rrecs-pkg"] = {} for task in range(len(rq.runq_fnid)): taskname = rq.runq_task[task] @@ -267,43 +349,118 @@ class BBCooker: fn = taskdata.fn_index[fnid] pn = self.status.pkg_fn[fn] version = "%s:%s-%s" % self.status.pkg_pepvpr[fn] - print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn) + if pn not in depend_tree["pn"]: + depend_tree["pn"][pn] = {} + depend_tree["pn"][pn]["filename"] = fn + depend_tree["pn"][pn]["version"] = version for dep in rq.runq_depends[task]: depfn = taskdata.fn_index[rq.runq_fnid[dep]] deppn = self.status.pkg_fn[depfn] - print >> tdepends_file, '"%s.%s" -> "%s.%s"' % (pn, rq.runq_task[task], deppn, rq.runq_task[dep]) + dotname = "%s.%s" % (pn, rq.runq_task[task]) + if not dotname in depend_tree["tdepends"]: + depend_tree["tdepends"][dotname] = [] + depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, rq.runq_task[dep])) if fnid not in seen_fnids: seen_fnids.append(fnid) packages = [] - print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) - for depend in self.status.deps[fn]: - print >> depends_file, '"%s" -> "%s"' % (pn, depend) + + depend_tree["depends"][pn] = [] + for dep in taskdata.depids[fnid]: + depend_tree["depends"][pn].append(taskdata.build_names_index[dep]) + + depend_tree["rdepends-pn"][pn] = [] + for rdep in taskdata.rdepids[fnid]: + depend_tree["rdepends-pn"][pn].append(taskdata.run_names_index[rdep]) + rdepends = self.status.rundeps[fn] for package in rdepends: - for rdepend in re.findall("([\w.-]+)(\ \(.+\))?", rdepends[package]): - print >> depends_file, '"%s" -> "%s%s" [style=dashed]' % (package, rdepend[0], rdepend[1]) + depend_tree["rdepends-pkg"][package] = [] + for rdepend in rdepends[package]: + depend_tree["rdepends-pkg"][package].append(rdepend) packages.append(package) + rrecs = self.status.runrecs[fn] for package in rrecs: - for rdepend in re.findall("([\w.-]+)(\ \(.+\))?", rrecs[package]): - print >> depends_file, '"%s" -> "%s%s" [style=dashed]' % (package, rdepend[0], rdepend[1]) + depend_tree["rrecs-pkg"][package] = [] + for rdepend in rrecs[package]: + depend_tree["rrecs-pkg"][package].append(rdepend) if not package in packages: packages.append(package) + for package in packages: - if package != pn: - print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn) - for depend in self.status.deps[fn]: - print >> depends_file, '"%s" -> "%s"' % (package, depend) - # Prints a flattened form of the above where subpackages of a package are merged into the main pn - #print >> depends_file, '"%s" [label="%s %s\\n%s\\n%s"]' % (pn, pn, taskname, version, fn) - #for rdep in taskdata.rdepids[fnid]: - # print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, taskdata.run_names_index[rdep]) - #for dep in taskdata.depids[fnid]: - # print >> depends_file, '"%s" -> "%s"' % (pn, taskdata.build_names_index[dep]) + if package not in depend_tree["packages"]: + depend_tree["packages"][package] = {} + depend_tree["packages"][package]["pn"] = pn + depend_tree["packages"][package]["filename"] = fn + depend_tree["packages"][package]["version"] = version + + return depend_tree + + + def generateDepTreeEvent(self, pkgs_to_build, task): + """ + Create a task dependency graph of pkgs_to_build. + Generate an event with the result + """ + depgraph = self.generateDepTreeData(pkgs_to_build, task) + bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.configuration.data) + + def generateDotGraphFiles(self, pkgs_to_build, task): + """ + Create a task dependency graph of pkgs_to_build. + Save the result to a set of .dot files. + """ + + depgraph = self.generateDepTreeData(pkgs_to_build, task) + + # Prints a flattened form of package-depends below where subpackages of a package are merged into the main pn + depends_file = file('pn-depends.dot', 'w' ) + print >> depends_file, "digraph depends {" + for pn in depgraph["pn"]: + fn = depgraph["pn"][pn]["filename"] + version = depgraph["pn"][pn]["version"] + print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) + for pn in depgraph["depends"]: + for depend in depgraph["depends"][pn]: + print >> depends_file, '"%s" -> "%s"' % (pn, depend) + for pn in depgraph["rdepends-pn"]: + for rdepend in depgraph["rdepends-pn"][pn]: + print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, rdepend) + print >> depends_file, "}" + bb.msg.plain("PN dependencies saved to 'pn-depends.dot'") + + depends_file = file('package-depends.dot', 'w' ) + print >> depends_file, "digraph depends {" + for package in depgraph["packages"]: + pn = depgraph["packages"][package]["pn"] + fn = depgraph["packages"][package]["filename"] + version = depgraph["packages"][package]["version"] + if package == pn: + print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) + else: + print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn) + for depend in depgraph["depends"][pn]: + print >> depends_file, '"%s" -> "%s"' % (package, depend) + for package in depgraph["rdepends-pkg"]: + for rdepend in depgraph["rdepends-pkg"][package]: + print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) + for package in depgraph["rrecs-pkg"]: + for rdepend in depgraph["rrecs-pkg"][package]: + print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) print >> depends_file, "}" + bb.msg.plain("Package dependencies saved to 'package-depends.dot'") + + tdepends_file = file('task-depends.dot', 'w' ) + print >> tdepends_file, "digraph depends {" + for task in depgraph["tdepends"]: + (pn, taskname) = task.rsplit(".", 1) + fn = depgraph["pn"][pn]["filename"] + version = depgraph["pn"][pn]["version"] + print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn) + for dep in depgraph["tdepends"][task]: + print >> tdepends_file, '"%s" -> "%s"' % (task, dep) print >> tdepends_file, "}" - bb.msg.note(1, bb.msg.domain.Collection, "Dependencies saved to 'depends.dot'") - bb.msg.note(1, bb.msg.domain.Collection, "Task dependencies saved to 'task-depends.dot'") + bb.msg.plain("Task dependencies saved to 'task-depends.dot'") def buildDepgraph( self ): all_depends = self.status.all_depends @@ -324,7 +481,7 @@ class BBCooker: try: (providee, provider) = p.split(':') except: - bb.msg.error(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p) + bb.msg.fatal(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p) continue if providee in self.status.preferred and self.status.preferred[providee] != provider: bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee])) @@ -362,19 +519,6 @@ class BBCooker: self.status.possible_world = None self.status.all_depends = None - def myProgressCallback( self, x, y, f, from_cache ): - """Update any tty with the progress change""" - if os.isatty(sys.stdout.fileno()): - sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) - sys.stdout.flush() - else: - if x == 1: - sys.stdout.write("Parsing .bb files, please wait...") - sys.stdout.flush() - if x == y: - sys.stdout.write("done.") - sys.stdout.flush() - def interactiveMode( self ): """Drop off into a shell""" try: @@ -383,12 +527,10 @@ class BBCooker: bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details ) else: shell.start( self ) - sys.exit( 0 ) - def parseConfigurationFile( self, afiles ): + def parseConfigurationFile( self, afile ): try: - for afile in afiles: - self.configuration.data = bb.parse.handle( afile, self.configuration.data ) + self.configuration.data = bb.parse.handle( afile, self.configuration.data ) # Handle any INHERITs and inherit the base class inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split() @@ -402,10 +544,10 @@ class BBCooker: bb.fetch.fetcher_init(self.configuration.data) - bb.event.fire(bb.event.ConfigParsed(self.configuration.data)) + bb.event.fire(bb.event.ConfigParsed(), self.configuration.data) except IOError, e: - bb.msg.fatal(bb.msg.domain.Parsing, "IO Error: %s" % str(e) ) + bb.msg.fatal(bb.msg.domain.Parsing, "Error when parsing %s: %s" % (afile, str(e))) except bb.parse.ParseError, details: bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) ) @@ -439,17 +581,17 @@ class BBCooker: """ if not bb.data.getVar("BUILDNAME", self.configuration.data): bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data) - bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data) + bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()), self.configuration.data) - def matchFile(self, buildfile): + def matchFiles(self, buildfile): """ - Convert the fragment buildfile into a real file - Error if there are too many matches + Find the .bb files which match the expression in 'buildfile'. """ + bf = os.path.abspath(buildfile) try: os.stat(bf) - return bf + return [bf] except OSError: (filelist, masked) = self.collect_bbfiles() regexp = re.compile(buildfile) @@ -458,27 +600,41 @@ class BBCooker: if regexp.search(f) and os.path.isfile(f): bf = f matches.append(f) - if len(matches) != 1: - bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches))) - for f in matches: - bb.msg.error(bb.msg.domain.Parsing, " %s" % f) - return False - return matches[0] + return matches - def buildFile(self, buildfile): + def matchFile(self, buildfile): + """ + Find the .bb file which matches the expression in 'buildfile'. + Raise an error if multiple files + """ + matches = self.matchFiles(buildfile) + if len(matches) != 1: + bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches))) + for f in matches: + bb.msg.error(bb.msg.domain.Parsing, " %s" % f) + raise MultipleMatches + return matches[0] + + def buildFile(self, buildfile, task): """ Build the file matching regexp buildfile """ - # Make sure our target is a fully qualified filename + # Parse the configuration here. We need to do it explicitly here since + # buildFile() doesn't use the cache + self.parseConfiguration() + + # If we are told to do the None task then query the default task + if (task == None): + task = self.configuration.cmd + fn = self.matchFile(buildfile) - if not fn: - return False + self.buildSetVars() # Load data into the cache for fn and parse the loaded cache data self.bb_cache = bb.cache.init(self) self.status = bb.cache.CacheData() - self.bb_cache.loadData(fn, self.configuration.data, self.status) + self.bb_cache.loadData(fn, self.configuration.data, self.status) # Tweak some variables item = self.bb_cache.getVar('PN', fn, True) @@ -493,159 +649,157 @@ class BBCooker: # Remove stamp for target if force mode active if self.configuration.force: - bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (self.configuration.cmd, fn)) - bb.build.del_stamp('do_%s' % self.configuration.cmd, self.configuration.data) + bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (task, fn)) + bb.build.del_stamp('do_%s' % task, self.status, fn) # Setup taskdata structure - taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) + taskdata = bb.taskdata.TaskData(self.configuration.abort) taskdata.add_provider(self.configuration.data, self.status, item) buildname = bb.data.getVar("BUILDNAME", self.configuration.data) - bb.event.fire(bb.event.BuildStarted(buildname, [item], self.configuration.event_data)) + bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.configuration.event_data) # Execute the runqueue - runlist = [[item, "do_%s" % self.configuration.cmd]] + runlist = [[item, "do_%s" % task]] + rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - rq.prepare_runqueue() - try: - failures = rq.execute_runqueue() - except runqueue.TaskFailure, fnids: + + def buildFileIdle(server, rq, abort): + + if abort or self.cookerAction == cookerStop: + rq.finish_runqueue(True) + elif self.cookerAction == cookerShutdown: + rq.finish_runqueue(False) failures = 0 - for fnid in fnids: - bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) - failures = failures + 1 - bb.event.fire(bb.event.BuildCompleted(buildname, [item], self.configuration.event_data, failures)) - return False - bb.event.fire(bb.event.BuildCompleted(buildname, [item], self.configuration.event_data, failures)) - return True + try: + retval = rq.execute_runqueue() + except runqueue.TaskFailure, fnids: + for fnid in fnids: + bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) + failures = failures + 1 + retval = False + if not retval: + self.command.finishAsyncCommand() + bb.event.fire(bb.event.BuildCompleted(buildname, item, failures), self.configuration.event_data) + return False + return 0.5 + + self.server.register_idle_function(buildFileIdle, rq) - def buildTargets(self, targets): + def buildTargets(self, targets, task): """ Attempt to build the targets specified """ - buildname = bb.data.getVar("BUILDNAME", self.configuration.data) - bb.event.fire(bb.event.BuildStarted(buildname, targets, self.configuration.event_data)) + # Need files parsed + self.updateCache() - localdata = data.createCopy(self.configuration.data) - bb.data.update_data(localdata) - bb.data.expandKeys(localdata) + # If we are told to do the NULL task then query the default task + if (task == None): + task = self.configuration.cmd - taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) + targets = self.checkPackages(targets) - runlist = [] - try: - for k in targets: - taskdata.add_provider(localdata, self.status, k) - runlist.append([k, "do_%s" % self.configuration.cmd]) - taskdata.add_unresolved(localdata, self.status) - except bb.providers.NoProvider: - sys.exit(1) + def buildTargetsIdle(server, rq, abort): - rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - rq.prepare_runqueue() - try: - failures = rq.execute_runqueue() - except runqueue.TaskFailure, fnids: + if abort or self.cookerAction == cookerStop: + rq.finish_runqueue(True) + elif self.cookerAction == cookerShutdown: + rq.finish_runqueue(False) failures = 0 - for fnid in fnids: - bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) - failures = failures + 1 - bb.event.fire(bb.event.BuildCompleted(buildname, targets, self.configuration.event_data, failures)) - sys.exit(1) - bb.event.fire(bb.event.BuildCompleted(buildname, targets, self.configuration.event_data, failures)) + try: + retval = rq.execute_runqueue() + except runqueue.TaskFailure, fnids: + for fnid in fnids: + bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) + failures = failures + 1 + retval = False + if not retval: + self.command.finishAsyncCommand() + bb.event.fire(bb.event.BuildCompleted(buildname, targets, failures), self.configuration.event_data) + return None + return 0.5 - sys.exit(0) + self.buildSetVars() - def updateCache(self): - # Import Psyco if available and not disabled - import platform - if platform.machine() in ['i386', 'i486', 'i586', 'i686']: - if not self.configuration.disable_psyco: - try: - import psyco - except ImportError: - bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.") - else: - psyco.bind( self.parse_bbfiles ) - else: - bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.") + buildname = bb.data.getVar("BUILDNAME", self.configuration.data) + bb.event.fire(bb.event.BuildStarted(buildname, targets), self.configuration.event_data) - self.status = bb.cache.CacheData() + localdata = data.createCopy(self.configuration.data) + bb.data.update_data(localdata) + bb.data.expandKeys(localdata) - ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" - self.status.ignored_dependencies = set( ignore.split() ) + taskdata = bb.taskdata.TaskData(self.configuration.abort) - self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) ) + runlist = [] + for k in targets: + taskdata.add_provider(localdata, self.status, k) + runlist.append([k, "do_%s" % task]) + taskdata.add_unresolved(localdata, self.status) - bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files") - (filelist, masked) = self.collect_bbfiles() - bb.data.renameVar("__depends", "__base_depends", self.configuration.data) - self.parse_bbfiles(filelist, masked, self.myProgressCallback) - bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete") + rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) - self.buildDepgraph() + self.server.register_idle_function(buildTargetsIdle, rq) - def cook(self): - """ - We are building stuff here. We do the building - from here. By default we try to execute task - build. - """ + def updateCache(self): - # Wipe the OS environment - bb.utils.empty_environment() + if self.cookerState == cookerParsed: + return - if self.configuration.show_environment: - self.showEnvironment(self.configuration.buildfile, self.configuration.pkgs_to_build) - sys.exit( 0 ) + if self.cookerState != cookerParsing: - self.buildSetVars() + self.parseConfiguration () - if self.configuration.interactive: - self.interactiveMode() + # Import Psyco if available and not disabled + import platform + if platform.machine() in ['i386', 'i486', 'i586', 'i686']: + if not self.configuration.disable_psyco: + try: + import psyco + except ImportError: + bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.") + else: + psyco.bind( CookerParser.parse_next ) + else: + bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.") - if self.configuration.buildfile is not None: - if not self.buildFile(self.configuration.buildfile): - sys.exit(1) - sys.exit(0) + self.status = bb.cache.CacheData() - # initialise the parsing status now we know we will need deps - self.updateCache() + ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" + self.status.ignored_dependencies = set(ignore.split()) + + for dep in self.configuration.extra_assume_provided: + self.status.ignored_dependencies.add(dep) + + self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) ) - if self.configuration.revisions_changed: - sys.exit(bb.fetch.fetcher_compare_revisons(self.configuration.data)) + bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files") + (filelist, masked) = self.collect_bbfiles() + bb.data.renameVar("__depends", "__base_depends", self.configuration.data) - if self.configuration.parse_only: - bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.") - return 0 + self.parser = CookerParser(self, filelist, masked) + self.cookerState = cookerParsing - pkgs_to_build = self.configuration.pkgs_to_build + if not self.parser.parse_next(): + bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete") + self.buildDepgraph() + self.cookerState = cookerParsed + return None - if len(pkgs_to_build) == 0 and not self.configuration.show_versions: - print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'" - print "for usage information." - sys.exit(0) + return True - try: - if self.configuration.show_versions: - self.showVersions() - sys.exit( 0 ) - if 'world' in pkgs_to_build: - self.buildWorldTargetList() - pkgs_to_build.remove('world') - for t in self.status.world_target: - pkgs_to_build.append(t) + def checkPackages(self, pkgs_to_build): - if self.configuration.dot_graph: - self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps ) - sys.exit( 0 ) + if len(pkgs_to_build) == 0: + raise NothingToBuild - return self.buildTargets(pkgs_to_build) + if 'world' in pkgs_to_build: + self.buildWorldTargetList() + pkgs_to_build.remove('world') + for t in self.status.world_target: + pkgs_to_build.append(t) - except KeyboardInterrupt: - bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.") - sys.exit(1) + return pkgs_to_build def get_bbfiles( self, path = os.getcwd() ): """Get list of default .bb files by reading out the current directory""" @@ -717,59 +871,108 @@ class BBCooker: return (finalfiles, masked) - def parse_bbfiles(self, filelist, masked, progressCallback = None): - parsed, cached, skipped, error = 0, 0, 0, 0 - for i in xrange( len( filelist ) ): - f = filelist[i] + def serve(self): - #bb.msg.debug(1, bb.msg.domain.Collection, "parsing %s" % f) + # Empty the environment. The environment will be populated as + # necessary from the data store. + bb.utils.empty_environment() - # read a file's metadata + if self.configuration.profile: try: - fromCache, skip = self.bb_cache.loadData(f, self.configuration.data, self.status) - if skip: - skipped += 1 - bb.msg.debug(2, bb.msg.domain.Collection, "skipping %s" % f) - self.bb_cache.skip(f) - continue - elif fromCache: cached += 1 - else: parsed += 1 - - # Disabled by RP as was no longer functional - # allow metadata files to add items to BBFILES - #data.update_data(self.pkgdata[f]) - #addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None - #if addbbfiles: - # for aof in addbbfiles.split(): - # if not files.count(aof): - # if not os.path.isabs(aof): - # aof = os.path.join(os.path.dirname(f),aof) - # files.append(aof) - - # now inform the caller - if progressCallback is not None: - progressCallback( i + 1, len( filelist ), f, fromCache ) + import cProfile as profile + except: + import profile + + profile.runctx("self.server.serve_forever()", globals(), locals(), "profile.log") + + # Redirect stdout to capture profile information + pout = open('profile.log.processed', 'w') + so = sys.stdout.fileno() + os.dup2(pout.fileno(), so) + + import pstats + p = pstats.Stats('profile.log') + p.sort_stats('time') + p.print_stats() + p.print_callers() + p.sort_stats('cumulative') + p.print_stats() + + os.dup2(so, pout.fileno()) + pout.flush() + pout.close() + else: + self.server.serve_forever() + + bb.event.fire(CookerExit(), self.configuration.event_data) + +class CookerExit(bb.event.Event): + """ + Notify clients of the Cooker shutdown + """ + + def __init__(self): + bb.event.Event.__init__(self) + +class CookerParser: + def __init__(self, cooker, filelist, masked): + # Internal data + self.filelist = filelist + self.cooker = cooker + + # Accounting statistics + self.parsed = 0 + self.cached = 0 + self.error = 0 + self.masked = masked + self.total = len(filelist) + + self.skipped = 0 + self.virtuals = 0 + + # Pointer to the next file to parse + self.pointer = 0 + + def parse_next(self): + if self.pointer < len(self.filelist): + f = self.filelist[self.pointer] + cooker = self.cooker + + try: + fromCache, skipped, virtuals = cooker.bb_cache.loadData(f, cooker.configuration.data, cooker.status) + if fromCache: + self.cached += 1 + else: + self.parsed += 1 + + self.skipped += skipped + self.virtuals += virtuals except IOError, e: - self.bb_cache.remove(f) + self.error += 1 + cooker.bb_cache.remove(f) bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e)) pass except KeyboardInterrupt: - self.bb_cache.sync() + cooker.bb_cache.remove(f) + cooker.bb_cache.sync() raise except Exception, e: - error += 1 - self.bb_cache.remove(f) + self.error += 1 + cooker.bb_cache.remove(f) bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f)) except: - self.bb_cache.remove(f) + cooker.bb_cache.remove(f) raise + finally: + bb.event.fire(bb.event.ParseProgress(self.cached, self.parsed, self.skipped, self.masked, self.virtuals, self.error, self.total), cooker.configuration.event_data) - if progressCallback is not None: - print "\r" # need newline after Handling Bitbake files message - bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked )) + self.pointer += 1 - self.bb_cache.sync() + if self.pointer >= self.total: + cooker.bb_cache.sync() + if self.error > 0: + raise ParsingErrorsFound + return False + return True - if error > 0: - bb.msg.fatal(bb.msg.domain.Collection, "Parsing errors found, exiting...") diff --git a/bitbake/lib/bb/daemonize.py b/bitbake/lib/bb/daemonize.py new file mode 100644 index 000000000..1a8bb379f --- /dev/null +++ b/bitbake/lib/bb/daemonize.py @@ -0,0 +1,191 @@ +""" +Python Deamonizing helper + +Configurable daemon behaviors: + + 1.) The current working directory set to the "/" directory. + 2.) The current file creation mode mask set to 0. + 3.) Close all open files (1024). + 4.) Redirect standard I/O streams to "/dev/null". + +A failed call to fork() now raises an exception. + +References: + 1) Advanced Programming in the Unix Environment: W. Richard Stevens + 2) Unix Programming Frequently Asked Questions: + http://www.erlenstar.demon.co.uk/unix/faq_toc.html + +Modified to allow a function to be daemonized and return for +bitbake use by Richard Purdie +""" + +__author__ = "Chad J. Schroeder" +__copyright__ = "Copyright (C) 2005 Chad J. Schroeder" +__version__ = "0.2" + +# Standard Python modules. +import os # Miscellaneous OS interfaces. +import sys # System-specific parameters and functions. + +# Default daemon parameters. +# File mode creation mask of the daemon. +# For BitBake's children, we do want to inherit the parent umask. +UMASK = None + +# Default maximum for the number of available file descriptors. +MAXFD = 1024 + +# The standard I/O file descriptors are redirected to /dev/null by default. +if (hasattr(os, "devnull")): + REDIRECT_TO = os.devnull +else: + REDIRECT_TO = "/dev/null" + +def createDaemon(function, logfile): + """ + Detach a process from the controlling terminal and run it in the + background as a daemon, returning control to the caller. + """ + + try: + # Fork a child process so the parent can exit. This returns control to + # the command-line or shell. It also guarantees that the child will not + # be a process group leader, since the child receives a new process ID + # and inherits the parent's process group ID. This step is required + # to insure that the next call to os.setsid is successful. + pid = os.fork() + except OSError, e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if (pid == 0): # The first child. + # To become the session leader of this new session and the process group + # leader of the new process group, we call os.setsid(). The process is + # also guaranteed not to have a controlling terminal. + os.setsid() + + # Is ignoring SIGHUP necessary? + # + # It's often suggested that the SIGHUP signal should be ignored before + # the second fork to avoid premature termination of the process. The + # reason is that when the first child terminates, all processes, e.g. + # the second child, in the orphaned group will be sent a SIGHUP. + # + # "However, as part of the session management system, there are exactly + # two cases where SIGHUP is sent on the death of a process: + # + # 1) When the process that dies is the session leader of a session that + # is attached to a terminal device, SIGHUP is sent to all processes + # in the foreground process group of that terminal device. + # 2) When the death of a process causes a process group to become + # orphaned, and one or more processes in the orphaned group are + # stopped, then SIGHUP and SIGCONT are sent to all members of the + # orphaned group." [2] + # + # The first case can be ignored since the child is guaranteed not to have + # a controlling terminal. The second case isn't so easy to dismiss. + # The process group is orphaned when the first child terminates and + # POSIX.1 requires that every STOPPED process in an orphaned process + # group be sent a SIGHUP signal followed by a SIGCONT signal. Since the + # second child is not STOPPED though, we can safely forego ignoring the + # SIGHUP signal. In any case, there are no ill-effects if it is ignored. + # + # import signal # Set handlers for asynchronous events. + # signal.signal(signal.SIGHUP, signal.SIG_IGN) + + try: + # Fork a second child and exit immediately to prevent zombies. This + # causes the second child process to be orphaned, making the init + # process responsible for its cleanup. And, since the first child is + # a session leader without a controlling terminal, it's possible for + # it to acquire one by opening a terminal in the future (System V- + # based systems). This second fork guarantees that the child is no + # longer a session leader, preventing the daemon from ever acquiring + # a controlling terminal. + pid = os.fork() # Fork a second child. + except OSError, e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if (pid == 0): # The second child. + # We probably don't want the file mode creation mask inherited from + # the parent, so we give the child complete control over permissions. + if UMASK is not None: + os.umask(UMASK) + else: + # Parent (the first child) of the second child. + os._exit(0) + else: + # exit() or _exit()? + # _exit is like exit(), but it doesn't call any functions registered + # with atexit (and on_exit) or any registered signal handlers. It also + # closes any open file descriptors. Using exit() may cause all stdio + # streams to be flushed twice and any temporary files may be unexpectedly + # removed. It's therefore recommended that child branches of a fork() + # and the parent branch(es) of a daemon use _exit(). + return + + # Close all open file descriptors. This prevents the child from keeping + # open any file descriptors inherited from the parent. There is a variety + # of methods to accomplish this task. Three are listed below. + # + # Try the system configuration variable, SC_OPEN_MAX, to obtain the maximum + # number of open file descriptors to close. If it doesn't exists, use + # the default value (configurable). + # + # try: + # maxfd = os.sysconf("SC_OPEN_MAX") + # except (AttributeError, ValueError): + # maxfd = MAXFD + # + # OR + # + # if (os.sysconf_names.has_key("SC_OPEN_MAX")): + # maxfd = os.sysconf("SC_OPEN_MAX") + # else: + # maxfd = MAXFD + # + # OR + # + # Use the getrlimit method to retrieve the maximum file descriptor number + # that can be opened by this process. If there is not limit on the + # resource, use the default value. + # + import resource # Resource usage information. + maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] + if (maxfd == resource.RLIM_INFINITY): + maxfd = MAXFD + + # Iterate through and close all file descriptors. +# for fd in range(0, maxfd): +# try: +# os.close(fd) +# except OSError: # ERROR, fd wasn't open to begin with (ignored) +# pass + + # Redirect the standard I/O file descriptors to the specified file. Since + # the daemon has no controlling terminal, most daemons redirect stdin, + # stdout, and stderr to /dev/null. This is done to prevent side-effects + # from reads and writes to the standard I/O file descriptors. + + # This call to open is guaranteed to return the lowest file descriptor, + # which will be 0 (stdin), since it was closed above. +# os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) + + # Duplicate standard input to standard output and standard error. +# os.dup2(0, 1) # standard output (1) +# os.dup2(0, 2) # standard error (2) + + + si = file('/dev/null', 'r') + so = file(logfile, 'w') + se = so + + + # Replace those fds with our own + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + function() + + os._exit(0) + diff --git a/bitbake/lib/bb/data.py b/bitbake/lib/bb/data.py index f424ac7a2..d3058b9a1 100644 --- a/bitbake/lib/bb/data.py +++ b/bitbake/lib/bb/data.py @@ -37,7 +37,7 @@ the speed is more critical here. # #Based on functions from the base bb module, Copyright 2003 Holger Schurig -import sys, os, re, time, types +import sys, os, re, types if sys.argv[0][-5:] == "pydoc": path = os.path.dirname(os.path.dirname(sys.argv[1])) else: diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py index 9d7341f87..7251d7871 100644 --- a/bitbake/lib/bb/event.py +++ b/bitbake/lib/bb/event.py @@ -24,21 +24,18 @@ BitBake build tools. import os, re import bb.utils +import pickle + +# This is the pid for which we should generate the event. This is set when +# the runqueue forks off. +worker_pid = 0 +worker_pipe = None class Event: """Base class for events""" - type = "Event" - - def __init__(self, d): - self._data = d - - def getData(self): - return self._data - - def setData(self, data): - self._data = data - data = property(getData, setData, None, "data property") + def __init__(self): + self.pid = worker_pid NotHandled = 0 Handled = 1 @@ -47,75 +44,83 @@ Registered = 10 AlreadyRegistered = 14 # Internal -_handlers = [] -_handlers_dict = {} +_handlers = {} +_ui_handlers = {} +_ui_handler_seq = 0 -def tmpHandler(event): - """Default handler for code events""" - return NotHandled +def fire(event, d): + """Fire off an Event""" -def defaultTmpHandler(): - tmp = "def tmpHandler(e):\n\t\"\"\"heh\"\"\"\n\treturn NotHandled" - comp = bb.utils.better_compile(tmp, "tmpHandler(e)", "bb.event.defaultTmpHandler") - return comp + if worker_pid != 0: + worker_fire(event, d) + return -def fire(event): - """Fire off an Event""" - for h in _handlers: + for handler in _handlers: + h = _handlers[handler] + event.data = d if type(h).__name__ == "code": exec(h) - if tmpHandler(event) == Handled: - return Handled + tmpHandler(event) else: - if h(event) == Handled: - return Handled - return NotHandled + h(event) + del event.data + + errors = [] + for h in _ui_handlers: + #print "Sending event %s" % event + try: + # We use pickle here since it better handles object instances + # which xmlrpc's marshaller does not. Events *must* be serializable + # by pickle. + _ui_handlers[h].event.send((pickle.dumps(event))) + except: + errors.append(h) + for h in errors: + del _ui_handlers[h] + +def worker_fire(event, d): + data = "" + pickle.dumps(event) + "" + if os.write(worker_pipe, data) != len (data): + print "Error sending event to server (short write)" + +def fire_from_worker(event, d): + if not event.startswith("") or not event.endswith(""): + print "Error, not an event" + return + event = pickle.loads(event[7:-8]) + bb.event.fire(event, d) def register(name, handler): """Register an Event handler""" # already registered - if name in _handlers_dict: + if name in _handlers: return AlreadyRegistered if handler is not None: -# handle string containing python code + # handle string containing python code if type(handler).__name__ == "str": - _registerCode(handler) + tmp = "def tmpHandler(e):\n%s" % handler + comp = bb.utils.better_compile(tmp, "tmpHandler(e)", "bb.event._registerCode") + _handlers[name] = comp else: - _handlers.append(handler) + _handlers[name] = handler - _handlers_dict[name] = 1 return Registered -def _registerCode(handlerStr): - """Register a 'code' Event. - Deprecated interface; call register instead. - - Expects to be passed python code as a string, which will - be passed in turn to compile() and then exec(). Note that - the code will be within a function, so should have had - appropriate tabbing put in place.""" - tmp = "def tmpHandler(e):\n%s" % handlerStr - comp = bb.utils.better_compile(tmp, "tmpHandler(e)", "bb.event._registerCode") -# prevent duplicate registration - _handlers.append(comp) - def remove(name, handler): """Remove an Event handler""" + _handlers.pop(name) - _handlers_dict.pop(name) - if type(handler).__name__ == "str": - return _removeCode(handler) - else: - _handlers.remove(handler) +def register_UIHhandler(handler): + bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 + _ui_handlers[_ui_handler_seq] = handler + return _ui_handler_seq -def _removeCode(handlerStr): - """Remove a 'code' Event handler - Deprecated interface; call remove instead.""" - tmp = "def tmpHandler(e):\n%s" % handlerStr - comp = bb.utils.better_compile(tmp, "tmpHandler(e)", "bb.event._removeCode") - _handlers.remove(comp) +def unregister_UIHhandler(handlerNum): + if handlerNum in _ui_handlers: + del _ui_handlers[handlerNum] + return def getName(e): """Returns the name of a class or class instance""" @@ -130,17 +135,17 @@ class ConfigParsed(Event): class RecipeParsed(Event): """ Recipe Parsing Complete """ - def __init__(self, fn, d): + def __init__(self, fn): self.fn = fn - Event.__init__(self, d) + Event.__init__(self) class StampUpdate(Event): """Trigger for any adjustment of the stamp files to happen""" - def __init__(self, targets, stampfns, d): + def __init__(self, targets, stampfns): self._targets = targets self._stampfns = stampfns - Event.__init__(self, d) + Event.__init__(self) def getStampPrefix(self): return self._stampfns @@ -151,29 +156,13 @@ class StampUpdate(Event): stampPrefix = property(getStampPrefix) targets = property(getTargets) -class PkgBase(Event): - """Base class for package events""" - - def __init__(self, t, d): - self._pkg = t - Event.__init__(self, d) - - def getPkg(self): - return self._pkg - - def setPkg(self, pkg): - self._pkg = pkg - - pkg = property(getPkg, setPkg, None, "pkg property") - - class BuildBase(Event): """Base class for bbmake run events""" - def __init__(self, n, p, c, failures = 0): + def __init__(self, n, p, failures = 0): self._name = n self._pkgs = p - Event.__init__(self, c) + Event.__init__(self) self._failures = failures def getPkgs(self): @@ -205,33 +194,8 @@ class BuildBase(Event): cfg = property(getCfg, setCfg, None, "cfg property") -class DepBase(PkgBase): - """Base class for dependency events""" - - def __init__(self, t, data, d): - self._dep = d - PkgBase.__init__(self, t, data) - - def getDep(self): - return self._dep - - def setDep(self, dep): - self._dep = dep - - dep = property(getDep, setDep, None, "dep property") - - -class PkgStarted(PkgBase): - """Package build started""" -class PkgFailed(PkgBase): - """Package build failed""" - - -class PkgSucceeded(PkgBase): - """Package build completed""" - class BuildStarted(BuildBase): """bbmake build run started""" @@ -241,18 +205,13 @@ class BuildCompleted(BuildBase): """bbmake build run completed""" -class UnsatisfiedDep(DepBase): - """Unsatisfied Dependency""" -class RecursiveDep(DepBase): - """Recursive Dependency""" - class NoProvider(Event): """No Provider for an Event""" - def __init__(self, item, data,runtime=False): - Event.__init__(self, data) + def __init__(self, item, runtime=False): + Event.__init__(self) self._item = item self._runtime = runtime @@ -265,8 +224,8 @@ class NoProvider(Event): class MultipleProviders(Event): """Multiple Providers""" - def __init__(self, item, candidates, data, runtime = False): - Event.__init__(self, data) + def __init__(self, item, candidates, runtime = False): + Event.__init__(self) self._item = item self._candidates = candidates self._is_runtime = runtime @@ -288,3 +247,29 @@ class MultipleProviders(Event): Get the possible Candidates for a PROVIDER. """ return self._candidates + +class ParseProgress(Event): + """ + Parsing Progress Event + """ + + def __init__(self, cached, parsed, skipped, masked, virtuals, errors, total): + Event.__init__(self) + self.cached = cached + self.parsed = parsed + self.skipped = skipped + self.virtuals = virtuals + self.masked = masked + self.errors = errors + self.sofar = cached + parsed + self.total = total + +class DepTreeGenerated(Event): + """ + Event when a dependency tree has been generated + """ + + def __init__(self, depgraph): + Event.__init__(self) + self._depgraph = depgraph + diff --git a/bitbake/lib/bb/fetch/__init__.py b/bitbake/lib/bb/fetch/__init__.py index 7326ed0f4..ab4658bc3 100644 --- a/bitbake/lib/bb/fetch/__init__.py +++ b/bitbake/lib/bb/fetch/__init__.py @@ -99,6 +99,11 @@ def fetcher_init(d): pd.delDomain("BB_URI_HEADREVS") else: bb.msg.fatal(bb.msg.domain.Fetcher, "Invalid SRCREV cache policy of: %s" % srcrev_policy) + + for m in methods: + if hasattr(m, "init"): + m.init(d) + # Make sure our domains exist pd.addDomain("BB_URI_HEADREVS") pd.addDomain("BB_URI_LOCALCOUNT") @@ -467,6 +472,23 @@ class Fetch(object): srcrev_internal_helper = staticmethod(srcrev_internal_helper) + def localcount_internal_helper(ud, d): + """ + Return: + a) a locked localcount if specified + b) None otherwise + """ + + localcount= None + if 'name' in ud.parm: + pn = data.getVar("PN", d, 1) + localcount = data.getVar("LOCALCOUNT_" + ud.parm['name'], d, 1) + if not localcount: + localcount = data.getVar("LOCALCOUNT", d, 1) + return localcount + + localcount_internal_helper = staticmethod(localcount_internal_helper) + def try_mirror(d, tarfn): """ Try to use a mirrored version of the sources. We do this @@ -555,12 +577,7 @@ class Fetch(object): """ """ - has_sortable_valid = hasattr(self, "_sortable_revision_valid") - has_sortable = hasattr(self, "_sortable_revision") - - if has_sortable and not has_sortable_valid: - return self._sortable_revision(url, ud, d) - elif has_sortable and self._sortable_revision_valid(url, ud, d): + if hasattr(self, "_sortable_revision"): return self._sortable_revision(url, ud, d) pd = persist_data.PersistData(d) @@ -568,13 +585,24 @@ class Fetch(object): latest_rev = self._build_revision(url, ud, d) last_rev = pd.getValue("BB_URI_LOCALCOUNT", key + "_rev") - count = pd.getValue("BB_URI_LOCALCOUNT", key + "_count") + uselocalcount = bb.data.getVar("BB_LOCALCOUNT_OVERRIDE", d, True) or False + count = None + if uselocalcount: + count = Fetch.localcount_internal_helper(ud, d) + if count is None: + count = pd.getValue("BB_URI_LOCALCOUNT", key + "_count") if last_rev == latest_rev: return str(count + "+" + latest_rev) + buildindex_provided = hasattr(self, "_sortable_buildindex") + if buildindex_provided: + count = self._sortable_buildindex(url, ud, d, latest_rev) + if count is None: count = "0" + elif uselocalcount or buildindex_provided: + count = str(count) else: count = str(int(count) + 1) diff --git a/bitbake/lib/bb/fetch/cvs.py b/bitbake/lib/bb/fetch/cvs.py index d8bd4eaf7..90a006500 100644 --- a/bitbake/lib/bb/fetch/cvs.py +++ b/bitbake/lib/bb/fetch/cvs.py @@ -41,7 +41,7 @@ class Cvs(Fetch): """ Check to see if a given url can be fetched with cvs. """ - return ud.type in ['cvs', 'pserver'] + return ud.type in ['cvs'] def localpath(self, url, ud, d): if not "module" in ud.parm: diff --git a/bitbake/lib/bb/fetch/git.py b/bitbake/lib/bb/fetch/git.py index 3016f0f00..0e68325db 100644 --- a/bitbake/lib/bb/fetch/git.py +++ b/bitbake/lib/bb/fetch/git.py @@ -28,6 +28,12 @@ from bb.fetch import runfetchcmd class Git(Fetch): """Class to fetch a module or modules from git repositories""" + def init(self, d): + # + # Only enable _sortable revision if the key is set + # + if bb.data.getVar("BB_GIT_CLONE_FOR_SRCREV", d, True): + self._sortable_buildindex = self._sortable_buildindex_disabled def supports(self, url, ud, d): """ Check to see if a given url can be fetched with git. @@ -58,10 +64,18 @@ class Git(Fetch): if not ud.tag or ud.tag == "master": ud.tag = self.latest_revision(url, ud, d) + subdir = ud.parm.get("subpath", "") + if subdir != "": + if subdir.endswith("/"): + subdir = subdir[:-1] + subdirpath = os.path.join(ud.path, subdir); + else: + subdirpath = ud.path; + if 'fullclone' in ud.parm: ud.localfile = ud.mirrortarball else: - ud.localfile = data.expand('git_%s%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.tag), d) + ud.localfile = data.expand('git_%s%s_%s.tar.gz' % (ud.host, subdirpath.replace('/', '.'), ud.tag), d) return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile) @@ -111,10 +125,27 @@ class Git(Fetch): if os.path.exists(codir): bb.utils.prunedir(codir) + subdir = ud.parm.get("subpath", "") + if subdir != "": + if subdir.endswith("/"): + subdirbase = os.path.basename(subdir[:-1]) + else: + subdirbase = os.path.basename(subdir) + else: + subdirbase = "" + + if subdir != "": + readpathspec = ":%s" % (subdir) + codir = os.path.join(codir, "git") + coprefix = os.path.join(codir, subdirbase, "") + else: + readpathspec = "" + coprefix = os.path.join(codir, "git", "") + bb.mkdirhier(codir) os.chdir(ud.clonedir) - runfetchcmd("git read-tree %s" % (ud.tag), d) - runfetchcmd("git checkout-index -q -f --prefix=%s -a" % (os.path.join(codir, "git", "")), d) + runfetchcmd("git read-tree %s%s" % (ud.tag, readpathspec), d) + runfetchcmd("git checkout-index -q -f --prefix=%s -a" % (coprefix), d) os.chdir(codir) bb.msg.note(1, bb.msg.domain.Fetcher, "Creating tarball of git checkout") @@ -154,42 +185,32 @@ class Git(Fetch): def _build_revision(self, url, ud, d): return ud.tag - def _sortable_revision_valid(self, url, ud, d): - return bb.data.getVar("BB_GIT_CLONE_FOR_SRCREV", d, True) or False - - def _sortable_revision(self, url, ud, d): + def _sortable_buildindex_disabled(self, url, ud, d, rev): """ - This is only called when _sortable_revision_valid called true - - We will have to get the updated revision. + Return a suitable buildindex for the revision specified. This is done by counting revisions + using "git rev-list" which may or may not work in different circumstances. """ - key = "GIT_CACHED_REVISION-%s-%s" % (gitsrcname, ud.tag) - if bb.data.getVar(key, d): - return bb.data.getVar(key, d) - - - # Runtime warning on wrongly configured sources - if ud.tag == "1": - bb.msg.error(1, bb.msg.domain.Fetcher, "SRCREV is '1'. This indicates a configuration error of %s" % url) - return "0+1" - cwd = os.getcwd() # Check if we have the rev already + if not os.path.exists(ud.clonedir): print "no repo" self.go(None, ud, d) + if not os.path.exists(ud.clonedir): + bb.msg.error(bb.msg.domain.Fetcher, "GIT repository for %s doesn't exist in %s, cannot get sortable buildnumber, using old value" % (url, ud.clonedir)) + return None + os.chdir(ud.clonedir) - if not self._contains_ref(ud.tag, d): + if not self._contains_ref(rev, d): self.go(None, ud, d) - output = runfetchcmd("git rev-list %s -- 2> /dev/null | wc -l" % ud.tag, d, quiet=True) + output = runfetchcmd("git rev-list %s -- 2> /dev/null | wc -l" % rev, d, quiet=True) os.chdir(cwd) - sortable_revision = "%s+%s" % (output.split()[0], ud.tag) - bb.data.setVar(key, sortable_revision, d) - return sortable_revision - + buildindex = "%s" % output.split()[0] + bb.msg.debug(1, bb.msg.domain.Fetcher, "GIT repository for %s in %s is returning %s revisions in rev-list before %s" % (url, repodir, buildindex, rev)) + return buildindex diff --git a/bitbake/lib/bb/fetch/local.py b/bitbake/lib/bb/fetch/local.py index 577774e59..f9bdf589c 100644 --- a/bitbake/lib/bb/fetch/local.py +++ b/bitbake/lib/bb/fetch/local.py @@ -33,9 +33,9 @@ from bb.fetch import Fetch class Local(Fetch): def supports(self, url, urldata, d): """ - Check to see if a given url can be fetched with cvs. + Check to see if a given url represents a local fetch. """ - return urldata.type in ['file','patch'] + return urldata.type in ['file'] def localpath(self, url, urldata, d): """ diff --git a/bitbake/lib/bb/fetch/svk.py b/bitbake/lib/bb/fetch/svk.py index 442f85804..120dad9d4 100644 --- a/bitbake/lib/bb/fetch/svk.py +++ b/bitbake/lib/bb/fetch/svk.py @@ -36,7 +36,7 @@ class Svk(Fetch): """Class to fetch a module or modules from svk repositories""" def supports(self, url, ud, d): """ - Check to see if a given url can be fetched with cvs. + Check to see if a given url can be fetched with svk. """ return ud.type in ['svk'] diff --git a/bitbake/lib/bb/fetch/wget.py b/bitbake/lib/bb/fetch/wget.py index a0dca9404..fd93c7ec4 100644 --- a/bitbake/lib/bb/fetch/wget.py +++ b/bitbake/lib/bb/fetch/wget.py @@ -36,7 +36,7 @@ class Wget(Fetch): """Class to fetch urls via 'wget'""" def supports(self, url, ud, d): """ - Check to see if a given url can be fetched with cvs. + Check to see if a given url can be fetched with wget. """ return ud.type in ['http','https','ftp'] diff --git a/bitbake/lib/bb/msg.py b/bitbake/lib/bb/msg.py index a1b31e5d6..3fcf7091b 100644 --- a/bitbake/lib/bb/msg.py +++ b/bitbake/lib/bb/msg.py @@ -22,8 +22,8 @@ Message handling infrastructure for bitbake # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import sys, os, re, bb -from bb import utils, event +import sys, bb +from bb import event debug_level = {} @@ -47,9 +47,9 @@ domain = bb.utils.Enum( class MsgBase(bb.event.Event): """Base class for messages""" - def __init__(self, msg, d ): + def __init__(self, msg): self._message = msg - event.Event.__init__(self, d) + event.Event.__init__(self) class MsgDebug(MsgBase): """Debug Message""" @@ -97,33 +97,29 @@ def set_debug_domains(domains): # def debug(level, domain, msg, fn = None): - bb.event.fire(MsgDebug(msg, None)) if not domain: domain = 'default' if debug_level[domain] >= level: - print 'DEBUG: ' + msg + bb.event.fire(MsgDebug(msg), None) def note(level, domain, msg, fn = None): - bb.event.fire(MsgNote(msg, None)) if not domain: domain = 'default' if level == 1 or verbose or debug_level[domain] >= 1: - print 'NOTE: ' + msg + bb.event.fire(MsgNote(msg), None) def warn(domain, msg, fn = None): - bb.event.fire(MsgWarn(msg, None)) - print 'WARNING: ' + msg + bb.event.fire(MsgWarn(msg), None) def error(domain, msg, fn = None): - bb.event.fire(MsgError(msg, None)) + bb.event.fire(MsgError(msg), None) print 'ERROR: ' + msg def fatal(domain, msg, fn = None): - bb.event.fire(MsgFatal(msg, None)) - print 'ERROR: ' + msg + bb.event.fire(MsgFatal(msg), None) + print 'FATAL: ' + msg sys.exit(1) def plain(msg, fn = None): - bb.event.fire(MsgPlain(msg, None)) - print msg + bb.event.fire(MsgPlain(msg), None) diff --git a/bitbake/lib/bb/parse/parse_py/BBHandler.py b/bitbake/lib/bb/parse/parse_py/BBHandler.py index 915db214f..86fa18ebd 100644 --- a/bitbake/lib/bb/parse/parse_py/BBHandler.py +++ b/bitbake/lib/bb/parse/parse_py/BBHandler.py @@ -94,7 +94,7 @@ def finalise(fn, d): for f in anonfuncs: code = code + " %s(d)\n" % f data.setVar("__anonfunc", code, d) - build.exec_func_python("__anonfunc", d) + build.exec_func("__anonfunc", d) data.delVar('T', d) if t: data.setVar('T', t, d) @@ -114,7 +114,7 @@ def finalise(fn, d): tasklist = data.getVar('__BBTASKS', d) or [] bb.build.add_tasks(tasklist, d) - bb.event.fire(bb.event.RecipeParsed(fn, d)) + bb.event.fire(bb.event.RecipeParsed(fn), d) def handle(fn, d, include = 0): @@ -185,18 +185,26 @@ def handle(fn, d, include = 0): multi = data.getVar('BBCLASSEXTEND', d, 1) if multi: based = bb.data.createCopy(d) + else: + based = d + try: finalise(fn, based) - darray = {"": based} - for cls in multi.split(): - pn = data.getVar('PN', d, True) - based = bb.data.createCopy(d) - data.setVar('PN', pn + '-' + cls, based) - inherit([cls], based) + except bb.parse.SkipPackage: + bb.data.setVar("__SKIPPED", True, based) + darray = {"": based} + + for cls in (multi or "").split(): + pn = data.getVar('PN', d, True) + based = bb.data.createCopy(d) + data.setVar('PN', pn + '-' + cls, based) + inherit([cls], based) + try: finalise(fn, based) - darray[cls] = based - return darray - else: - finalise(fn, d) + except bb.parse.SkipPackage: + bb.data.setVar("__SKIPPED", True, based) + darray[cls] = based + return darray + bbpath.pop(0) if oldfile: bb.data.setVar("FILE", oldfile, d) diff --git a/bitbake/lib/bb/parse/parse_py/ConfHandler.py b/bitbake/lib/bb/parse/parse_py/ConfHandler.py index c9f1ea13f..23316ada5 100644 --- a/bitbake/lib/bb/parse/parse_py/ConfHandler.py +++ b/bitbake/lib/bb/parse/parse_py/ConfHandler.py @@ -34,10 +34,17 @@ __require_regexp__ = re.compile( r"require\s+(.+)" ) __export_regexp__ = re.compile( r"export\s+(.+)" ) def init(data): - if not bb.data.getVar('TOPDIR', data): - bb.data.setVar('TOPDIR', os.getcwd(), data) + topdir = bb.data.getVar('TOPDIR', data) + if not topdir: + topdir = os.getcwd() + bb.data.setVar('TOPDIR', topdir, data) if not bb.data.getVar('BBPATH', data): - bb.data.setVar('BBPATH', os.path.join(sys.prefix, 'share', 'bitbake'), data) + from pkg_resources import Requirement, resource_filename + bitbake = Requirement.parse("bitbake") + datadir = resource_filename(bitbake, "../share/bitbake") + basedir = resource_filename(bitbake, "..") + bb.data.setVar('BBPATH', '%s:%s:%s' % (topdir, datadir, basedir), data) + def supports(fn, d): return localpath(fn, d)[-5:] == ".conf" diff --git a/bitbake/lib/bb/providers.py b/bitbake/lib/bb/providers.py index 001281a29..8617251ca 100644 --- a/bitbake/lib/bb/providers.py +++ b/bitbake/lib/bb/providers.py @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import os, re +import re from bb import data, utils import bb @@ -203,7 +203,7 @@ def _filterProviders(providers, item, cfgData, dataCache): eligible.append(preferred_versions[pn][1]) # Now add latest verisons - for pn in pkg_pn.keys(): + for pn in sortpkg_pn.keys(): if pn in preferred_versions and preferred_versions[pn][1]: continue preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0]) diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py index cce5da405..c3ad442e4 100644 --- a/bitbake/lib/bb/runqueue.py +++ b/bitbake/lib/bb/runqueue.py @@ -37,20 +37,38 @@ class RunQueueStats: """ Holds statistics on the tasks handled by the associated runQueue """ - def __init__(self): + def __init__(self, total): self.completed = 0 self.skipped = 0 self.failed = 0 + self.active = 0 + self.total = total def taskFailed(self): + self.active = self.active - 1 self.failed = self.failed + 1 def taskCompleted(self, number = 1): + self.active = self.active - number self.completed = self.completed + number def taskSkipped(self, number = 1): + self.active = self.active + number self.skipped = self.skipped + number + def taskActive(self): + self.active = self.active + 1 + +# These values indicate the next step due to be run in the +# runQueue state machine +runQueuePrepare = 2 +runQueueRunInit = 3 +runQueueRunning = 4 +runQueueFailed = 6 +runQueueCleanUp = 7 +runQueueComplete = 8 +runQueueChildProcess = 9 + class RunQueueScheduler: """ Control the order tasks are scheduled in. @@ -142,9 +160,9 @@ class RunQueue: self.cooker = cooker self.dataCache = dataCache self.taskData = taskData + self.cfgData = cfgData self.targets = targets - self.cfgdata = cfgData self.number_tasks = int(bb.data.getVar("BB_NUMBER_THREADS", cfgData, 1) or 1) self.multi_provider_whitelist = (bb.data.getVar("MULTI_PROVIDER_WHITELIST", cfgData, 1) or "").split() self.scheduler = bb.data.getVar("BB_SCHEDULER", cfgData, 1) or "speed" @@ -152,12 +170,13 @@ class RunQueue: self.stampwhitelist = bb.data.getVar("BB_STAMP_WHITELIST", cfgData, 1) or "" def reset_runqueue(self): - self.runq_fnid = [] self.runq_task = [] self.runq_depends = [] self.runq_revdeps = [] + self.state = runQueuePrepare + def get_user_idstring(self, task): fn = self.taskData.fn_index[self.runq_fnid[task]] taskname = self.runq_task[task] @@ -653,6 +672,8 @@ class RunQueue: #self.dump_data(taskData) + self.state = runQueueRunInit + def check_stamps(self): unchecked = {} current = [] @@ -796,39 +817,51 @@ class RunQueue: (if the abort on failure configuration option isn't set) """ - failures = 0 - while 1: - failed_fnids = [] - try: - self.execute_runqueue_internal() - finally: - if self.master_process: - failed_fnids = self.finish_runqueue() - if len(failed_fnids) == 0: - return failures + if self.state is runQueuePrepare: + self.prepare_runqueue() + + if self.state is runQueueRunInit: + bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue") + self.execute_runqueue_initVars() + + if self.state is runQueueRunning: + self.execute_runqueue_internal() + + if self.state is runQueueCleanUp: + self.finish_runqueue() + + if self.state is runQueueFailed: if not self.taskData.tryaltconfigs: - raise bb.runqueue.TaskFailure(failed_fnids) - for fnid in failed_fnids: - #print "Failure: %s %s %s" % (fnid, self.taskData.fn_index[fnid], self.runq_task[fnid]) + raise bb.runqueue.TaskFailure(self.failed_fnids) + for fnid in self.failed_fnids: self.taskData.fail_fnid(fnid) - failures = failures + 1 self.reset_runqueue() - self.prepare_runqueue() + + if self.state is runQueueComplete: + # All done + bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed)) + return False + + if self.state is runQueueChildProcess: + print "Child process" + return False + + # Loop + return True def execute_runqueue_initVars(self): - self.stats = RunQueueStats() + self.stats = RunQueueStats(len(self.runq_fnid)) - self.active_builds = 0 self.runq_buildable = [] self.runq_running = [] self.runq_complete = [] self.build_pids = {} + self.build_pipes = {} self.failed_fnids = [] - self.master_process = True # Mark initial buildable tasks - for task in range(len(self.runq_fnid)): + for task in range(self.stats.total): self.runq_running.append(0) self.runq_complete.append(0) if len(self.runq_depends[task]) == 0: @@ -836,6 +869,10 @@ class RunQueue: else: self.runq_buildable.append(0) + self.state = runQueueRunning + + event.fire(bb.event.StampUpdate(self.target_pairs, self.dataCache.stamp), self.cfgData) + def task_complete(self, task): """ Mark a task as completed @@ -858,26 +895,32 @@ class RunQueue: taskname = self.runq_task[revdep] bb.msg.debug(1, bb.msg.domain.RunQueue, "Marking task %s (%s, %s) as buildable" % (revdep, fn, taskname)) + def task_fail(self, task, exitcode): + """ + Called when a task has failed + Updates the state engine with the failure + """ + bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed with %s" % (task, self.get_user_idstring(task), exitcode)) + self.stats.taskFailed() + fnid = self.runq_fnid[task] + self.failed_fnids.append(fnid) + bb.event.fire(runQueueTaskFailed(task, self.stats, self), self.cfgData) + if self.taskData.abort: + self.state = runQueueCleanup + def execute_runqueue_internal(self): """ Run the tasks in a queue prepared by prepare_runqueue """ - bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue") - - self.execute_runqueue_initVars() - - if len(self.runq_fnid) == 0: + if self.stats.total == 0: # nothing to do - return [] - - def sigint_handler(signum, frame): - raise KeyboardInterrupt - - event.fire(bb.event.StampUpdate(self.target_pairs, self.dataCache.stamp, self.cfgdata)) + self.state = runQueueCleanup while True: - task = self.sched.next() + task = None + if self.stats.active < self.number_tasks: + task = self.sched.next() if task is not None: fn = self.taskData.fn_index[self.runq_fnid[task]] @@ -885,107 +928,143 @@ class RunQueue: if self.check_stamp_task(task): bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task))) self.runq_running[task] = 1 + self.runq_buildable[task] = 1 self.task_complete(task) self.stats.taskCompleted() self.stats.taskSkipped() continue - bb.msg.note(1, bb.msg.domain.RunQueue, "Running task %d of %d (ID: %s, %s)" % (self.stats.completed + self.active_builds + 1, len(self.runq_fnid), task, self.get_user_idstring(task))) sys.stdout.flush() sys.stderr.flush() - try: + try: + pipein, pipeout = os.pipe() pid = os.fork() except OSError, e: bb.msg.fatal(bb.msg.domain.RunQueue, "fork failed: %d (%s)" % (e.errno, e.strerror)) if pid == 0: - # Bypass master process' handling - self.master_process = False - # Stop Ctrl+C being sent to children - # signal.signal(signal.SIGINT, signal.SIG_IGN) + os.close(pipein) + # Save out the PID so that the event can include it the + # events + bb.event.worker_pid = os.getpid() + bb.event.worker_pipe = pipeout + + self.state = runQueueChildProcess # Make the child the process group leader os.setpgid(0, 0) + # No stdin newsi = os.open('/dev/null', os.O_RDWR) os.dup2(newsi, sys.stdin.fileno()) - self.cooker.configuration.cmd = taskname[3:] + + bb.event.fire(runQueueTaskStarted(task, self.stats, self), self.cfgData) + bb.msg.note(1, bb.msg.domain.RunQueue, + "Running task %d of %d (ID: %s, %s)" % (self.stats.completed + self.stats.active + 1, + self.stats.total, + task, + self.get_user_idstring(task))) + bb.data.setVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY", self, self.cooker.configuration.data) try: - self.cooker.tryBuild(fn) + self.cooker.tryBuild(fn, taskname[3:]) except bb.build.EventException: bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed") - sys.exit(1) + os._exit(1) except: bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed") - raise - sys.exit(0) + os._exit(1) + os._exit(0) + self.build_pids[pid] = task + self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData) self.runq_running[task] = 1 - self.active_builds = self.active_builds + 1 - if self.active_builds < self.number_tasks: + self.stats.taskActive() + if self.stats.active < self.number_tasks: continue - if self.active_builds > 0: - result = os.waitpid(-1, 0) - self.active_builds = self.active_builds - 1 + + for pipe in self.build_pipes: + self.build_pipes[pipe].read() + + if self.stats.active > 0: + result = os.waitpid(-1, os.WNOHANG) + if result[0] is 0 and result[1] is 0: + return task = self.build_pids[result[0]] + del self.build_pids[result[0]] + self.build_pipes[result[0]].close() + del self.build_pipes[result[0]] if result[1] != 0: - del self.build_pids[result[0]] - bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task))) - self.failed_fnids.append(self.runq_fnid[task]) - self.stats.taskFailed() - if not self.taskData.abort: - continue - break + self.task_fail(task, result[1]) + return self.task_complete(task) self.stats.taskCompleted() - del self.build_pids[result[0]] + bb.event.fire(runQueueTaskCompleted(task, self.stats, self), self.cfgData) continue + + if len(self.failed_fnids) != 0: + self.state = runQueueFailed + return + + # Sanity Checks + for task in range(self.stats.total): + if self.runq_buildable[task] == 0: + bb.msg.error(bb.msg.domain.RunQueue, "Task %s never buildable!" % task) + if self.runq_running[task] == 0: + bb.msg.error(bb.msg.domain.RunQueue, "Task %s never ran!" % task) + if self.runq_complete[task] == 0: + bb.msg.error(bb.msg.domain.RunQueue, "Task %s never completed!" % task) + self.state = runQueueComplete return - def finish_runqueue(self): + def finish_runqueue_now(self): + bb.msg.note(1, bb.msg.domain.RunQueue, "Sending SIGINT to remaining %s tasks" % self.stats.active) + for k, v in self.build_pids.iteritems(): + try: + os.kill(-k, signal.SIGINT) + except: + pass + for pipe in self.build_pipes: + self.build_pipes[pipe].read() + + def finish_runqueue(self, now = False): + self.state = runQueueCleanUp + if now: + self.finish_runqueue_now() try: - while self.active_builds > 0: - bb.msg.note(1, bb.msg.domain.RunQueue, "Waiting for %s active tasks to finish" % self.active_builds) + while self.stats.active > 0: + bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData) + bb.msg.note(1, bb.msg.domain.RunQueue, "Waiting for %s active tasks to finish" % self.stats.active) tasknum = 1 for k, v in self.build_pids.iteritems(): - bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v), k)) - tasknum = tasknum + 1 - result = os.waitpid(-1, 0) + bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v), k)) + tasknum = tasknum + 1 + result = os.waitpid(-1, os.WNOHANG) + if result[0] is 0 and result[1] is 0: + return task = self.build_pids[result[0]] - if result[1] != 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task))) - self.failed_fnids.append(self.runq_fnid[task]) - self.stats.taskFailed() del self.build_pids[result[0]] - self.active_builds = self.active_builds - 1 - bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed)) - return self.failed_fnids - except KeyboardInterrupt: - bb.msg.note(1, bb.msg.domain.RunQueue, "Sending SIGINT to remaining %s tasks" % self.active_builds) - for k, v in self.build_pids.iteritems(): - try: - os.kill(-k, signal.SIGINT) - except: - pass + self.build_pipes[result[0]].close() + del self.build_pipes[result[0]] + if result[1] != 0: + self.task_fail(task, result[1]) + else: + self.stats.taskCompleted() + bb.event.fire(runQueueTaskCompleted(task, self.stats, self), self.cfgData) + except: + self.finish_runqueue_now() raise - # Sanity Checks - for task in range(len(self.runq_fnid)): - if self.runq_buildable[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never buildable!" % task) - if self.runq_running[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never ran!" % task) - if self.runq_complete[task] == 0: - bb.msg.error(bb.msg.domain.RunQueue, "Task %s never completed!" % task) - - bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed)) + if len(self.failed_fnids) != 0: + self.state = runQueueFailed + return - return self.failed_fnids + self.state = runQueueComplete + return def dump_data(self, taskQueue): """ Dump some debug information on the internal data structures """ bb.msg.debug(3, bb.msg.domain.RunQueue, "run_tasks:") - for task in range(len(self.runq_fnid)): + for task in range(len(self.runq_task)): bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s Deps %s RevDeps %s" % (task, taskQueue.fn_index[self.runq_fnid[task]], self.runq_task[task], @@ -994,7 +1073,7 @@ class RunQueue: self.runq_revdeps[task])) bb.msg.debug(3, bb.msg.domain.RunQueue, "sorted_tasks:") - for task1 in range(len(self.runq_fnid)): + for task1 in range(len(self.runq_task)): if task1 in self.prio_map: task = self.prio_map[task1] bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s Deps %s RevDeps %s" % (task, @@ -1005,6 +1084,58 @@ class RunQueue: self.runq_revdeps[task])) +class TaskFailure(Exception): + """ + Exception raised when a task in a runqueue fails + """ + def __init__(self, x): + self.args = x + + +class runQueueExitWait(bb.event.Event): + """ + Event when waiting for task processes to exit + """ + + def __init__(self, remain): + self.remain = remain + self.message = "Waiting for %s active tasks to finish" % remain + bb.event.Event.__init__(self) + +class runQueueEvent(bb.event.Event): + """ + Base runQueue event class + """ + def __init__(self, task, stats, rq): + self.taskid = task + self.taskstring = rq.get_user_idstring(task) + self.stats = stats + bb.event.Event.__init__(self) + +class runQueueTaskStarted(runQueueEvent): + """ + Event notifing a task was started + """ + def __init__(self, task, stats, rq): + runQueueEvent.__init__(self, task, stats, rq) + self.message = "Running task %s (%d of %d) (%s)" % (task, stats.completed + stats.active + 1, self.stats.total, self.taskstring) + +class runQueueTaskFailed(runQueueEvent): + """ + Event notifing a task failed + """ + def __init__(self, task, stats, rq): + runQueueEvent.__init__(self, task, stats, rq) + self.message = "Task %s failed (%s)" % (task, self.taskstring) + +class runQueueTaskCompleted(runQueueEvent): + """ + Event notifing a task completed + """ + def __init__(self, task, stats, rq): + runQueueEvent.__init__(self, task, stats, rq) + self.message = "Task %s completed (%s)" % (task, self.taskstring) + def check_stamp_fn(fn, taskname, d): rq = bb.data.getVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY", d) fnid = rq.taskData.getfn_id(fn) @@ -1013,3 +1144,31 @@ def check_stamp_fn(fn, taskname, d): return rq.check_stamp_task(taskid) return None +class runQueuePipe(): + """ + Abstraction for a pipe between a worker thread and the server + """ + def __init__(self, pipein, pipeout, d): + self.fd = pipein + os.close(pipeout) + self.queue = "" + self.d = d + + def read(self): + start = len(self.queue) + self.queue = self.queue + os.read(self.fd, 1024) + end = len(self.queue) + index = self.queue.find("") + while index != -1: + bb.event.fire_from_worker(self.queue[:index+8], self.d) + self.queue = self.queue[index+8:] + index = self.queue.find("") + return (end > start) + + def close(self): + while self.read(): + continue + if len(self.queue) > 0: + print "Warning, worker left partial message" + os.close(self.fd) + diff --git a/bitbake/lib/bb/server/__init__.py b/bitbake/lib/bb/server/__init__.py new file mode 100644 index 000000000..1a732236e --- /dev/null +++ b/bitbake/lib/bb/server/__init__.py @@ -0,0 +1,2 @@ +import xmlrpc +import none diff --git a/bitbake/lib/bb/server/none.py b/bitbake/lib/bb/server/none.py new file mode 100644 index 000000000..ebda11158 --- /dev/null +++ b/bitbake/lib/bb/server/none.py @@ -0,0 +1,181 @@ +# +# BitBake 'dummy' Passthrough Server +# +# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer +# Copyright (C) 2006 - 2008 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" + This module implements an xmlrpc server for BitBake. + + Use this by deriving a class from BitBakeXMLRPCServer and then adding + methods which you want to "export" via XMLRPC. If the methods have the + prefix xmlrpc_, then registering those function will happen automatically, + if not, you need to call register_function. + + Use register_idle_function() to add a function which the xmlrpc server + calls from within server_forever when no requests are pending. Make sure + that those functions are non-blocking or else you will introduce latency + in the server's main loop. +""" + +import time +import bb +from bb.ui import uievent +import xmlrpclib +import pickle + +DEBUG = False + +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler +import inspect, select + +class BitBakeServerCommands(): + def __init__(self, server, cooker): + self.cooker = cooker + self.server = server + + def runCommand(self, command): + """ + Run a cooker command on the server + """ + #print "Running Command %s" % command + return self.cooker.command.runCommand(command) + + def terminateServer(self): + """ + Trigger the server to quit + """ + self.server.server_exit() + #print "Server (cooker) exitting" + return + + def ping(self): + """ + Dummy method which can be used to check the server is still alive + """ + return True + +eventQueue = [] + +class BBUIEventQueue: + class event: + def __init__(self, parent): + self.parent = parent + @staticmethod + def send(event): + bb.server.none.eventQueue.append(pickle.loads(event)) + @staticmethod + def quit(): + return + + def __init__(self, BBServer): + self.eventQueue = bb.server.none.eventQueue + self.BBServer = BBServer + self.EventHandle = bb.event.register_UIHhandler(self) + + def getEvent(self): + if len(self.eventQueue) == 0: + return None + + return self.eventQueue.pop(0) + + def waitEvent(self, delay): + event = self.getEvent() + if event: + return event + self.BBServer.idle_commands(delay) + return self.getEvent() + + def queue_event(self, event): + self.eventQueue.append(event) + + def system_quit( self ): + bb.event.unregister_UIHhandler(self.EventHandle) + +class BitBakeServer(): + # remove this when you're done with debugging + # allow_reuse_address = True + + def __init__(self, cooker): + self._idlefuns = {} + self.commands = BitBakeServerCommands(self, cooker) + + def register_idle_function(self, function, data): + """Register a function to be called while the server is idle""" + assert callable(function) + self._idlefuns[function] = data + + def idle_commands(self, delay): + #print "Idle queue length %s" % len(self._idlefuns) + #print "Idle timeout, running idle functions" + #if len(self._idlefuns) == 0: + nextsleep = delay + for function, data in self._idlefuns.items(): + try: + retval = function(self, data, False) + #print "Idle function returned %s" % (retval) + if retval is False: + del self._idlefuns[function] + elif retval is True: + nextsleep = None + elif nextsleep is None: + continue + elif retval < nextsleep: + nextsleep = retval + except SystemExit: + raise + except: + import traceback + traceback.print_exc() + pass + if nextsleep is not None: + #print "Sleeping for %s (%s)" % (nextsleep, delay) + time.sleep(nextsleep) + + def server_exit(self): + # Tell idle functions we're exiting + for function, data in self._idlefuns.items(): + try: + retval = function(self, data, True) + except: + pass + +class BitbakeServerInfo(): + def __init__(self, server): + self.server = server + self.commands = server.commands + +class BitBakeServerFork(): + def __init__(self, serverinfo, command, logfile): + serverinfo.forkCommand = command + serverinfo.logfile = logfile + +class BitBakeServerConnection(): + def __init__(self, serverinfo): + self.server = serverinfo.server + self.connection = serverinfo.commands + self.events = bb.server.none.BBUIEventQueue(self.server) + + def terminate(self): + try: + self.events.system_quit() + except: + pass + try: + self.connection.terminateServer() + except: + pass + diff --git a/bitbake/lib/bb/server/xmlrpc.py b/bitbake/lib/bb/server/xmlrpc.py new file mode 100644 index 000000000..3364918c7 --- /dev/null +++ b/bitbake/lib/bb/server/xmlrpc.py @@ -0,0 +1,187 @@ +# +# BitBake XMLRPC Server +# +# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer +# Copyright (C) 2006 - 2008 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" + This module implements an xmlrpc server for BitBake. + + Use this by deriving a class from BitBakeXMLRPCServer and then adding + methods which you want to "export" via XMLRPC. If the methods have the + prefix xmlrpc_, then registering those function will happen automatically, + if not, you need to call register_function. + + Use register_idle_function() to add a function which the xmlrpc server + calls from within server_forever when no requests are pending. Make sure + that those functions are non-blocking or else you will introduce latency + in the server's main loop. +""" + +import bb +import xmlrpclib, sys +from bb import daemonize +from bb.ui import uievent + +DEBUG = False + +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler +import inspect, select + +if sys.hexversion < 0x020600F0: + print "Sorry, python 2.6 or later is required for bitbake's XMLRPC mode" + sys.exit(1) + +class BitBakeServerCommands(): + def __init__(self, server, cooker): + self.cooker = cooker + self.server = server + + def registerEventHandler(self, host, port): + """ + Register a remote UI Event Handler + """ + s = xmlrpclib.Server("http://%s:%d" % (host, port), allow_none=True) + return bb.event.register_UIHhandler(s) + + def unregisterEventHandler(self, handlerNum): + """ + Unregister a remote UI Event Handler + """ + return bb.event.unregister_UIHhandler(handlerNum) + + def runCommand(self, command): + """ + Run a cooker command on the server + """ + return self.cooker.command.runCommand(command) + + def terminateServer(self): + """ + Trigger the server to quit + """ + self.server.quit = True + print "Server (cooker) exitting" + return + + def ping(self): + """ + Dummy method which can be used to check the server is still alive + """ + return True + +class BitBakeServer(SimpleXMLRPCServer): + # remove this when you're done with debugging + # allow_reuse_address = True + + def __init__(self, cooker, interface = ("localhost", 0)): + """ + Constructor + """ + SimpleXMLRPCServer.__init__(self, interface, + requestHandler=SimpleXMLRPCRequestHandler, + logRequests=False, allow_none=True) + self._idlefuns = {} + self.host, self.port = self.socket.getsockname() + #self.register_introspection_functions() + commands = BitBakeServerCommands(self, cooker) + self.autoregister_all_functions(commands, "") + + def autoregister_all_functions(self, context, prefix): + """ + Convenience method for registering all functions in the scope + of this class that start with a common prefix + """ + methodlist = inspect.getmembers(context, inspect.ismethod) + for name, method in methodlist: + if name.startswith(prefix): + self.register_function(method, name[len(prefix):]) + + def register_idle_function(self, function, data): + """Register a function to be called while the server is idle""" + assert callable(function) + self._idlefuns[function] = data + + def serve_forever(self): + """ + Serve Requests. Overloaded to honor a quit command + """ + self.quit = False + self.timeout = 0 # Run Idle calls for our first callback + while not self.quit: + #print "Idle queue length %s" % len(self._idlefuns) + self.handle_request() + #print "Idle timeout, running idle functions" + nextsleep = None + for function, data in self._idlefuns.items(): + try: + retval = function(self, data, False) + if retval is False: + del self._idlefuns[function] + elif retval is True: + nextsleep = 0 + elif nextsleep is 0: + continue + elif nextsleep is None: + nextsleep = retval + elif retval < nextsleep: + nextsleep = retval + except SystemExit: + raise + except: + import traceback + traceback.print_exc() + pass + if nextsleep is None and len(self._idlefuns) > 0: + nextsleep = 0 + self.timeout = nextsleep + # Tell idle functions we're exiting + for function, data in self._idlefuns.items(): + try: + retval = function(self, data, True) + except: + pass + + self.server_close() + return + +class BitbakeServerInfo(): + def __init__(self, server): + self.host = server.host + self.port = server.port + +class BitBakeServerFork(): + def __init__(self, serverinfo, command, logfile): + daemonize.createDaemon(command, logfile) + +class BitBakeServerConnection(): + def __init__(self, serverinfo): + self.connection = xmlrpclib.Server("http://%s:%s" % (serverinfo.host, serverinfo.port), allow_none=True) + self.events = uievent.BBUIEventQueue(self.connection) + + def terminate(self): + # Don't wait for server indefinitely + import socket + socket.setdefaulttimeout(2) + try: + self.events.system_quit() + except: + pass + try: + self.connection.terminateServer() + except: + pass + diff --git a/bitbake/lib/bb/shell.py b/bitbake/lib/bb/shell.py index b1ad78306..66e51719a 100644 --- a/bitbake/lib/bb/shell.py +++ b/bitbake/lib/bb/shell.py @@ -151,9 +151,6 @@ class BitBakeShellCommands: if len( names ) == 0: names = [ globexpr ] print "SHELL: Building %s" % ' '.join( names ) - oldcmd = cooker.configuration.cmd - cooker.configuration.cmd = cmd - td = taskdata.TaskData(cooker.configuration.abort) localdata = data.createCopy(cooker.configuration.data) data.update_data(localdata) @@ -168,7 +165,7 @@ class BitBakeShellCommands: if len(providers) == 0: raise Providers.NoProvider - tasks.append([name, "do_%s" % cooker.configuration.cmd]) + tasks.append([name, "do_%s" % cmd]) td.add_unresolved(localdata, cooker.status) @@ -189,7 +186,6 @@ class BitBakeShellCommands: print "ERROR: Couldn't build '%s'" % names last_exception = e - cooker.configuration.cmd = oldcmd build.usage = "" @@ -208,6 +204,11 @@ class BitBakeShellCommands: self.build( params, "configure" ) configure.usage = "" + def install( self, params ): + """Execute 'install' on a providee""" + self.build( params, "install" ) + install.usage = "" + def edit( self, params ): """Call $EDITOR on a providee""" name = params[0] @@ -240,18 +241,14 @@ class BitBakeShellCommands: bf = completeFilePath( name ) print "SHELL: Calling '%s' on '%s'" % ( cmd, bf ) - oldcmd = cooker.configuration.cmd - cooker.configuration.cmd = cmd - try: - cooker.buildFile(bf) + cooker.buildFile(bf, cmd) except parse.ParseError: print "ERROR: Unable to open or parse '%s'" % bf except build.EventException, e: print "ERROR: Couldn't build '%s'" % name last_exception = e - cooker.configuration.cmd = oldcmd fileBuild.usage = "" def fileClean( self, params ): @@ -493,7 +490,7 @@ SRC_URI = "" interpreter.interact( "SHELL: Expert Mode - BitBake Python %s\nType 'help' for more information, press CTRL-D to switch back to BBSHELL." % sys.version ) def showdata( self, params ): - """Show the parsed metadata for a given providee""" + """Execute 'showdata' on a providee""" cooker.showEnvironment(None, params) showdata.usage = "" diff --git a/bitbake/lib/bb/taskdata.py b/bitbake/lib/bb/taskdata.py index 976e0ca1f..4a88e75f6 100644 --- a/bitbake/lib/bb/taskdata.py +++ b/bitbake/lib/bb/taskdata.py @@ -23,8 +23,20 @@ Task data collection and handling # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -from bb import data, event, mkdirhier, utils -import bb, os +import bb + +def re_match_strings(target, strings): + """ + Whether or not the string 'target' matches + any one string of the strings which can be regular expression string + """ + import re + + for name in strings: + if (name==target or + re.search(name,target)!=None): + return True + return False class TaskData: """ @@ -264,7 +276,7 @@ class TaskData: """ unresolved = [] for target in self.build_names_index: - if target in dataCache.ignored_dependencies: + if re_match_strings(target, dataCache.ignored_dependencies): continue if self.build_names_index.index(target) in self.failed_deps: continue @@ -279,7 +291,7 @@ class TaskData: """ unresolved = [] for target in self.run_names_index: - if target in dataCache.ignored_dependencies: + if re_match_strings(target, dataCache.ignored_dependencies): continue if self.run_names_index.index(target) in self.failed_rdeps: continue @@ -359,7 +371,7 @@ class TaskData: added internally during dependency resolution """ - if item in dataCache.ignored_dependencies: + if re_match_strings(item, dataCache.ignored_dependencies): return if not item in dataCache.providers: @@ -367,7 +379,7 @@ class TaskData: bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item))) else: bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item)) - bb.event.fire(bb.event.NoProvider(item, cfgData)) + bb.event.fire(bb.event.NoProvider(item), cfgData) raise bb.providers.NoProvider(item) if self.have_build_target(item): @@ -380,7 +392,7 @@ class TaskData: if not eligible: bb.msg.note(2, bb.msg.domain.Provider, "No buildable provider PROVIDES '%s' but '%s' DEPENDS on or otherwise requires it. Enable debugging and see earlier logs to find unbuildable providers." % (item, self.get_dependees_str(item))) - bb.event.fire(bb.event.NoProvider(item, cfgData)) + bb.event.fire(bb.event.NoProvider(item), cfgData) raise bb.providers.NoProvider(item) if len(eligible) > 1 and foundUnique == False: @@ -390,7 +402,7 @@ class TaskData: providers_list.append(dataCache.pkg_fn[fn]) bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available for %s (%s);" % (item, ", ".join(providers_list))) bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item) - bb.event.fire(bb.event.MultipleProviders(item, providers_list, cfgData)) + bb.event.fire(bb.event.MultipleProviders(item, providers_list), cfgData) self.consider_msgs_cache.append(item) for fn in eligible: @@ -410,7 +422,7 @@ class TaskData: (takes item names from RDEPENDS/PACKAGES namespace) """ - if item in dataCache.ignored_dependencies: + if re_match_strings(item, dataCache.ignored_dependencies): return if self.have_runtime_target(item): @@ -420,7 +432,7 @@ class TaskData: if not all_p: bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables" % (self.get_rdependees_str(item), item)) - bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True)) + bb.event.fire(bb.event.NoProvider(item, runtime=True), cfgData) raise bb.providers.NoRProvider(item) eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache) @@ -428,7 +440,7 @@ class TaskData: if not eligible: bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables of any buildable targets.\nEnable debugging and see earlier logs to find unbuildable targets." % (self.get_rdependees_str(item), item)) - bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True)) + bb.event.fire(bb.event.NoProvider(item, runtime=True), cfgData) raise bb.providers.NoRProvider(item) if len(eligible) > 1 and numberPreferred == 0: @@ -438,7 +450,7 @@ class TaskData: providers_list.append(dataCache.pkg_fn[fn]) bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available for runtime %s (%s);" % (item, ", ".join(providers_list))) bb.msg.note(2, bb.msg.domain.Provider, "consider defining a PREFERRED_PROVIDER entry to match runtime %s" % item) - bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True)) + bb.event.fire(bb.event.MultipleProviders(item,providers_list, runtime=True), cfgData) self.consider_msgs_cache.append(item) if numberPreferred > 1: @@ -448,7 +460,7 @@ class TaskData: providers_list.append(dataCache.pkg_fn[fn]) bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available for runtime %s (top %s entries preferred) (%s);" % (item, numberPreferred, ", ".join(providers_list))) bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER entry to match runtime %s" % item) - bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True)) + bb.event.fire(bb.event.MultipleProviders(item,providers_list, runtime=True), cfgData) self.consider_msgs_cache.append(item) # run through the list until we find one that we can build diff --git a/bitbake/lib/bb/ui/__init__.py b/bitbake/lib/bb/ui/__init__.py new file mode 100644 index 000000000..c6a377a8e --- /dev/null +++ b/bitbake/lib/bb/ui/__init__.py @@ -0,0 +1,18 @@ +# +# BitBake UI Implementation +# +# Copyright (C) 2006-2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + diff --git a/bitbake/lib/bb/ui/crumbs/__init__.py b/bitbake/lib/bb/ui/crumbs/__init__.py new file mode 100644 index 000000000..c6a377a8e --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/__init__.py @@ -0,0 +1,18 @@ +# +# BitBake UI Implementation +# +# Copyright (C) 2006-2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + diff --git a/bitbake/lib/bb/ui/crumbs/buildmanager.py b/bitbake/lib/bb/ui/crumbs/buildmanager.py new file mode 100644 index 000000000..f89e8eefd --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/buildmanager.py @@ -0,0 +1,457 @@ +# +# BitBake Graphical GTK User Interface +# +# Copyright (C) 2008 Intel Corporation +# +# Authored by Rob Bradford +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import gtk +import gobject +import threading +import os +import datetime +import time + +class BuildConfiguration: + """ Represents a potential *or* historic *or* concrete build. It + encompasses all the things that we need to tell bitbake to do to make it + build what we want it to build. + + It also stored the metadata URL and the set of possible machines (and the + distros / images / uris for these. Apart from the metdata URL these are + not serialised to file (since they may be transient). In some ways this + functionality might be shifted to the loader class.""" + + def __init__ (self): + self.metadata_url = None + + # Tuple of (distros, image, urls) + self.machine_options = {} + + self.machine = None + self.distro = None + self.image = None + self.urls = [] + self.extra_urls = [] + self.extra_pkgs = [] + + def get_machines_model (self): + model = gtk.ListStore (gobject.TYPE_STRING) + for machine in self.machine_options.keys(): + model.append ([machine]) + + return model + + def get_distro_and_images_models (self, machine): + distro_model = gtk.ListStore (gobject.TYPE_STRING) + + for distro in self.machine_options[machine][0]: + distro_model.append ([distro]) + + image_model = gtk.ListStore (gobject.TYPE_STRING) + + for image in self.machine_options[machine][1]: + image_model.append ([image]) + + return (distro_model, image_model) + + def get_repos (self): + self.urls = self.machine_options[self.machine][2] + return self.urls + + # It might be a lot lot better if we stored these in like, bitbake conf + # file format. + @staticmethod + def load_from_file (filename): + f = open (filename, "r") + + conf = BuildConfiguration() + for line in f.readlines(): + data = line.split (";")[1] + if (line.startswith ("metadata-url;")): + conf.metadata_url = data.strip() + continue + if (line.startswith ("url;")): + conf.urls += [data.strip()] + continue + if (line.startswith ("extra-url;")): + conf.extra_urls += [data.strip()] + continue + if (line.startswith ("machine;")): + conf.machine = data.strip() + continue + if (line.startswith ("distribution;")): + conf.distro = data.strip() + continue + if (line.startswith ("image;")): + conf.image = data.strip() + continue + + f.close () + return conf + + # Serialise to a file. This is part of the build process and we use this + # to be able to repeat a given build (using the same set of parameters) + # but also so that we can include the details of the image / machine / + # distro in the build manager tree view. + def write_to_file (self, filename): + f = open (filename, "w") + + lines = [] + + if (self.metadata_url): + lines += ["metadata-url;%s\n" % (self.metadata_url)] + + for url in self.urls: + lines += ["url;%s\n" % (url)] + + for url in self.extra_urls: + lines += ["extra-url;%s\n" % (url)] + + if (self.machine): + lines += ["machine;%s\n" % (self.machine)] + + if (self.distro): + lines += ["distribution;%s\n" % (self.distro)] + + if (self.image): + lines += ["image;%s\n" % (self.image)] + + f.writelines (lines) + f.close () + +class BuildResult(gobject.GObject): + """ Represents an historic build. Perhaps not successful. But it includes + things such as the files that are in the directory (the output from the + build) as well as a deserialised BuildConfiguration file that is stored in + ".conf" in the directory for the build. + + This is GObject so that it can be included in the TreeStore.""" + + (STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \ + (0, 1, 2) + + def __init__ (self, parent, identifier): + gobject.GObject.__init__ (self) + self.date = None + + self.files = [] + self.status = None + self.identifier = identifier + self.path = os.path.join (parent, identifier) + + # Extract the date, since the directory name is of the + # format build-- we can easily + # pull it out. + # TODO: Better to stat a file? + (_ , date, revision) = identifier.split ("-") + print date + + year = int (date[0:4]) + month = int (date[4:6]) + day = int (date[6:8]) + + self.date = datetime.date (year, month, day) + + self.conf = None + + # By default builds are STATE_FAILED unless we find a "complete" file + # in which case they are STATE_COMPLETE + self.state = BuildResult.STATE_FAILED + for file in os.listdir (self.path): + if (file.startswith (".conf")): + conffile = os.path.join (self.path, file) + self.conf = BuildConfiguration.load_from_file (conffile) + elif (file.startswith ("complete")): + self.state = BuildResult.STATE_COMPLETE + else: + self.add_file (file) + + def add_file (self, file): + # Just add the file for now. Don't care about the type. + self.files += [(file, None)] + +class BuildManagerModel (gtk.TreeStore): + """ Model for the BuildManagerTreeView. This derives from gtk.TreeStore + but it abstracts nicely what the columns mean and the setup of the columns + in the model. """ + + (COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \ + (0, 1, 2, 3, 4, 5, 6) + + def __init__ (self): + gtk.TreeStore.__init__ (self, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_OBJECT, + gobject.TYPE_INT64, + gobject.TYPE_INT) + +class BuildManager (gobject.GObject): + """ This class manages the historic builds that have been found in the + "results" directory but is also used for starting a new build.""" + + __gsignals__ = { + 'population-finished' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()), + 'populate-error' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()) + } + + def update_build_result (self, result, iter): + # Convert the date into something we can sort by. + date = long (time.mktime (result.date.timetuple())) + + # Add a top level entry for the build + + self.model.set (iter, + BuildManagerModel.COL_IDENT, result.identifier, + BuildManagerModel.COL_DESC, result.conf.image, + BuildManagerModel.COL_MACHINE, result.conf.machine, + BuildManagerModel.COL_DISTRO, result.conf.distro, + BuildManagerModel.COL_BUILD_RESULT, result, + BuildManagerModel.COL_DATE, date, + BuildManagerModel.COL_STATE, result.state) + + # And then we use the files in the directory as the children for the + # top level iter. + for file in result.files: + self.model.append (iter, (None, file[0], None, None, None, date, -1)) + + # This function is called as an idle by the BuildManagerPopulaterThread + def add_build_result (self, result): + gtk.gdk.threads_enter() + self.known_builds += [result] + + self.update_build_result (result, self.model.append (None)) + + gtk.gdk.threads_leave() + + def notify_build_finished (self): + # This is a bit of a hack. If we have a running build running then we + # will have a row in the model in STATE_ONGOING. Find it and make it + # as if it was a proper historic build (well, it is completed now....) + + # We need to use the iters here rather than the Python iterator + # interface to the model since we need to pass it into + # update_build_result + + iter = self.model.get_iter_first() + + while (iter): + (ident, state) = self.model.get(iter, + BuildManagerModel.COL_IDENT, + BuildManagerModel.COL_STATE) + + if state == BuildResult.STATE_ONGOING: + result = BuildResult (self.results_directory, ident) + self.update_build_result (result, iter) + iter = self.model.iter_next(iter) + + def notify_build_succeeded (self): + # Write the "complete" file so that when we create the BuildResult + # object we put into the model + + complete_file_path = os.path.join (self.cur_build_directory, "complete") + f = file (complete_file_path, "w") + f.close() + self.notify_build_finished() + + def notify_build_failed (self): + # Without a "complete" file then this will mark the build as failed: + self.notify_build_finished() + + # This function is called as an idle + def emit_population_finished_signal (self): + gtk.gdk.threads_enter() + self.emit ("population-finished") + gtk.gdk.threads_leave() + + class BuildManagerPopulaterThread (threading.Thread): + def __init__ (self, manager, directory): + threading.Thread.__init__ (self) + self.manager = manager + self.directory = directory + + def run (self): + # For each of the "build-<...>" directories .. + + if os.path.exists (self.directory): + for directory in os.listdir (self.directory): + + if not directory.startswith ("build-"): + continue + + build_result = BuildResult (self.directory, directory) + self.manager.add_build_result (build_result) + + gobject.idle_add (BuildManager.emit_population_finished_signal, + self.manager) + + def __init__ (self, server, results_directory): + gobject.GObject.__init__ (self) + + # The builds that we've found from walking the result directory + self.known_builds = [] + + # Save out the bitbake server, we need this for issuing commands to + # the cooker: + self.server = server + + # The TreeStore that we use + self.model = BuildManagerModel () + + # The results directory is where we create (and look for) the + # build-- directories. We need to populate ourselves from + # directory + self.results_directory = results_directory + self.populate_from_directory (self.results_directory) + + def populate_from_directory (self, directory): + thread = BuildManager.BuildManagerPopulaterThread (self, directory) + thread.start() + + # Come up with the name for the next build ident by combining "build-" + # with the date formatted as yyyymmdd and then an ordinal. We do this by + # an optimistic algorithm incrementing the ordinal if we find that it + # already exists. + def get_next_build_ident (self): + today = datetime.date.today () + datestr = str (today.year) + str (today.month) + str (today.day) + + revision = 0 + test_name = "build-%s-%d" % (datestr, revision) + test_path = os.path.join (self.results_directory, test_name) + + while (os.path.exists (test_path)): + revision += 1 + test_name = "build-%s-%d" % (datestr, revision) + test_path = os.path.join (self.results_directory, test_name) + + return test_name + + # Take a BuildConfiguration and then try and build it based on the + # parameters of that configuration. S + def do_build (self, conf): + server = self.server + + # Work out the build directory. Note we actually create the + # directories here since we need to write the ".conf" file. Otherwise + # we could have relied on bitbake's builder thread to actually make + # the directories as it proceeds with the build. + ident = self.get_next_build_ident () + build_directory = os.path.join (self.results_directory, + ident) + self.cur_build_directory = build_directory + os.makedirs (build_directory) + + conffile = os.path.join (build_directory, ".conf") + conf.write_to_file (conffile) + + # Add a row to the model representing this ongoing build. It's kinda a + # fake entry. If this build completes or fails then this gets updated + # with the real stuff like the historic builds + date = long (time.time()) + self.model.append (None, (ident, conf.image, conf.machine, conf.distro, + None, date, BuildResult.STATE_ONGOING)) + try: + server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1]) + server.runCommand(["setVariable", "MACHINE", conf.machine]) + server.runCommand(["setVariable", "DISTRO", conf.distro]) + server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"]) + server.runCommand(["setVariable", "BBFILES", \ + """${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""]) + server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"]) + server.runCommand(["setVariable", "IPK_FEED_URIS", \ + " ".join(conf.get_repos())]) + server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE", + build_directory]) + server.runCommand(["buildTargets", [conf.image], "rootfs"]) + + except Exception, e: + print e + +class BuildManagerTreeView (gtk.TreeView): + """ The tree view for the build manager. This shows the historic builds + and so forth. """ + + # We use this function to control what goes in the cell since we store + # the date in the model as seconds since the epoch (for sorting) and so we + # need to make it human readable. + def date_format_custom_cell_data_func (self, col, cell, model, iter): + date = model.get (iter, BuildManagerModel.COL_DATE)[0] + datestr = time.strftime("%A %d %B %Y", time.localtime(date)) + cell.set_property ("text", datestr) + + # This format function controls what goes in the cell. We use this to map + # the integer state to a string and also to colourise the text + def state_format_custom_cell_data_fun (self, col, cell, model, iter): + state = model.get (iter, BuildManagerModel.COL_STATE)[0] + + if (state == BuildResult.STATE_ONGOING): + cell.set_property ("text", "Active") + cell.set_property ("foreground", "#000000") + elif (state == BuildResult.STATE_FAILED): + cell.set_property ("text", "Failed") + cell.set_property ("foreground", "#ff0000") + elif (state == BuildResult.STATE_COMPLETE): + cell.set_property ("text", "Complete") + cell.set_property ("foreground", "#00ff00") + else: + cell.set_property ("text", "") + + def __init__ (self): + gtk.TreeView.__init__(self) + + # Misc descriptiony thing + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn (None, renderer, + text=BuildManagerModel.COL_DESC) + self.append_column (col) + + # Machine + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn ("Machine", renderer, + text=BuildManagerModel.COL_MACHINE) + self.append_column (col) + + # distro + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn ("Distribution", renderer, + text=BuildManagerModel.COL_DISTRO) + self.append_column (col) + + # date (using a custom function for formatting the cell contents it + # takes epoch -> human readable string) + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn ("Date", renderer, + text=BuildManagerModel.COL_DATE) + self.append_column (col) + col.set_cell_data_func (renderer, + self.date_format_custom_cell_data_func) + + # For status. + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn ("Status", renderer, + text = BuildManagerModel.COL_STATE) + self.append_column (col) + col.set_cell_data_func (renderer, + self.state_format_custom_cell_data_fun) + diff --git a/bitbake/lib/bb/ui/crumbs/puccho.glade b/bitbake/lib/bb/ui/crumbs/puccho.glade new file mode 100644 index 000000000..d7553a6e1 --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/puccho.glade @@ -0,0 +1,606 @@ + + + + + + Start a build + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + 2 + + + True + 6 + 7 + 3 + 5 + 6 + + + True + 12 + + + 6 + + + True + True + 0 + gtk-dialog-error + + + False + False + + + + + True + 0 + If you see this text something is wrong... + True + True + + + 1 + + + + + + + 3 + 2 + 3 + + + + + True + 0 + <b>Build configuration</b> + True + + + 3 + 3 + 4 + + + + + + True + False + + + 1 + 2 + 6 + 7 + + + + + + True + False + 0 + 12 + Image: + + + 6 + 7 + + + + + + True + False + + + 1 + 2 + 5 + 6 + + + + + + True + False + 0 + 12 + Distribution: + + + 5 + 6 + + + + + + True + False + + + 1 + 2 + 4 + 5 + + + + + + True + False + 0 + 12 + Machine: + + + 4 + 5 + + + + + + True + False + True + True + gtk-refresh + True + 0 + + + 2 + 3 + 1 + 2 + + + + + + True + True + 32 + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + 12 + Location: + + + 1 + 2 + + + + + + True + 0 + <b>Repository</b> + True + + + 3 + + + + + + True + + + + + + 2 + 3 + 4 + 5 + + + + + + True + + + + + + 2 + 3 + 5 + 6 + + + + + + True + + + + + + 2 + 3 + 6 + 7 + + + + + + 1 + + + + + True + GTK_BUTTONBOX_END + + + + + + + + + + + + False + GTK_PACK_END + + + + + + + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + 2 + + + True + 6 + 7 + 3 + 6 + 6 + + + True + 0 + <b>Repositories</b> + True + + + 3 + + + + + + True + 0 + 12 + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + True + + + + + + + 3 + 2 + 3 + + + + + + True + True + + + 1 + 3 + 1 + 2 + + + + + + True + 0 + <b>Additional packages</b> + True + + + 3 + 4 + 5 + + + + + + True + 0 + 0 + + + True + 0 + 0 + 12 + Location: + + + + + 1 + 2 + + + + + + True + 1 + 0 + + + True + 5 + + + True + True + True + gtk-remove + True + 0 + + + + + True + True + True + gtk-edit + True + 0 + + + 1 + + + + + True + True + True + gtk-add + True + 0 + + + 2 + + + + + + + 1 + 3 + 3 + 4 + + + + + + True + + + + + + 3 + 4 + + + + + + True + 0 + 0 + 12 + Search: + + + 5 + 6 + + + + + + True + True + + + 1 + 3 + 5 + 6 + + + + + + True + 0 + 12 + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + True + + + + + + + 3 + 6 + 7 + + + + + + 1 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-close + True + 0 + + + + + False + GTK_PACK_END + + + + + + + + + True + + + True + + + True + Build + gtk-execute + + + False + + + + + False + + + + + True + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + + + + False + True + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + + + + True + True + + + + + 1 + + + + + + diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py new file mode 100644 index 000000000..401559255 --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py @@ -0,0 +1,180 @@ +# +# BitBake Graphical GTK User Interface +# +# Copyright (C) 2008 Intel Corporation +# +# Authored by Rob Bradford +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import gtk +import gobject + +class RunningBuildModel (gtk.TreeStore): + (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5) + def __init__ (self): + gtk.TreeStore.__init__ (self, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_BOOLEAN) + +class RunningBuild (gobject.GObject): + __gsignals__ = { + 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()), + 'build-failed' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()) + } + pids_to_task = {} + tasks_to_iter = {} + + def __init__ (self): + gobject.GObject.__init__ (self) + self.model = RunningBuildModel() + + def handle_event (self, event): + # Handle an event from the event queue, this may result in updating + # the model and thus the UI. Or it may be to tell us that the build + # has finished successfully (or not, as the case may be.) + + parent = None + pid = 0 + package = None + task = None + + # If we have a pid attached to this message/event try and get the + # (package, task) pair for it. If we get that then get the parent iter + # for the message. + if hassattr(event, 'pid'): + pid = event.pid + if self.pids_to_task.has_key(pid): + (package, task) = self.pids_to_task[pid] + parent = self.tasks_to_iter[(package, task)] + + if isinstance(event, bb.msg.Msg): + # Set a pretty icon for the message based on it's type. + if isinstance(event, bb.msg.MsgWarn): + icon = "dialog-warning" + elif isinstance(event, bb.msg.MsgErr): + icon = "dialog-error" + else: + icon = None + + # Ignore the "Running task i of n .." messages + if (event._message.startswith ("Running task")): + return + + # Add the message to the tree either at the top level if parent is + # None otherwise as a descendent of a task. + self.model.append (parent, + (event.__name__.split()[-1], # e.g. MsgWarn, MsgError + package, + task, + event._message, + icon, + False)) + elif isinstance(event, bb.build.TaskStarted): + (package, task) = (event._package, event._task) + + # Save out this PID. + self.pids_to_task[pid] = (package,task) + + # Check if we already have this package in our model. If so then + # that can be the parent for the task. Otherwise we create a new + # top level for the package. + if (self.tasks_to_iter.has_key ((package, None))): + parent = self.tasks_to_iter[(package, None)] + else: + parent = self.model.append (None, (None, + package, + None, + "Package: %s" % (package), + None, + False)) + self.tasks_to_iter[(package, None)] = parent + + # Because this parent package now has an active child mark it as + # such. + self.model.set(parent, self.model.COL_ICON, "gtk-execute") + + # Add an entry in the model for this task + i = self.model.append (parent, (None, + package, + task, + "Task: %s" % (task), + None, + False)) + + # Save out the iter so that we can find it when we have a message + # that we need to attach to a task. + self.tasks_to_iter[(package, task)] = i + + # Mark this task as active. + self.model.set(i, self.model.COL_ICON, "gtk-execute") + + elif isinstance(event, bb.build.Task): + + if isinstance(event, bb.build.TaskFailed): + # Mark the task as failed + i = self.tasks_to_iter[(package, task)] + self.model.set(i, self.model.COL_ICON, "dialog-error") + + # Mark the parent package as failed + i = self.tasks_to_iter[(package, None)] + self.model.set(i, self.model.COL_ICON, "dialog-error") + else: + # Mark the task as inactive + i = self.tasks_to_iter[(package, task)] + self.model.set(i, self.model.COL_ICON, None) + + # Mark the parent package as inactive + i = self.tasks_to_iter[(package, None)] + self.model.set(i, self.model.COL_ICON, None) + + + # Clear the iters and the pids since when the task goes away the + # pid will no longer be used for messages + del self.tasks_to_iter[(package, task)] + del self.pids_to_task[pid] + + elif isinstance(event, bb.event.BuildCompleted): + failures = int (event._failures) + + # Emit the appropriate signal depending on the number of failures + if (failures > 1): + self.emit ("build-failed") + else: + self.emit ("build-succeeded") + +class RunningBuildTreeView (gtk.TreeView): + def __init__ (self): + gtk.TreeView.__init__ (self) + + # The icon that indicates whether we're building or failed. + renderer = gtk.CellRendererPixbuf () + col = gtk.TreeViewColumn ("Status", renderer) + col.add_attribute (renderer, "icon-name", 4) + self.append_column (col) + + # The message of the build. + renderer = gtk.CellRendererText () + col = gtk.TreeViewColumn ("Message", renderer, text=3) + self.append_column (col) + + diff --git a/bitbake/lib/bb/ui/depexp.py b/bitbake/lib/bb/ui/depexp.py new file mode 100644 index 000000000..cfa5b6564 --- /dev/null +++ b/bitbake/lib/bb/ui/depexp.py @@ -0,0 +1,272 @@ +# +# BitBake Graphical GTK based Dependency Explorer +# +# Copyright (C) 2007 Ross Burton +# Copyright (C) 2007 - 2008 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import gobject +import gtk +import threading +import xmlrpclib + +# Package Model +(COL_PKG_NAME) = (0) + +# Dependency Model +(TYPE_DEP, TYPE_RDEP) = (0, 1) +(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2) + +class PackageDepView(gtk.TreeView): + def __init__(self, model, dep_type, label): + gtk.TreeView.__init__(self) + self.current = None + self.dep_type = dep_type + self.filter_model = model.filter_new() + self.filter_model.set_visible_func(self._filter) + self.set_model(self.filter_model) + #self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) + self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE)) + + def _filter(self, model, iter): + (this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT) + if this_type != self.dep_type: return False + return package == self.current + + def set_current_package(self, package): + self.current = package + self.filter_model.refilter() + +class PackageReverseDepView(gtk.TreeView): + def __init__(self, model, label): + gtk.TreeView.__init__(self) + self.current = None + self.filter_model = model.filter_new() + self.filter_model.set_visible_func(self._filter) + self.set_model(self.filter_model) + self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT)) + + def _filter(self, model, iter): + package = model.get_value(iter, COL_DEP_PACKAGE) + return package == self.current + + def set_current_package(self, package): + self.current = package + self.filter_model.refilter() + +class DepExplorer(gtk.Window): + def __init__(self): + gtk.Window.__init__(self) + self.set_title("Dependency Explorer") + self.set_default_size(500, 500) + self.connect("delete-event", gtk.main_quit) + + # Create the data models + self.pkg_model = gtk.ListStore(gobject.TYPE_STRING) + self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING) + + pane = gtk.HPaned() + pane.set_position(250) + self.add(pane) + + # The master list of packages + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled.set_shadow_type(gtk.SHADOW_IN) + self.pkg_treeview = gtk.TreeView(self.pkg_model) + self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed) + self.pkg_treeview.append_column(gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)) + pane.add1(scrolled) + scrolled.add(self.pkg_treeview) + + box = gtk.VBox(homogeneous=True, spacing=4) + + # Runtime Depends + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled.set_shadow_type(gtk.SHADOW_IN) + self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends") + self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) + scrolled.add(self.rdep_treeview) + box.add(scrolled) + + # Build Depends + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled.set_shadow_type(gtk.SHADOW_IN) + self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends") + self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE) + scrolled.add(self.dep_treeview) + box.add(scrolled) + pane.add2(box) + + # Reverse Depends + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled.set_shadow_type(gtk.SHADOW_IN) + self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends") + self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT) + scrolled.add(self.revdep_treeview) + box.add(scrolled) + pane.add2(box) + + self.show_all() + + def on_package_activated(self, treeview, path, column, data_col): + model = treeview.get_model() + package = model.get_value(model.get_iter(path), data_col) + + pkg_path = [] + def finder(model, path, iter, needle): + package = model.get_value(iter, COL_PKG_NAME) + if package == needle: + pkg_path.append(path) + return True + else: + return False + self.pkg_model.foreach(finder, package) + if pkg_path: + self.pkg_treeview.get_selection().select_path(pkg_path[0]) + self.pkg_treeview.scroll_to_cell(pkg_path[0]) + + def on_cursor_changed(self, selection): + (model, it) = selection.get_selected() + if iter is None: + current_package = None + else: + current_package = model.get_value(it, COL_PKG_NAME) + self.rdep_treeview.set_current_package(current_package) + self.dep_treeview.set_current_package(current_package) + self.revdep_treeview.set_current_package(current_package) + + +def parse(depgraph, pkg_model, depends_model): + + for package in depgraph["pn"]: + pkg_model.set(pkg_model.append(), COL_PKG_NAME, package) + + for package in depgraph["depends"]: + for depend in depgraph["depends"][package]: + depends_model.set (depends_model.append(), + COL_DEP_TYPE, TYPE_DEP, + COL_DEP_PARENT, package, + COL_DEP_PACKAGE, depend) + + for package in depgraph["rdepends-pn"]: + for rdepend in depgraph["rdepends-pn"][package]: + depends_model.set (depends_model.append(), + COL_DEP_TYPE, TYPE_RDEP, + COL_DEP_PARENT, package, + COL_DEP_PACKAGE, rdepend) + +class ProgressBar(gtk.Window): + def __init__(self): + + gtk.Window.__init__(self) + self.set_title("Parsing .bb files, please wait...") + self.set_default_size(500, 0) + self.connect("delete-event", gtk.main_quit) + + self.progress = gtk.ProgressBar() + self.add(self.progress) + self.show_all() + +class gtkthread(threading.Thread): + quit = threading.Event() + def __init__(self, shutdown): + threading.Thread.__init__(self) + self.setDaemon(True) + self.shutdown = shutdown + + def run(self): + gobject.threads_init() + gtk.gdk.threads_init() + gtk.main() + gtkthread.quit.set() + +def init(server, eventHandler): + + try: + cmdline = server.runCommand(["getCmdLineAction"]) + if not cmdline or cmdline[0] != "generateDotGraph": + print "This UI is only compatible with the -g option" + return + ret = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]]) + if ret != True: + print "Couldn't run command! %s" % ret + return + except xmlrpclib.Fault, x: + print "XMLRPC Fault getting commandline:\n %s" % x + return + + shutdown = 0 + + gtkgui = gtkthread(shutdown) + gtkgui.start() + + gtk.gdk.threads_enter() + pbar = ProgressBar() + dep = DepExplorer() + gtk.gdk.threads_leave() + + while True: + try: + event = eventHandler.waitEvent(0.25) + if gtkthread.quit.isSet(): + break + + if event is None: + continue + if isinstance(event, bb.event.ParseProgress): + x = event.sofar + y = event.total + if x == y: + print("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors." + % ( event.cached, event.parsed, event.skipped, event.masked, event.errors)) + pbar.hide() + gtk.gdk.threads_enter() + pbar.progress.set_fraction(float(x)/float(y)) + pbar.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y)) + gtk.gdk.threads_leave() + continue + + if isinstance(event, bb.event.DepTreeGenerated): + gtk.gdk.threads_enter() + parse(event._depgraph, dep.pkg_model, dep.depends_model) + gtk.gdk.threads_leave() + + if isinstance(event, bb.command.CookerCommandCompleted): + continue + if isinstance(event, bb.command.CookerCommandFailed): + print "Command execution failed: %s" % event.error + break + if isinstance(event, bb.cooker.CookerExit): + break + + continue + + except KeyboardInterrupt: + if shutdown == 2: + print "\nThird Keyboard Interrupt, exit.\n" + break + if shutdown == 1: + print "\nSecond Keyboard Interrupt, stopping...\n" + server.runCommand(["stateStop"]) + if shutdown == 0: + print "\nKeyboard Interrupt, closing down...\n" + server.runCommand(["stateShutdown"]) + shutdown = shutdown + 1 + pass + diff --git a/bitbake/lib/bb/ui/goggle.py b/bitbake/lib/bb/ui/goggle.py new file mode 100644 index 000000000..94995d82d --- /dev/null +++ b/bitbake/lib/bb/ui/goggle.py @@ -0,0 +1,77 @@ +# +# BitBake Graphical GTK User Interface +# +# Copyright (C) 2008 Intel Corporation +# +# Authored by Rob Bradford +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import gobject +import gtk +import xmlrpclib +from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild + +def event_handle_idle_func (eventHandler, build): + + # Consume as many messages as we can in the time available to us + event = eventHandler.getEvent() + while event: + build.handle_event (event) + event = eventHandler.getEvent() + + return True + +class MainWindow (gtk.Window): + def __init__ (self): + gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL) + + # Setup tree view and the scrolled window + scrolled_window = gtk.ScrolledWindow () + self.add (scrolled_window) + self.cur_build_tv = RunningBuildTreeView() + scrolled_window.add (self.cur_build_tv) + +def init (server, eventHandler): + gobject.threads_init() + gtk.gdk.threads_init() + + window = MainWindow () + window.show_all () + + # Create the object for the current build + running_build = RunningBuild () + window.cur_build_tv.set_model (running_build.model) + try: + cmdline = server.runCommand(["getCmdLineAction"]) + print cmdline + if not cmdline: + return 1 + ret = server.runCommand(cmdline) + if ret != True: + print "Couldn't get default commandline! %s" % ret + return 1 + except xmlrpclib.Fault, x: + print "XMLRPC Fault getting commandline:\n %s" % x + return 1 + + # Use a timeout function for probing the event queue to find out if we + # have a message waiting for us. + gobject.timeout_add (200, + event_handle_idle_func, + eventHandler, + running_build) + + gtk.main() + diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py new file mode 100644 index 000000000..c69fd6ca6 --- /dev/null +++ b/bitbake/lib/bb/ui/knotty.py @@ -0,0 +1,162 @@ +# +# BitBake (No)TTY UI Implementation +# +# Handling output to TTYs or files (no TTY) +# +# Copyright (C) 2006-2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import os + +import sys +import itertools +import xmlrpclib + +parsespin = itertools.cycle( r'|/-\\' ) + +def init(server, eventHandler): + + # Get values of variables which control our output + includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"]) + loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) + + try: + cmdline = server.runCommand(["getCmdLineAction"]) + #print cmdline + if not cmdline: + return 1 + ret = server.runCommand(cmdline) + if ret != True: + print "Couldn't get default commandline! %s" % ret + return 1 + except xmlrpclib.Fault, x: + print "XMLRPC Fault getting commandline:\n %s" % x + return 1 + + shutdown = 0 + return_value = 0 + while True: + try: + event = eventHandler.waitEvent(0.25) + if event is None: + continue + #print event + if isinstance(event, bb.msg.MsgPlain): + print event._message + continue + if isinstance(event, bb.msg.MsgDebug): + print 'DEBUG: ' + event._message + continue + if isinstance(event, bb.msg.MsgNote): + print 'NOTE: ' + event._message + continue + if isinstance(event, bb.msg.MsgWarn): + print 'WARNING: ' + event._message + continue + if isinstance(event, bb.msg.MsgError): + return_value = 1 + print 'ERROR: ' + event._message + continue + if isinstance(event, bb.msg.MsgFatal): + return_value = 1 + print 'FATAL: ' + event._message + break + if isinstance(event, bb.build.TaskFailed): + return_value = 1 + logfile = event.logfile + if logfile: + print "ERROR: Logfile of failure stored in %s." % logfile + if 1 or includelogs: + print "Log data follows:" + f = open(logfile, "r") + lines = [] + while True: + l = f.readline() + if l == '': + break + l = l.rstrip() + if loglines: + lines.append(' | %s' % l) + if len(lines) > int(loglines): + lines.pop(0) + else: + print '| %s' % l + f.close() + if lines: + for line in lines: + print line + if isinstance(event, bb.build.TaskBase): + print "NOTE: %s" % event._message + continue + if isinstance(event, bb.event.ParseProgress): + x = event.sofar + y = event.total + if os.isatty(sys.stdout.fileno()): + sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) + sys.stdout.flush() + else: + if x == 1: + sys.stdout.write("Parsing .bb files, please wait...") + sys.stdout.flush() + if x == y: + sys.stdout.write("done.") + sys.stdout.flush() + if x == y: + print("\nParsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." + % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors)) + continue + + if isinstance(event, bb.command.CookerCommandCompleted): + break + if isinstance(event, bb.command.CookerCommandSetExitCode): + return_value = event.exitcode + continue + if isinstance(event, bb.command.CookerCommandFailed): + return_value = 1 + print "Command execution failed: %s" % event.error + break + if isinstance(event, bb.cooker.CookerExit): + break + + # ignore + if isinstance(event, bb.event.BuildStarted): + continue + if isinstance(event, bb.event.BuildCompleted): + continue + if isinstance(event, bb.event.MultipleProviders): + continue + if isinstance(event, bb.runqueue.runQueueEvent): + continue + if isinstance(event, bb.event.StampUpdate): + continue + if isinstance(event, bb.event.ConfigParsed): + continue + if isinstance(event, bb.event.RecipeParsed): + continue + print "Unknown Event: %s" % event + + except KeyboardInterrupt: + if shutdown == 2: + print "\nThird Keyboard Interrupt, exit.\n" + break + if shutdown == 1: + print "\nSecond Keyboard Interrupt, stopping...\n" + server.runCommand(["stateStop"]) + if shutdown == 0: + print "\nKeyboard Interrupt, closing down...\n" + server.runCommand(["stateShutdown"]) + shutdown = shutdown + 1 + pass + return return_value diff --git a/bitbake/lib/bb/ui/ncurses.py b/bitbake/lib/bb/ui/ncurses.py new file mode 100644 index 000000000..14310dc12 --- /dev/null +++ b/bitbake/lib/bb/ui/ncurses.py @@ -0,0 +1,335 @@ +# +# BitBake Curses UI Implementation +# +# Implements an ncurses frontend for the BitBake utility. +# +# Copyright (C) 2006 Michael 'Mickey' Lauer +# Copyright (C) 2006-2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" + We have the following windows: + + 1.) Main Window: Shows what we are ultimately building and how far we are. Includes status bar + 2.) Thread Activity Window: Shows one status line for every concurrent bitbake thread. + 3.) Command Line Window: Contains an interactive command line where you can interact w/ Bitbake. + + Basic window layout is like that: + + |---------------------------------------------------------| + |
| | + | | 0: foo do_compile complete| + | Building Gtk+-2.6.10 | 1: bar do_patch complete | + | Status: 60% | ... | + | | ... | + | | ... | + |---------------------------------------------------------| + | | + |>>> which virtual/kernel | + |openzaurus-kernel | + |>>> _ | + |---------------------------------------------------------| + +""" + +import os, sys, curses, itertools, time +import bb +import xmlrpclib +from bb import ui +from bb.ui import uihelper + +parsespin = itertools.cycle( r'|/-\\' ) + +X = 0 +Y = 1 +WIDTH = 2 +HEIGHT = 3 + +MAXSTATUSLENGTH = 32 + +class NCursesUI: + """ + NCurses UI Class + """ + class Window: + """Base Window Class""" + def __init__( self, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ): + self.win = curses.newwin( height, width, y, x ) + self.dimensions = ( x, y, width, height ) + """ + if curses.has_colors(): + color = 1 + curses.init_pair( color, fg, bg ) + self.win.bkgdset( ord(' '), curses.color_pair(color) ) + else: + self.win.bkgdset( ord(' '), curses.A_BOLD ) + """ + self.erase() + self.setScrolling() + self.win.noutrefresh() + + def erase( self ): + self.win.erase() + + def setScrolling( self, b = True ): + self.win.scrollok( b ) + self.win.idlok( b ) + + def setBoxed( self ): + self.boxed = True + self.win.box() + self.win.noutrefresh() + + def setText( self, x, y, text, *args ): + self.win.addstr( y, x, text, *args ) + self.win.noutrefresh() + + def appendText( self, text, *args ): + self.win.addstr( text, *args ) + self.win.noutrefresh() + + def drawHline( self, y ): + self.win.hline( y, 0, curses.ACS_HLINE, self.dimensions[WIDTH] ) + self.win.noutrefresh() + + class DecoratedWindow( Window ): + """Base class for windows with a box and a title bar""" + def __init__( self, title, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ): + NCursesUI.Window.__init__( self, x+1, y+3, width-2, height-4, fg, bg ) + self.decoration = NCursesUI.Window( x, y, width, height, fg, bg ) + self.decoration.setBoxed() + self.decoration.win.hline( 2, 1, curses.ACS_HLINE, width-2 ) + self.setTitle( title ) + + def setTitle( self, title ): + self.decoration.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) + + #-------------------------------------------------------------------------# +# class TitleWindow( Window ): + #-------------------------------------------------------------------------# +# """Title Window""" +# def __init__( self, x, y, width, height ): +# NCursesUI.Window.__init__( self, x, y, width, height ) +# version = bb.__version__ +# title = "BitBake %s" % version +# credit = "(C) 2003-2007 Team BitBake" +# #self.win.hline( 2, 1, curses.ACS_HLINE, width-2 ) +# self.win.border() +# self.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) +# self.setText( 1, 2, credit.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD ) + + #-------------------------------------------------------------------------# + class ThreadActivityWindow( DecoratedWindow ): + #-------------------------------------------------------------------------# + """Thread Activity Window""" + def __init__( self, x, y, width, height ): + NCursesUI.DecoratedWindow.__init__( self, "Thread Activity", x, y, width, height ) + + def setStatus( self, thread, text ): + line = "%02d: %s" % ( thread, text ) + width = self.dimensions[WIDTH] + if ( len(line) > width ): + line = line[:width-3] + "..." + else: + line = line.ljust( width ) + self.setText( 0, thread, line ) + + #-------------------------------------------------------------------------# + class MainWindow( DecoratedWindow ): + #-------------------------------------------------------------------------# + """Main Window""" + def __init__( self, x, y, width, height ): + self.StatusPosition = width - MAXSTATUSLENGTH + NCursesUI.DecoratedWindow.__init__( self, None, x, y, width, height ) + curses.nl() + + def setTitle( self, title ): + title = "BitBake %s" % bb.__version__ + self.decoration.setText( 2, 1, title, curses.A_BOLD ) + self.decoration.setText( self.StatusPosition - 8, 1, "Status:", curses.A_BOLD ) + + def setStatus(self, status): + while len(status) < MAXSTATUSLENGTH: + status = status + " " + self.decoration.setText( self.StatusPosition, 1, status, curses.A_BOLD ) + + + #-------------------------------------------------------------------------# + class ShellOutputWindow( DecoratedWindow ): + #-------------------------------------------------------------------------# + """Interactive Command Line Output""" + def __init__( self, x, y, width, height ): + NCursesUI.DecoratedWindow.__init__( self, "Command Line Window", x, y, width, height ) + + #-------------------------------------------------------------------------# + class ShellInputWindow( Window ): + #-------------------------------------------------------------------------# + """Interactive Command Line Input""" + def __init__( self, x, y, width, height ): + NCursesUI.Window.__init__( self, x, y, width, height ) + +# put that to the top again from curses.textpad import Textbox +# self.textbox = Textbox( self.win ) +# t = threading.Thread() +# t.run = self.textbox.edit +# t.start() + + #-------------------------------------------------------------------------# + def main(self, stdscr, server, eventHandler): + #-------------------------------------------------------------------------# + height, width = stdscr.getmaxyx() + + # for now split it like that: + # MAIN_y + THREAD_y = 2/3 screen at the top + # MAIN_x = 2/3 left, THREAD_y = 1/3 right + # CLI_y = 1/3 of screen at the bottom + # CLI_x = full + + main_left = 0 + main_top = 0 + main_height = ( height / 3 * 2 ) + main_width = ( width / 3 ) * 2 + clo_left = main_left + clo_top = main_top + main_height + clo_height = height - main_height - main_top - 1 + clo_width = width + cli_left = main_left + cli_top = clo_top + clo_height + cli_height = 1 + cli_width = width + thread_left = main_left + main_width + thread_top = main_top + thread_height = main_height + thread_width = width - main_width + + #tw = self.TitleWindow( 0, 0, width, main_top ) + mw = self.MainWindow( main_left, main_top, main_width, main_height ) + taw = self.ThreadActivityWindow( thread_left, thread_top, thread_width, thread_height ) + clo = self.ShellOutputWindow( clo_left, clo_top, clo_width, clo_height ) + cli = self.ShellInputWindow( cli_left, cli_top, cli_width, cli_height ) + cli.setText( 0, 0, "BB>" ) + + mw.setStatus("Idle") + + helper = uihelper.BBUIHelper() + shutdown = 0 + + try: + cmdline = server.runCommand(["getCmdLineAction"]) + if not cmdline: + return + ret = server.runCommand(cmdline) + if ret != True: + print "Couldn't get default commandlind! %s" % ret + return + except xmlrpclib.Fault, x: + print "XMLRPC Fault getting commandline:\n %s" % x + return + + exitflag = False + while not exitflag: + try: + event = eventHandler.waitEvent(0.25) + if not event: + continue + helper.eventHandler(event) + #mw.appendText("%s\n" % event[0]) + if isinstance(event, bb.build.Task): + mw.appendText("NOTE: %s\n" % event._message) + if isinstance(event, bb.msg.MsgDebug): + mw.appendText('DEBUG: ' + event._message + '\n') + if isinstance(event, bb.msg.MsgNote): + mw.appendText('NOTE: ' + event._message + '\n') + if isinstance(event, bb.msg.MsgWarn): + mw.appendText('WARNING: ' + event._message + '\n') + if isinstance(event, bb.msg.MsgError): + mw.appendText('ERROR: ' + event._message + '\n') + if isinstance(event, bb.msg.MsgFatal): + mw.appendText('FATAL: ' + event._message + '\n') + if isinstance(event, bb.event.ParseProgress): + x = event.sofar + y = event.total + if x == y: + mw.setStatus("Idle") + mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked." + % ( event.cached, event.parsed, event.skipped, event.masked )) + else: + mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) +# if isinstance(event, bb.build.TaskFailed): +# if event.logfile: +# if data.getVar("BBINCLUDELOGS", d): +# bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile) +# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d) +# if number_of_lines: +# os.system('tail -n%s %s' % (number_of_lines, logfile)) +# else: +# f = open(logfile, "r") +# while True: +# l = f.readline() +# if l == '': +# break +# l = l.rstrip() +# print '| %s' % l +# f.close() +# else: +# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile) + + if isinstance(event, bb.command.CookerCommandCompleted): + exitflag = True + if isinstance(event, bb.command.CookerCommandFailed): + mw.appendText("Command execution failed: %s" % event.error) + time.sleep(2) + exitflag = True + if isinstance(event, bb.cooker.CookerExit): + exitflag = True + + if helper.needUpdate: + activetasks, failedtasks = helper.getTasks() + taw.erase() + taw.setText(0, 0, "") + if activetasks: + taw.appendText("Active Tasks:\n") + for task in activetasks: + taw.appendText(task) + if failedtasks: + taw.appendText("Failed Tasks:\n") + for task in failedtasks: + taw.appendText(task) + + curses.doupdate() + except KeyboardInterrupt: + if shutdown == 2: + mw.appendText("Third Keyboard Interrupt, exit.\n") + exitflag = True + if shutdown == 1: + mw.appendText("Second Keyboard Interrupt, stopping...\n") + server.runCommand(["stateStop"]) + if shutdown == 0: + mw.appendText("Keyboard Interrupt, closing down...\n") + server.runCommand(["stateShutdown"]) + shutdown = shutdown + 1 + pass + +def init(server, eventHandler): + if not os.isatty(sys.stdout.fileno()): + print "FATAL: Unable to run 'ncurses' UI without a TTY." + return + ui = NCursesUI() + try: + curses.wrapper(ui.main, server, eventHandler) + except: + import traceback + traceback.print_exc() + diff --git a/bitbake/lib/bb/ui/puccho.py b/bitbake/lib/bb/ui/puccho.py new file mode 100644 index 000000000..713aa1f4a --- /dev/null +++ b/bitbake/lib/bb/ui/puccho.py @@ -0,0 +1,425 @@ +# +# BitBake Graphical GTK User Interface +# +# Copyright (C) 2008 Intel Corporation +# +# Authored by Rob Bradford +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import gtk +import gobject +import gtk.glade +import threading +import urllib2 +import os + +from bb.ui.crumbs.buildmanager import BuildManager, BuildConfiguration +from bb.ui.crumbs.buildmanager import BuildManagerTreeView + +from bb.ui.crumbs.runningbuild import RunningBuild, RunningBuildTreeView + +# The metadata loader is used by the BuildSetupDialog to download the +# available options to populate the dialog +class MetaDataLoader(gobject.GObject): + """ This class provides the mechanism for loading the metadata (the + fetching and parsing) from a given URL. The metadata encompasses details + on what machines are available. The distribution and images available for + the machine and the the uris to use for building the given machine.""" + __gsignals__ = { + 'success' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()), + 'error' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING,)) + } + + # We use these little helper functions to ensure that we take the gdk lock + # when emitting the signal. These functions are called as idles (so that + # they happen in the gtk / main thread's main loop. + def emit_error_signal (self, remark): + gtk.gdk.threads_enter() + self.emit ("error", remark) + gtk.gdk.threads_leave() + + def emit_success_signal (self): + gtk.gdk.threads_enter() + self.emit ("success") + gtk.gdk.threads_leave() + + def __init__ (self): + gobject.GObject.__init__ (self) + + class LoaderThread(threading.Thread): + """ This class provides an asynchronous loader for the metadata (by + using threads and signals). This is useful since the metadata may be + at a remote URL.""" + class LoaderImportException (Exception): + pass + + def __init__(self, loader, url): + threading.Thread.__init__ (self) + self.url = url + self.loader = loader + + def run (self): + result = {} + try: + f = urllib2.urlopen (self.url) + + # Parse the metadata format. The format is.... + # ;|...;|...;|... + for line in f.readlines(): + components = line.split(";") + if (len (components) < 4): + raise MetaDataLoader.LoaderThread.LoaderImportException + machine = components[0] + distros = components[1].split("|") + images = components[2].split("|") + urls = components[3].split("|") + + result[machine] = (distros, images, urls) + + # Create an object representing this *potential* + # configuration. It can become concrete if the machine, distro + # and image are all chosen in the UI + configuration = BuildConfiguration() + configuration.metadata_url = self.url + configuration.machine_options = result + self.loader.configuration = configuration + + # Emit that we've actually got a configuration + gobject.idle_add (MetaDataLoader.emit_success_signal, + self.loader) + + except MetaDataLoader.LoaderThread.LoaderImportException, e: + gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader, + "Repository metadata corrupt") + except Exception, e: + gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader, + "Unable to download repository metadata") + print e + + def try_fetch_from_url (self, url): + # Try and download the metadata. Firing a signal if successful + thread = MetaDataLoader.LoaderThread(self, url) + thread.start() + +class BuildSetupDialog (gtk.Dialog): + RESPONSE_BUILD = 1 + + # A little helper method that just sets the states on the widgets based on + # whether we've got good metadata or not. + def set_configurable (self, configurable): + if (self.configurable == configurable): + return + + self.configurable = configurable + for widget in self.conf_widgets: + widget.set_sensitive (configurable) + + if not configurable: + self.machine_combo.set_active (-1) + self.distribution_combo.set_active (-1) + self.image_combo.set_active (-1) + + # GTK widget callbacks + def refresh_button_clicked (self, button): + # Refresh button clicked. + + url = self.location_entry.get_chars (0, -1) + self.loader.try_fetch_from_url(url) + + def repository_entry_editable_changed (self, entry): + if (len (entry.get_chars (0, -1)) > 0): + self.refresh_button.set_sensitive (True) + else: + self.refresh_button.set_sensitive (False) + self.clear_status_message() + + # If we were previously configurable we are no longer since the + # location entry has been changed + self.set_configurable (False) + + def machine_combo_changed (self, combobox): + active_iter = combobox.get_active_iter() + + if not active_iter: + return + + model = combobox.get_model() + + if model: + chosen_machine = model.get (active_iter, 0)[0] + + (distros_model, images_model) = \ + self.loader.configuration.get_distro_and_images_models (chosen_machine) + + self.distribution_combo.set_model (distros_model) + self.image_combo.set_model (images_model) + + # Callbacks from the loader + def loader_success_cb (self, loader): + self.status_image.set_from_icon_name ("info", + gtk.ICON_SIZE_BUTTON) + self.status_image.show() + self.status_label.set_label ("Repository metadata successfully downloaded") + + # Set the models on the combo boxes based on the models generated from + # the configuration that the loader has created + + # We just need to set the machine here, that then determines the + # distro and image options. Cunning huh? :-) + + self.configuration = self.loader.configuration + model = self.configuration.get_machines_model () + self.machine_combo.set_model (model) + + self.set_configurable (True) + + def loader_error_cb (self, loader, message): + self.status_image.set_from_icon_name ("error", + gtk.ICON_SIZE_BUTTON) + self.status_image.show() + self.status_label.set_text ("Error downloading repository metadata") + for widget in self.conf_widgets: + widget.set_sensitive (False) + + def clear_status_message (self): + self.status_image.hide() + self.status_label.set_label ( + """Enter the repository location and press _Refresh""") + + def __init__ (self): + gtk.Dialog.__init__ (self) + + # Cancel + self.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + + # Build + button = gtk.Button ("_Build", None, True) + image = gtk.Image () + image.set_from_stock (gtk.STOCK_EXECUTE,gtk.ICON_SIZE_BUTTON) + button.set_image (image) + self.add_action_widget (button, BuildSetupDialog.RESPONSE_BUILD) + button.show_all () + + # Pull in *just* the table from the Glade XML data. + gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade", + root = "build_table") + table = gxml.get_widget ("build_table") + self.vbox.pack_start (table, True, False, 0) + + # Grab all the widgets that we need to turn on/off when we refresh... + self.conf_widgets = [] + self.conf_widgets += [gxml.get_widget ("machine_label")] + self.conf_widgets += [gxml.get_widget ("distribution_label")] + self.conf_widgets += [gxml.get_widget ("image_label")] + self.conf_widgets += [gxml.get_widget ("machine_combo")] + self.conf_widgets += [gxml.get_widget ("distribution_combo")] + self.conf_widgets += [gxml.get_widget ("image_combo")] + + # Grab the status widgets + self.status_image = gxml.get_widget ("status_image") + self.status_label = gxml.get_widget ("status_label") + + # Grab the refresh button and connect to the clicked signal + self.refresh_button = gxml.get_widget ("refresh_button") + self.refresh_button.connect ("clicked", self.refresh_button_clicked) + + # Grab the location entry and connect to editable::changed + self.location_entry = gxml.get_widget ("location_entry") + self.location_entry.connect ("changed", + self.repository_entry_editable_changed) + + # Grab the machine combo and hook onto the changed signal. This then + # allows us to populate the distro and image combos + self.machine_combo = gxml.get_widget ("machine_combo") + self.machine_combo.connect ("changed", self.machine_combo_changed) + + # Setup the combo + cell = gtk.CellRendererText() + self.machine_combo.pack_start(cell, True) + self.machine_combo.add_attribute(cell, 'text', 0) + + # Grab the distro and image combos. We need these to populate with + # models once the machine is chosen + self.distribution_combo = gxml.get_widget ("distribution_combo") + cell = gtk.CellRendererText() + self.distribution_combo.pack_start(cell, True) + self.distribution_combo.add_attribute(cell, 'text', 0) + + self.image_combo = gxml.get_widget ("image_combo") + cell = gtk.CellRendererText() + self.image_combo.pack_start(cell, True) + self.image_combo.add_attribute(cell, 'text', 0) + + # Put the default descriptive text in the status box + self.clear_status_message() + + # Mark as non-configurable, this is just greys out the widgets the + # user can't yet use + self.configurable = False + self.set_configurable(False) + + # Show the table + table.show_all () + + # The loader and some signals connected to it to update the status + # area + self.loader = MetaDataLoader() + self.loader.connect ("success", self.loader_success_cb) + self.loader.connect ("error", self.loader_error_cb) + + def update_configuration (self): + """ A poorly named function but it updates the internal configuration + from the widgets. This can make that configuration concrete and can + thus be used for building """ + # Extract the chosen machine from the combo + model = self.machine_combo.get_model() + active_iter = self.machine_combo.get_active_iter() + if (active_iter): + self.configuration.machine = model.get(active_iter, 0)[0] + + # Extract the chosen distro from the combo + model = self.distribution_combo.get_model() + active_iter = self.distribution_combo.get_active_iter() + if (active_iter): + self.configuration.distro = model.get(active_iter, 0)[0] + + # Extract the chosen image from the combo + model = self.image_combo.get_model() + active_iter = self.image_combo.get_active_iter() + if (active_iter): + self.configuration.image = model.get(active_iter, 0)[0] + +# This function operates to pull events out from the event queue and then push +# them into the RunningBuild (which then drives the RunningBuild which then +# pushes through and updates the progress tree view.) +# +# TODO: Should be a method on the RunningBuild class +def event_handle_timeout (eventHandler, build): + # Consume as many messages as we can ... + event = eventHandler.getEvent() + while event: + build.handle_event (event) + event = eventHandler.getEvent() + return True + +class MainWindow (gtk.Window): + + # Callback that gets fired when the user hits a button in the + # BuildSetupDialog. + def build_dialog_box_response_cb (self, dialog, response_id): + conf = None + if (response_id == BuildSetupDialog.RESPONSE_BUILD): + dialog.update_configuration() + print dialog.configuration.machine, dialog.configuration.distro, \ + dialog.configuration.image + conf = dialog.configuration + + dialog.destroy() + + if conf: + self.manager.do_build (conf) + + def build_button_clicked_cb (self, button): + dialog = BuildSetupDialog () + + # For some unknown reason Dialog.run causes nice little deadlocks ... :-( + dialog.connect ("response", self.build_dialog_box_response_cb) + dialog.show() + + def __init__ (self): + gtk.Window.__init__ (self) + + # Pull in *just* the main vbox from the Glade XML data and then pack + # that inside the window + gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade", + root = "main_window_vbox") + vbox = gxml.get_widget ("main_window_vbox") + self.add (vbox) + + # Create the tree views for the build manager view and the progress view + self.build_manager_view = BuildManagerTreeView() + self.running_build_view = RunningBuildTreeView() + + # Grab the scrolled windows that we put the tree views into + self.results_scrolledwindow = gxml.get_widget ("results_scrolledwindow") + self.progress_scrolledwindow = gxml.get_widget ("progress_scrolledwindow") + + # Put the tree views inside ... + self.results_scrolledwindow.add (self.build_manager_view) + self.progress_scrolledwindow.add (self.running_build_view) + + # Hook up the build button... + self.build_button = gxml.get_widget ("main_toolbutton_build") + self.build_button.connect ("clicked", self.build_button_clicked_cb) + +# I'm not very happy about the current ownership of the RunningBuild. I have +# my suspicions that this object should be held by the BuildManager since we +# care about the signals in the manager + +def running_build_succeeded_cb (running_build, manager): + # Notify the manager that a build has succeeded. This is necessary as part + # of the 'hack' that we use for making the row in the model / view + # representing the ongoing build change into a row representing the + # completed build. Since we know only one build can be running a time then + # we can handle this. + + # FIXME: Refactor all this so that the RunningBuild is owned by the + # BuildManager. It can then hook onto the signals directly and drive + # interesting things it cares about. + manager.notify_build_succeeded () + print "build succeeded" + +def running_build_failed_cb (running_build, manager): + # As above + print "build failed" + manager.notify_build_failed () + +def init (server, eventHandler): + # Initialise threading... + gobject.threads_init() + gtk.gdk.threads_init() + + main_window = MainWindow () + main_window.show_all () + + # Set up the build manager stuff in general + builds_dir = os.path.join (os.getcwd(), "results") + manager = BuildManager (server, builds_dir) + main_window.build_manager_view.set_model (manager.model) + + # Do the running build setup + running_build = RunningBuild () + main_window.running_build_view.set_model (running_build.model) + running_build.connect ("build-succeeded", running_build_succeeded_cb, + manager) + running_build.connect ("build-failed", running_build_failed_cb, manager) + + # We need to save the manager into the MainWindow so that the toolbar + # button can use it. + # FIXME: Refactor ? + main_window.manager = manager + + # Use a timeout function for probing the event queue to find out if we + # have a message waiting for us. + gobject.timeout_add (200, + event_handle_timeout, + eventHandler, + running_build) + + gtk.main() diff --git a/bitbake/lib/bb/ui/uievent.py b/bitbake/lib/bb/ui/uievent.py new file mode 100644 index 000000000..36302f4da --- /dev/null +++ b/bitbake/lib/bb/ui/uievent.py @@ -0,0 +1,125 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer +# Copyright (C) 2006 - 2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +""" +Use this class to fork off a thread to recieve event callbacks from the bitbake +server and queue them for the UI to process. This process must be used to avoid +client/server deadlocks. +""" + +import socket, threading, pickle +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler + +class BBUIEventQueue: + def __init__(self, BBServer): + + self.eventQueue = [] + self.eventQueueLock = threading.Lock() + self.eventQueueNotify = threading.Event() + + self.BBServer = BBServer + + self.t = threading.Thread() + self.t.setDaemon(True) + self.t.run = self.startCallbackHandler + self.t.start() + + def getEvent(self): + + self.eventQueueLock.acquire() + + if len(self.eventQueue) == 0: + self.eventQueueLock.release() + return None + + item = self.eventQueue.pop(0) + + if len(self.eventQueue) == 0: + self.eventQueueNotify.clear() + + self.eventQueueLock.release() + return item + + def waitEvent(self, delay): + self.eventQueueNotify.wait(delay) + return self.getEvent() + + def queue_event(self, event): + self.eventQueueLock.acquire() + self.eventQueue.append(pickle.loads(event)) + self.eventQueueNotify.set() + self.eventQueueLock.release() + + def startCallbackHandler(self): + + server = UIXMLRPCServer() + self.host, self.port = server.socket.getsockname() + + server.register_function( self.system_quit, "event.quit" ) + server.register_function( self.queue_event, "event.send" ) + server.socket.settimeout(1) + + self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port) + + self.server = server + while not server.quit: + server.handle_request() + server.server_close() + + def system_quit( self ): + """ + Shut down the callback thread + """ + try: + self.BBServer.unregisterEventHandler(self.EventHandle) + except: + pass + self.server.quit = True + +class UIXMLRPCServer (SimpleXMLRPCServer): + + def __init__( self, interface = ("localhost", 0) ): + self.quit = False + SimpleXMLRPCServer.__init__( self, + interface, + requestHandler=SimpleXMLRPCRequestHandler, + logRequests=False, allow_none=True) + + def get_request(self): + while not self.quit: + try: + sock, addr = self.socket.accept() + sock.settimeout(1) + return (sock, addr) + except socket.timeout: + pass + return (None,None) + + def close_request(self, request): + if request is None: + return + SimpleXMLRPCServer.close_request(self, request) + + def process_request(self, request, client_address): + if request is None: + return + SimpleXMLRPCServer.process_request(self, request, client_address) + + diff --git a/bitbake/lib/bb/ui/uihelper.py b/bitbake/lib/bb/ui/uihelper.py new file mode 100644 index 000000000..151ffc585 --- /dev/null +++ b/bitbake/lib/bb/ui/uihelper.py @@ -0,0 +1,49 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer +# Copyright (C) 2006 - 2007 Richard Purdie +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +class BBUIHelper: + def __init__(self): + self.needUpdate = False + self.running_tasks = {} + self.failed_tasks = {} + + def eventHandler(self, event): + if isinstance(event, bb.build.TaskStarted): + self.running_tasks["%s %s\n" % (event._package, event._task)] = "" + self.needUpdate = True + if isinstance(event, bb.build.TaskSucceeded): + del self.running_tasks["%s %s\n" % (event._package, event._task)] + self.needUpdate = True + if isinstance(event, bb.build.TaskFailed): + del self.running_tasks["%s %s\n" % (event._package, event._task)] + self.failed_tasks["%s %s\n" % (event._package, event._task)] = "" + self.needUpdate = True + + # Add runqueue event handling + #if isinstance(event, bb.runqueue.runQueueTaskCompleted): + # a = 1 + #if isinstance(event, bb.runqueue.runQueueTaskStarted): + # a = 1 + #if isinstance(event, bb.runqueue.runQueueTaskFailed): + # a = 1 + #if isinstance(event, bb.runqueue.runQueueExitWait): + # a = 1 + + def getTasks(self): + return (self.running_tasks, self.failed_tasks) diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py index 3017ecfa4..5fc1463e6 100644 --- a/bitbake/lib/bb/utils.py +++ b/bitbake/lib/bb/utils.py @@ -21,8 +21,9 @@ BitBake Utility Functions digits = "0123456789" ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +separators = ".-" -import re, fcntl, os +import re, fcntl, os, types def explode_version(s): r = [] @@ -39,12 +40,15 @@ def explode_version(s): r.append(m.group(1)) s = m.group(2) continue + r.append(s[0]) s = s[1:] return r def vercmp_part(a, b): va = explode_version(a) vb = explode_version(b) + sa = False + sb = False while True: if va == []: ca = None @@ -56,6 +60,16 @@ def vercmp_part(a, b): cb = vb.pop(0) if ca == None and cb == None: return 0 + + if type(ca) is types.StringType: + sa = ca in separators + if type(cb) is types.StringType: + sb = cb in separators + if sa and not sb: + return -1 + if not sa and sb: + return 1 + if ca > cb: return 1 if ca < cb: @@ -151,7 +165,7 @@ def better_compile(text, file, realfile): # split the text into lines again body = text.split('\n') - bb.msg.error(bb.msg.domain.Util, "Error in compiling: ", realfile) + bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile) bb.msg.error(bb.msg.domain.Util, "The lines resulting into this error were:") bb.msg.error(bb.msg.domain.Util, "\t%d:%s:'%s'" % (e.lineno, e.__class__.__name__, body[e.lineno-1])) @@ -176,7 +190,7 @@ def better_exec(code, context, text, realfile): raise # print the Header of the Error Message - bb.msg.error(bb.msg.domain.Util, "Error in executing: %s" % realfile) + bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile) bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) ) # let us find the line number now -- cgit v1.2.3