mirror of
https://git.yoctoproject.org/poky
synced 2026-03-26 13:02:22 +01:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d3591db9c | ||
|
|
753f79806b | ||
|
|
50ece536ae | ||
|
|
fdcda41c5e | ||
|
|
6ceab2c76d | ||
|
|
0b8fcb95dd | ||
|
|
45526f5ecf | ||
|
|
d1fd60f69d | ||
|
|
7fa2b1c154 | ||
|
|
fd7a07b3a2 | ||
|
|
01bc47f4d4 | ||
|
|
12a3d41a24 | ||
|
|
ce4f835679 | ||
|
|
54f08d23cd | ||
|
|
8a3d0f375c | ||
|
|
0c2003f134 | ||
|
|
6e71b0a012 | ||
|
|
4b5c1c0530 | ||
|
|
171e709ae6 | ||
|
|
a8b8557e4c | ||
|
|
399e6b8008 | ||
|
|
290280b332 | ||
|
|
9e11fbf904 | ||
|
|
0f8244faba | ||
|
|
0cc23a8656 | ||
|
|
30c39cc97c | ||
|
|
261ca88596 | ||
|
|
72ddd5c202 | ||
|
|
6026999e81 | ||
|
|
c5ab4d56f9 | ||
|
|
b9d6950732 | ||
|
|
95b64df744 | ||
|
|
941855f0c0 | ||
|
|
17641a7ead | ||
|
|
c6e842d9f5 | ||
|
|
372186ff62 | ||
|
|
c718ef5b60 | ||
|
|
e160e68136 | ||
|
|
c42340603f | ||
|
|
1a0cf646cf | ||
|
|
3ed9ba33a3 | ||
|
|
88ff0a4470 | ||
|
|
7b6db199fa | ||
|
|
a28bc0d0b6 | ||
|
|
47cfaec4ea | ||
|
|
59df74164d | ||
|
|
b5419d931e | ||
|
|
1ddda942bd | ||
|
|
c115f07678 | ||
|
|
73be41e475 | ||
|
|
b96412845d | ||
|
|
f0c88f220e | ||
|
|
1a3140eaf6 | ||
|
|
e34401720d | ||
|
|
72aadcf274 | ||
|
|
411910bb98 | ||
|
|
23bae7e299 | ||
|
|
13642c439c | ||
|
|
784f9b3369 | ||
|
|
0826752c04 | ||
|
|
a168d77ea9 | ||
|
|
297c60afd8 | ||
|
|
0f49a2c359 | ||
|
|
37eec34886 | ||
|
|
2e793fe2bf | ||
|
|
2bf160150f | ||
|
|
c3a5bee36f | ||
|
|
4a5a659674 | ||
|
|
ce18dde858 | ||
|
|
e51e4870f0 | ||
|
|
440a4cfffb | ||
|
|
779288f438 | ||
|
|
c3fa1a6677 | ||
|
|
e8ea66f5ff | ||
|
|
476242adc4 | ||
|
|
e9ef9424a3 | ||
|
|
fc9c11de28 | ||
|
|
4ae9c0785e |
49
.gitignore
vendored
49
.gitignore
vendored
@@ -1,39 +1,26 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
/*.patch
|
||||
/.repo/
|
||||
/build*/
|
||||
pyshtables.py
|
||||
build/conf/local.conf
|
||||
build/conf/bblayers.conf
|
||||
build/tmp/
|
||||
build/sstate-cache
|
||||
build/pyshtables.py
|
||||
pstage/
|
||||
scripts/oe-git-proxy-socks
|
||||
scripts/poky-git-proxy-socks
|
||||
sources/
|
||||
meta-*/
|
||||
buildtools/
|
||||
!meta-skeleton
|
||||
!meta-selftest
|
||||
hob-image-*.bb
|
||||
meta-darwin
|
||||
meta-maemo
|
||||
meta-extras
|
||||
meta-m2
|
||||
meta-prvt*
|
||||
poky-autobuilder*
|
||||
*.swp
|
||||
*.orig
|
||||
*.rej
|
||||
*~
|
||||
!meta-poky
|
||||
!meta-yocto
|
||||
!meta-yocto-bsp
|
||||
!meta-yocto-imported
|
||||
/documentation/*/eclipse/
|
||||
/documentation/*/*.html
|
||||
/documentation/*/*.pdf
|
||||
/documentation/*/*.tgz
|
||||
/bitbake/doc/bitbake-user-manual/bitbake-user-manual.html
|
||||
/bitbake/doc/bitbake-user-manual/bitbake-user-manual.pdf
|
||||
/bitbake/doc/bitbake-user-manual/bitbake-user-manual.tgz
|
||||
pull-*/
|
||||
bitbake/lib/toaster/contrib/tts/backlog.txt
|
||||
bitbake/lib/toaster/contrib/tts/log/*
|
||||
bitbake/lib/toaster/contrib/tts/.cache/*
|
||||
bitbake/lib/bb/tests/runqueue-tests/bitbake-cookerdaemon.log
|
||||
_toaster_clones/
|
||||
downloads/
|
||||
sstate-cache/
|
||||
toaster.sqlite
|
||||
.vscode/
|
||||
documentation/poky-ref-manual/poky-ref-manual.html
|
||||
documentation/poky-ref-manual/poky-ref-manual.pdf
|
||||
documentation/poky-ref-manual/poky-ref-manual.tgz
|
||||
documentation/poky-ref-manual/bsp-guide.html
|
||||
documentation/poky-ref-manual/bsp-guide.pdf
|
||||
|
||||
|
||||
220
CHANGELOG
Normal file
220
CHANGELOG
Normal file
@@ -0,0 +1,220 @@
|
||||
commit fd7a07b3a2153826bedda2ef76b9a33ab2791680
|
||||
Author: Scott Garman <scott.a.garman@intel.com>
|
||||
Date: Fri Jan 21 14:15:05 2011 -0800
|
||||
|
||||
poky-extract-sdk: allow relative paths for extract-dir
|
||||
|
||||
psuedo needs a full path to its pid file, so convert
|
||||
relative extract-dir paths to full ones.
|
||||
|
||||
The symptom of this bug is receiving the following error:
|
||||
|
||||
pseudo: Couldn't open relative/path/to/var/pseudo/pseudo.pid: No such file or directory
|
||||
|
||||
This fixes [BUGID #670]
|
||||
|
||||
Signed-off-by: Scott Garman <scott.a.garman@intel.com>
|
||||
|
||||
commit 01bc47f4d47df3276b4b6c2583bcddd834fd5050
|
||||
Author: Beth Flanagan <elizabeth.flanagan@intel.com>
|
||||
Date: Wed Nov 3 17:20:00 2010 -0700
|
||||
|
||||
quilt: Fixed configure test for patch --version.
|
||||
|
||||
OpenSuSE 11.3 uses GNU patch 2.6.1.81-5b68 which breaks quilt's
|
||||
configure test for patch version.
|
||||
|
||||
Signed-off-by: Beth Flanagan <elizabeth.flanagan@intel.com>
|
||||
|
||||
commit 12a3d41a24db79ae6c0491defffcf4f4753001cf
|
||||
Author: Richard Purdie <richard.purdie@linuxfoundation.org>
|
||||
Date: Fri Jan 14 11:57:18 2011 +0000
|
||||
|
||||
image.bbclass: Use the dedicated BB_WORKERCONTEXT, not bitbake internals to detect context
|
||||
|
||||
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
|
||||
|
||||
commit ce4f8356796bc797d9156ed252a4ed638a2150d5
|
||||
Author: Richard Purdie <rpurdie@linux.intel.com>
|
||||
Date: Wed Dec 15 23:22:16 2010 +0000
|
||||
|
||||
scripts/poky-qemu: Improve tmp layout assumption
|
||||
|
||||
If someone has changed TMPDIR in local.conf to a non-standard location, the
|
||||
poky-qemu script currently doesn't handle this and assumes if BUILDDIR is set,
|
||||
$BUILDDIR/tmp will exist.
|
||||
|
||||
Its simple to check if this exists and if not, to ask bitbake where the
|
||||
directory is so this patch changes the code to do that.
|
||||
|
||||
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
|
||||
|
||||
commit 54f08d23cd7d0de6aec31f4764389ff4dab2990d
|
||||
Author: Scott Garman <scott.a.garman@intel.com>
|
||||
Date: Tue Dec 7 20:59:06 2010 -0800
|
||||
|
||||
Make poky-qemu and related scripts work with arbitrary SDK locations
|
||||
|
||||
* No longer assume SDK toolchains are installed in /opt/poky
|
||||
* [BUGFIX #568] where specifying paths to both the kernel and fs
|
||||
image caused an error due to POKY_NATIVE_SYSROOT never being
|
||||
set, triggering failure of poky-qemu-ifup/ifdown
|
||||
* Cosmetic improvements to usage() functions by using basename
|
||||
|
||||
Signed-off-by: Scott Garman <scott.a.garman@intel.com>
|
||||
|
||||
commit 8a3d0f375ce416ada1a5443e4a8e467504001beb
|
||||
Author: Scott Garman <scott.a.garman@intel.com>
|
||||
Date: Fri Nov 12 16:31:13 2010 -0800
|
||||
|
||||
poky-qemu: Fix issues when running Yocto 0.9 release images
|
||||
|
||||
This fixes two bugs with poky-qemu when it is run from a
|
||||
standalone meta-toolchain setup.
|
||||
|
||||
[BUGFIX #535] and [BUGFIX #536]
|
||||
|
||||
Signed-off-by: Scott Garman <scott.a.garman@intel.com>
|
||||
|
||||
commit 0c2003f13434c77f901a976523478d37d8aadb48
|
||||
Author: Paul Eggleton <paul.eggleton@linux.intel.com>
|
||||
Date: Thu Dec 16 10:29:50 2010 +0000
|
||||
|
||||
openssl: restore -Wall flag
|
||||
|
||||
The -Wall flag was unintentionally removed from the end of the CFLAG var in
|
||||
089612794d4d8d9c79bd2a4365d6df78371f7f40 by me. This patch puts it back in.
|
||||
|
||||
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
|
||||
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
|
||||
|
||||
commit 6e71b0a012f0676c06b7b4788d932f320fca0b74
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Wed Dec 15 14:31:21 2010 +0000
|
||||
|
||||
web-webkit: fix for make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 4b5c1c053000d297956f08949ffde7454ee33c5d
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Wed Dec 15 13:42:15 2010 +0000
|
||||
|
||||
contacts: fix for make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 171e709ae6f4b1a7640bf393f57aa787648cdc0f
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Wed Dec 15 12:58:09 2010 +0000
|
||||
|
||||
dates: fix for Make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit a8b8557e4cb34b594bb620eb276bcaf7a8e0a8e3
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Wed Dec 15 12:27:52 2010 +0000
|
||||
|
||||
owl-video-widget: fix Makefile for super strict make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 399e6b8008cb0b8cc0b75efd48dd821a6cf5a8a8
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Dec 14 18:29:43 2010 +0000
|
||||
|
||||
libowl-av: fix for Make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 290280b332570ec73301f76765b1c5f2de20a9fd
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Dec 14 17:56:53 2010 +0000
|
||||
|
||||
gst-plugins: fix for make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 9e11fbf9048b17526ca8160d82b69f386595c9a7
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Dec 14 15:39:42 2010 +0000
|
||||
|
||||
gstreamer: fix to comply with make 3.82's stricter parser
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 0f8244faba5c36c0580081c112ea27ce683af99b
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Dec 14 12:49:13 2010 +0000
|
||||
|
||||
linux-libc-headers: fix for Make 3.82
|
||||
|
||||
Fix the kernel Makefile for use with Make 3.82 by splitting mixed implicit and
|
||||
normal rules into separate rules.
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 0cc23a86562d0ce1e236ceb4a56a8f19d400192f
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Dec 14 12:21:33 2010 +0000
|
||||
|
||||
busybox: additional fixes for Make 3.82
|
||||
|
||||
There where still some mixed implicit and normal rules in the Busybox Makefile,
|
||||
Update our existing make-382.patch to split these into separate rules.
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 30c39cc97c384134661300e107d7a81f257f8034
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Fri Nov 12 16:36:54 2010 +0000
|
||||
|
||||
procps: fix for build against make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 261ca885962ba9606bcad4c5415927a79fdd7b96
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Nov 9 12:18:14 2010 +0000
|
||||
|
||||
busybox: import upstream patch for make 3.82
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 72ddd5c20246a5d5b1752b58a61ef75b4c39cc40
|
||||
Author: Joshua Lock <josh@linux.intel.com>
|
||||
Date: Tue Nov 9 12:14:28 2010 +0000
|
||||
|
||||
eglibc: fix build of eglibc-initial for make 3.82
|
||||
|
||||
Make 3.82, as shipped with Fedora 14, fixes some holes in the parser which in
|
||||
turn breaks behaviour of some Makefiles. Most notably eglibc's.
|
||||
|
||||
Signed-off-by: Joshua Lock <josh@linux.intel.com>
|
||||
|
||||
commit 6026999e81042a7f6560f9bce04390865509b235
|
||||
Author: Paul Eggleton <paul.eggleton@intel.com>
|
||||
Date: Fri Nov 19 15:03:32 2010 +0000
|
||||
|
||||
qemu: fix failure to find zlib header files during configure
|
||||
|
||||
Corrects problems during configure of qemu-native due to the BUILD_CFLAGS
|
||||
not being included when attempting to compile the test program for zlib
|
||||
within the configure script.
|
||||
|
||||
Signed-off-by: Paul Eggleton <paul.eggleton@intel.com>
|
||||
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
|
||||
|
||||
commit c5ab4d56f97a0e45b124d40c9f536541be04c201
|
||||
Author: Paul Eggleton <paul.eggleton@intel.com>
|
||||
Date: Wed Nov 17 11:37:47 2010 +0000
|
||||
|
||||
openssl-native: disable execstack flag to prevent problems with SELinux
|
||||
|
||||
The execstack flag gets set on libcrypto.so by default which causes SELinux
|
||||
to prevent it from being loaded on systems using SELinux, which includes
|
||||
Fedora. This patch disables the execstack flag. (Note: Red Hat do this in
|
||||
their openssl packaging.)
|
||||
|
||||
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
|
||||
14
LICENSE
Normal file
14
LICENSE
Normal file
@@ -0,0 +1,14 @@
|
||||
Different components of Poky are under different licenses (a mix of
|
||||
MIT and GPLv2). Please see:
|
||||
|
||||
bitbake/COPYING (GPLv2)
|
||||
meta/COPYING.MIT (MIT)
|
||||
meta-extras/COPYING.MIT (MIT)
|
||||
|
||||
which cover the components in those subdirectories. This means all
|
||||
metadata is MIT licensed unless otherwise stated. Source code included
|
||||
in tree for individual recipes is under the LICENSE stated in the .bb
|
||||
file for those software projects unless otherwise stated.
|
||||
|
||||
License information for any other files is either explicitly stated
|
||||
or defaults to GPL version 2.
|
||||
70
NOTES
Normal file
70
NOTES
Normal file
@@ -0,0 +1,70 @@
|
||||
Name: Laverne
|
||||
Version: 4.0.1
|
||||
Built from Revision: fd7a07b3a2153826bedda2ef76b9a33ab2791680
|
||||
Build Date: Jan 26 2011
|
||||
Builder: autobuilder.pokylinux.org
|
||||
|
||||
The Laverne 4.0.1 Release ensures you can use Poky Laverne on systems running
|
||||
Fedora 14 and Opensuse 11.3, fixes issues with the poky-qemu script, and fixes
|
||||
several other bugs. For the full changelog for Laverne 4.0.1 please read
|
||||
CHANGELOG.
|
||||
|
||||
Following are descriptions of fixes and known issues.
|
||||
|
||||
Fixes
|
||||
------------------------
|
||||
|
||||
* Make 3.82, as shipped with Fedora 14, included parser bug fixes that
|
||||
resulted in a much stricter parser. As a result, the Makefiles could not be
|
||||
parsed for many of the software versions shipped with Laverne. The Makefiles
|
||||
in the following recipes were fixed:
|
||||
|
||||
o eglibc
|
||||
o busybox
|
||||
o procps
|
||||
o linux-libc-headers
|
||||
o gstreamer
|
||||
o gst-plugins
|
||||
o libowl-av
|
||||
o owl-video-widget
|
||||
o dates
|
||||
o contacts
|
||||
o web-webkit
|
||||
|
||||
* The ability to build openssl-native on a system that has SELINUX enabled
|
||||
was restored. (We disabled the execstack flag at compile time.)
|
||||
|
||||
* A host-intrusion issue caused by a failure in QEMU to find zlib headers
|
||||
during configure was fixed. The issue was causing qemu-native to use the
|
||||
system zlib if it was present. If the system zlib was not present the build
|
||||
would fail.
|
||||
|
||||
* Stability and usability enhancements, which included handling relative
|
||||
filesystem paths, were made to poky-qemu scripts.
|
||||
|
||||
* The run-time remapping of package names when adding extra packages to an
|
||||
image via the IMAGE_INSTALL mechanism were fixed.
|
||||
|
||||
* The configure test in quilt for GNU patch was fixed to that it correctly
|
||||
detects the version.
|
||||
|
||||
Known Issues
|
||||
------------------------
|
||||
|
||||
* The mpc3815e-rbd and routerstationpro machines were untested and not a
|
||||
part of the official Laverne 4.0 release. These machines are still unusable
|
||||
for this Laverne 4.0.1 release.
|
||||
o mpx3815e-rdb will not boot due to a kernel/uboot issue Bug #685
|
||||
o routerstation will not boot (by default) due to incorrect boot
|
||||
parameters Bug #681
|
||||
o routerstationpro debug messages related to the ethernet driver print
|
||||
during boot Bug #679
|
||||
* Shutdown/poweroff on qemuarm does not cleanly halt the virtual machine.
|
||||
To workaround this issue use the reboot command. Using this command avoids
|
||||
a "power-cycle" and instead cleanly shuts down the VM Bug #684
|
||||
* Two "Connection Manager" icons appear in the Sato UI. This duplication has
|
||||
been fixed in master. Note that you can use either icon to launch the
|
||||
connectivity UI. Bug #683
|
||||
* The on-screen keyboard incorrectly launches in the qemumips machine. This
|
||||
issue is due to a mis-configured formfactor file Bug #682
|
||||
|
||||
123
README
123
README
@@ -1,114 +1,15 @@
|
||||
The poky repository master branch is no longer being updated.
|
||||
Poky
|
||||
====
|
||||
|
||||
You can either:
|
||||
Poky platform builder is a combined cross build system and development
|
||||
environment. It features support for building X11/Matchbox/GTK based
|
||||
filesystem images for various embedded devices and boards. It also
|
||||
supports cross-architecture application development using QEMU emulation
|
||||
and a standalone toolchain and SDK with IDE integration.
|
||||
|
||||
a) switch to individual clones of bitbake, openembedded-core, meta-yocto and yocto-docs
|
||||
|
||||
https://docs.yoctoproject.org/dev-manual/poky-manual-setup.html
|
||||
|
||||
b) use the new bitbake-setup
|
||||
|
||||
https://docs.yoctoproject.org/bitbake/bitbake-user-manual/bitbake-user-manual-environment-setup.html
|
||||
|
||||
You can find more information in our documentation: https://docs.yoctoproject.org/
|
||||
|
||||
Note that "poky" the distro setting is still available in meta-yocto as
|
||||
before and we continue to use and maintain that.
|
||||
|
||||
Long live Poky!
|
||||
|
||||
|
||||
|
||||
|
||||
Some further information on the background of this change follows. The
|
||||
details are taken from:
|
||||
https://lists.openembedded.org/g/openembedded-architecture/message/2179
|
||||
|
||||
TLDR: People have complained about the combo-layer built poky
|
||||
repository for years. It was meant to be a temporary thing, we now have
|
||||
an alternative and I'm therefore doing what I promised I'd do. Change
|
||||
is tough, things may break but this is the right point to at least try
|
||||
it.
|
||||
|
||||
I'd like to note that:
|
||||
* setting up builds with a separate oe-core and bitbake clone
|
||||
works as it always has done
|
||||
* you can change your CI just to use those two repos instead of poky
|
||||
* bitbake-setup isn't mandatory, it will just be what the yocto-
|
||||
docs presents to users
|
||||
* we don't have to stop maintaining the poky repository
|
||||
however nobody will test the new approach/code unless we do
|
||||
* we are optionally exposing sstate mirrors in the new config
|
||||
* we are also exposing config fragments to users
|
||||
* poky as a DISTRO in meta-yocto remains
|
||||
|
||||
A bit more about the history and background for those who are
|
||||
interested and then some FAQs:
|
||||
|
||||
Back around 2010 when we split up openembedded-classic and started
|
||||
developing layers, we made the artificial "poky" repository construct
|
||||
as a way to let people easily and quickly get started with the project.
|
||||
without cloning and managing multiple repositories. Layers were a new
|
||||
idea with lots of rough edges. kas didn't exist, I think repo was only
|
||||
just created and it was a different world. For us, it meant hacking up
|
||||
a quick tool, "combo-layer" and it was really a temporary solution to
|
||||
fill a gap and it was at least as functional as repo of the era. It was
|
||||
assumed we'd work it out properly in the future.
|
||||
|
||||
At developer meetings there are inevitable questions about why
|
||||
poky/combo-layer exist and few seem to actually like/support it. There
|
||||
are continual questions about why a tool doesn't exist or why we don't
|
||||
adopt one too.
|
||||
|
||||
15 years later, a bit longer than we might have thought, we are finally
|
||||
in a position where there may be a viable way forward to change.
|
||||
|
||||
It has taken us a bit of time to get to this point. I wrote the
|
||||
original description of something like bitbake-setup about 7-8 years
|
||||
ago. I shared it privately with a few people, the review feedback
|
||||
stopped me pushing it further as I simply did not have the bandwidth.
|
||||
We were fortunate to get funding from the Sovereign Tech Fund to start
|
||||
the work and whilst I'd probably prefer to avoid the issue, the time
|
||||
had come to start. Since then, Alexander Kanavin has put a lot of work
|
||||
into getting it to the point where it would be possible to switch. A
|
||||
huge thanks to him for getting this to the current point.
|
||||
|
||||
Why not use kas/submodules/repo?
|
||||
|
||||
This topic has been discussed in depth several times. Very roughly,
|
||||
these are either difficult to focus on our use cases or have specific
|
||||
designs and intent which we as a project would struggle to influence.
|
||||
We are taking significant influence from some of them but also trying
|
||||
to build something where we can benefit from tight direct integration
|
||||
with bitbake and the metadata. For example fragment support is generic
|
||||
and hopefully something other approaches can also benefit from. We want
|
||||
to provide something we can switch the projects docs and autobuilder to
|
||||
which we can control and develop as we need it to. We are not aiming to
|
||||
force anyone to switch, you can use whichever tool you want.
|
||||
|
||||
Can we not keep poky [repository master branch] around?
|
||||
|
||||
If we do that, nobody will use the new tooling and it will be a
|
||||
disaster as issues won't get resolved. We need our CI to use the same
|
||||
thing we promote to our new and experienced users. We need this new
|
||||
tooling to be usable by our experienced developers too. We have tried
|
||||
for months to get people to try it and they simply don't. Making a
|
||||
release with it won't change much either. It needs people using it and
|
||||
for that, poky has to stop being updated.
|
||||
|
||||
What happens to poky [repository]?
|
||||
|
||||
The LTS branches continue their lifetime as planned. For master, I'll
|
||||
probably put a final commit in changing to just a README which points
|
||||
people at the bitbake-setup changes and explains what happened.
|
||||
|
||||
What are the timelines? Why now?
|
||||
|
||||
If we're going to make a change, we really want this in the next LTS
|
||||
release, which is April 2026. We only have one release before that
|
||||
which is now, October 2025. We therefore need to switch now, and then
|
||||
give us time to update docs, fix issues that arise and so on and have
|
||||
it in a release cycle. Whilst it means delaying the Oct 2025 release
|
||||
slightly, that is the right thing to do in the context of the bigger
|
||||
picture.
|
||||
Poky has an extensive handbook, the source of which is contained in
|
||||
the handbook directory. For compiled HTML or pdf versions of this,
|
||||
see the Poky website http://pokylinux.org.
|
||||
|
||||
Additional information on the specifics of hardware that Poky supports
|
||||
is available in README.hardware.
|
||||
|
||||
436
README.hardware
Normal file
436
README.hardware
Normal file
@@ -0,0 +1,436 @@
|
||||
Poky Hardware Reference Guide
|
||||
=============================
|
||||
|
||||
This file gives details about using Poky with different hardware reference
|
||||
boards and consumer devices. A full list of target machines can be found by
|
||||
looking in the meta/conf/machine/ directory. If in doubt about using Poky with
|
||||
your hardware, consult the documentation for your board/device. To discuss
|
||||
support for further hardware reference boards/devices please contact OpenedHand.
|
||||
|
||||
QEMU Emulation Images (qemuarm and qemux86)
|
||||
===========================================
|
||||
|
||||
To simplify development Poky supports building images to work with the QEMU
|
||||
emulator in system emulation mode. Two architectures are currently supported,
|
||||
ARM (via qemuarm) and x86 (via qemux86). Use of the QEMU images is covered
|
||||
in the Poky Handbook.
|
||||
|
||||
Hardware Reference Boards
|
||||
=========================
|
||||
|
||||
The following boards are supported by Poky:
|
||||
|
||||
* Compulab CM-X270 (cm-x270)
|
||||
* Compulab EM-X270 (em-x270)
|
||||
* FreeScale iMX31ADS (mx31ads)
|
||||
* Marvell PXA3xx Zylonite (zylonite)
|
||||
* Logic iMX31 Lite Kit (mx31litekit)
|
||||
* Phytec phyCORE-iMX31 (mx31phy)
|
||||
|
||||
For more information see board's section below. The Poky MACHINE setting
|
||||
corresponding to the board is given in brackets.
|
||||
|
||||
Consumer Devices
|
||||
================
|
||||
|
||||
The following consumer devices are supported by Poky:
|
||||
|
||||
* FIC Neo1973 GTA01 smartphone (fic-gta01)
|
||||
* HTC Universal (htcuniversal)
|
||||
* Nokia 770/N800/N810 Internet Tablets (nokia770 and nokia800)
|
||||
* Sharp Zaurus SL-C7x0 series (c7x0)
|
||||
* Sharp Zaurus SL-C1000 (akita)
|
||||
* Sharp Zaurus SL-C3x00 series (spitz)
|
||||
|
||||
For more information see board's section below. The Poky MACHINE setting
|
||||
corresponding to the board is given in brackets.
|
||||
|
||||
Poky Boot CD (bootcdx86)
|
||||
========================
|
||||
|
||||
The Poky boot CD iso images are designed as a demonstration of the Poky
|
||||
environment and to show the versatile image formats Poky can generate. It will
|
||||
run on Pentium2 or greater PC style computers. The iso image can be
|
||||
burnt to CD and then booted from.
|
||||
|
||||
|
||||
Hardware Reference Boards
|
||||
=========================
|
||||
|
||||
Compulab CM-X270 (cm-x270)
|
||||
==========================
|
||||
|
||||
The bootloader on this board doesn't support writing jffs2 images directly to
|
||||
NAND and normally uses a proprietary kernel flash driver. To allow the use of
|
||||
jffs2 images, a two stage updating procedure is needed. Firstly, an initramfs
|
||||
is booted which contains mtd utilities and this is then used to write the main
|
||||
filesystem.
|
||||
|
||||
It is assumed the board is connected to a network where a TFTP server is
|
||||
available and that a serial terminal is available to communicate with the
|
||||
bootloader (38400, 8N1). If a DHCP server is available the device will use it
|
||||
to obtain an IP address. If not, run:
|
||||
|
||||
ARMmon > setip dhcp off
|
||||
ARMmon > setip ip 192.168.1.203
|
||||
ARMmon > setip mask 255.255.255.0
|
||||
|
||||
To reflash the kernel:
|
||||
|
||||
ARMmon > download kernel tftp zimage 192.168.1.202
|
||||
ARMmon > flash kernel
|
||||
|
||||
where zimage is the name of the kernel on the TFTP server and its IP address is
|
||||
192.168.1.202. The names of the files must be all lowercase.
|
||||
|
||||
To reflash the initrd/initramfs:
|
||||
|
||||
ARMmon > download ramdisk tftp diskimage 192.168.1.202
|
||||
ARMmon > flash ramdisk
|
||||
|
||||
where diskimage is the name of the initramfs image (a cpio.gz file).
|
||||
|
||||
To boot the initramfs:
|
||||
|
||||
ARMmon > ramdisk on
|
||||
ARMmon > bootos "console=ttyS0,38400 rdinit=/sbin/init"
|
||||
|
||||
To reflash the main image login to the system as user "root", then run:
|
||||
|
||||
# ifconfig eth0 192.168.1.203
|
||||
# tftp -g -r mainimage 192.168.1.202
|
||||
# flash_eraseall /dev/mtd1
|
||||
# nandwrite /dev/mtd1 mainimage
|
||||
|
||||
which configures the network interface with the IP address 192.168.1.203,
|
||||
downloads the "mainimage" file from the TFTP server at 192.168.1.202, erases
|
||||
the flash and then writes the new image to the flash.
|
||||
|
||||
The main image can then be booted with:
|
||||
|
||||
ARMmon > bootos "console=ttyS0,38400 root=/dev/mtdblock1 rootfstype=jffs2"
|
||||
|
||||
Note that the initramfs image is built by poky in a slightly different mode to
|
||||
normal since it uses uclibc. To generate this use a command like:
|
||||
|
||||
IMAGE_FSTYPES=cpio.gz MACHINE=cm-x270 POKYLIBC=uclibc bitbake poky-image-minimal-mtdutils
|
||||
|
||||
|
||||
Compulab EM-X270 (em-x270)
|
||||
==========================
|
||||
|
||||
Fetch the "Linux - kernel and run-time image (Angstrom)" ZIP file from the
|
||||
Compulab website. Inside the images directory of this ZIP file is another ZIP
|
||||
file called 'LiveDisk.zip'. Extract this over a cleanly formatted vfat USB flash
|
||||
drive. Replace the 'em_x270.img' file with the 'updater-em-x270.ext2' file.
|
||||
|
||||
Insert this USB disk into the supplied adapter and connect this to the
|
||||
board. Whilst holding down the the suspend button press the reset button. The
|
||||
board will now boot off the USB key and into a version of Angstrom. On the
|
||||
desktop is an icon labelled "Updater". Run this program to launch the updater
|
||||
that will flash the Poky kernel and rootfs to the board.
|
||||
|
||||
|
||||
FreeScale iMX31ADS (mx31ads)
|
||||
===========================
|
||||
|
||||
The correct serial port is the top-most female connector to the right of the
|
||||
ethernet socket.
|
||||
|
||||
For uploading data to RedBoot we are going to use tftp. In this example we
|
||||
assume that the tftpserver is on 192.168.9.1 and the board is on192.168.9.2.
|
||||
|
||||
To set the IP address, run:
|
||||
|
||||
ip_address -l 192.168.9.2/24 -h 192.168.9.1
|
||||
|
||||
To download a kernel called "zimage" from the TFTP server, run:
|
||||
|
||||
load -r -b 0x100000 zimage
|
||||
|
||||
To write the kernel to flash run:
|
||||
|
||||
fis create kernel
|
||||
|
||||
To download a rootfs jffs2 image "rootfs" from the TFTP server, run:
|
||||
|
||||
load -r -b 0x100000 rootfs
|
||||
|
||||
To write the root filesystem to flash run:
|
||||
|
||||
fis create root
|
||||
|
||||
To load and boot a kernel and rootfs from flash:
|
||||
|
||||
fis load kernel
|
||||
exec -b 0x100000 -l 0x200000 -c "noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rootfstype=jffs2 init=linuxrc ip=none"
|
||||
|
||||
To load and boot a kernel from a TFTP server with the rootfs over NFS:
|
||||
|
||||
load -r -b 0x100000 zimage
|
||||
exec -b 0x100000 -l 0x200000 -c "noinitrd console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.9.1:/mnt/nfsmx31 rw ip=192.168.9.2::192.168.9.1:255.255.255.0"
|
||||
|
||||
The instructions above are for using the (default) NOR flash on the board,
|
||||
there is also 128M of NAND flash. It is possible to install Poky to the NAND
|
||||
flash which gives more space for the rootfs and instructions for using this are
|
||||
given below. To switch to the NAND flash:
|
||||
|
||||
factive NAND
|
||||
|
||||
This will then restart RedBoot using the NAND rather than the NOR. If you
|
||||
have not used the NAND before then it is unlikely that there will be a
|
||||
partition table yet. You can get the list of partitions with 'fis list'.
|
||||
|
||||
If this shows no partitions then you can create them with:
|
||||
|
||||
fis init
|
||||
|
||||
The output of 'fis list' should now show:
|
||||
|
||||
Name FLASH addr Mem addr Length Entry point
|
||||
RedBoot 0xE0000000 0xE0000000 0x00040000 0x00000000
|
||||
FIS directory 0xE7FF4000 0xE7FF4000 0x00003000 0x00000000
|
||||
RedBoot config 0xE7FF7000 0xE7FF7000 0x00001000 0x00000000
|
||||
|
||||
Partitions for the kernel and rootfs need to be created:
|
||||
|
||||
fis create -l 0x1A0000 -e 0x00100000 kernel
|
||||
fis create -l 0x5000000 -e 0x00100000 root
|
||||
|
||||
You may now use the instructions above for flashing. However it is important
|
||||
to note that the erase block size for the NAND is different to the NOR so the
|
||||
JFFS erase size will need to be changed to 0x4000. Stardard images are built
|
||||
for NOR and you will need to build custom images for NAND.
|
||||
|
||||
You will also need to update the kernel command line to use the correct root
|
||||
filesystem. This should be '/dev/mtdblock7' if you adhere to the partitioning
|
||||
scheme shown above. If this fails then you can doublecheck against the output
|
||||
from the kernel when it evaluates the available mtd partitions.
|
||||
|
||||
|
||||
Marvell PXA3xx Zylonite (zylonite)
|
||||
==================================
|
||||
|
||||
These instructions assume the Zylonite is connected to a machine running a TFTP
|
||||
server at address 192.168.123.5 and that a serial link (38400 8N1) is available
|
||||
to access the blob bootloader. The kernel is on the TFTP server as
|
||||
"zylonite-kernel" and the root filesystem jffs2 file is "zylonite-rootfs" and
|
||||
the images are to be saved in NAND flash.
|
||||
|
||||
The following commands setup blob:
|
||||
|
||||
blob> setip client 192.168.123.4
|
||||
blob> setip server 192.168.123.5
|
||||
|
||||
To flash the kernel:
|
||||
|
||||
blob> tftp zylonite-kernel
|
||||
blob> nandwrite -j 0x80800000 0x60000 0x200000
|
||||
|
||||
To flash the rootfs:
|
||||
|
||||
blob> tftp zylonite-rootfs
|
||||
blob> nanderase -j 0x260000 0x5000000
|
||||
blob> nandwrite -j 0x80800000 0x260000 <length>
|
||||
|
||||
(where <length> is the rootfs size which will be printed by the tftp step)
|
||||
|
||||
To boot the board:
|
||||
|
||||
blob> nkernel
|
||||
blob> boot
|
||||
|
||||
|
||||
Logic iMX31 Lite Kit (mx31litekit)
|
||||
===============================
|
||||
|
||||
The easiest method to boot this board is to take an MMC/SD card and format
|
||||
the first partition as ext2, then extract the poky image onto this as root.
|
||||
Assuming the board is network connected, a TFTP server is available at
|
||||
192.168.1.33 and a serial terminal is available (115200 8N1), the following
|
||||
commands will boot a kernel called "mx31kern" from the TFTP server:
|
||||
|
||||
losh> ifconfig sm0 192.168.1.203 255.255.255.0 192.168.1.33
|
||||
losh> load raw 0x80100000 0x200000 /tftp/192.168.1.33:mx31kern
|
||||
losh> exec 0x80100000 -
|
||||
|
||||
|
||||
Phytec phyCORE-iMX31 (mx31phy)
|
||||
==============================
|
||||
|
||||
Support for this board is currently being developed. Experimental jffs2
|
||||
images and a suitable kernel are available and are known to work with the
|
||||
board.
|
||||
|
||||
|
||||
Consumer Devices
|
||||
================
|
||||
|
||||
FIC Neo1973 GTA01 smartphone (fic-gta01)
|
||||
========================================
|
||||
|
||||
To install Poky on a GTA01 smartphone you will need "dfu-util" tool
|
||||
which you can build with "bitbake dfu-util-native" command.
|
||||
|
||||
Flashing requires these steps:
|
||||
|
||||
1. Power down the device.
|
||||
2. Connect the device to the host machine via USB.
|
||||
3. Hold AUX key and press Power key. There should be a bootmenu
|
||||
on screen.
|
||||
4. Run "dfu-util -l" to check if the phone is visible on the USB bus.
|
||||
The output should look like this:
|
||||
|
||||
dfu-util - (C) 2007 by OpenMoko Inc.
|
||||
This program is Free Software and has ABSOLUTELY NO WARRANTY
|
||||
|
||||
Found Runtime: [0x1457:0x5119] devnum=19, cfg=0, intf=2, alt=0, name="USB Device Firmware Upgrade"
|
||||
|
||||
5. Flash the kernel with "dfu-util -a kernel -D uImage-2.6.21.6-moko11-r2-fic-gta01.bin"
|
||||
6. Flash rootfs with "dfu-util -a rootfs -D <image>", where <image> is the
|
||||
jffs2 image file to use as the root filesystem
|
||||
(e.g. ./tmp/deploy/images/poky-image-sato-fic-gta01.jffs2)
|
||||
|
||||
|
||||
HTC Universal (htcuniversal)
|
||||
============================
|
||||
|
||||
Note: HTC Universal support is highly experimental.
|
||||
|
||||
On the HTC Universal, entirely replacing the Windows installation is not
|
||||
supported, instead Poky is booted from an MMC/SD card from Windows. Once Poky
|
||||
has booted, Windows is no longer in memory or active but when power is removed,
|
||||
the user will be returned to windows and will need to return to Linux from
|
||||
there.
|
||||
|
||||
Once an MMC/SD card is available it is suggested its split into two partitions,
|
||||
one for a program called HaRET which lets you boot Linux from within Windows
|
||||
and the second for the rootfs. The HaRET partition should be the first partition
|
||||
on the card and be vfat formatted. It doesn't need to be large, just enough for
|
||||
HaRET and a kernel (say 5MB max). The rootfs should be ext2 and is usually the
|
||||
second partition. The first partition should be vfat so Windows recognises it
|
||||
as if it doesn't, it has been known to reformat cards.
|
||||
|
||||
On the first partition you need three files:
|
||||
|
||||
* a HaRET binary (version 0.5.1 works well and a working version
|
||||
should be part of the last Poky release)
|
||||
* a kernel renamed to "zImage"
|
||||
* a default.txt which contains:
|
||||
|
||||
set kernel "zImage"
|
||||
set mtype "855"
|
||||
set cmdline "root=/dev/mmcblk0p2 rw console=ttyS0,115200n8 console=tty0 rootdelay=5 fbcon=rotate:1"
|
||||
boot2
|
||||
|
||||
On the second parition the root file system is extracted as root. A different
|
||||
partition layout or other kernel options can be changed in the default.txt file.
|
||||
|
||||
When inserted into the device, Windows should see the card and let you browse
|
||||
its contents using File Explorer. Running the HaRET binary will present a dialog
|
||||
box (maybe after messages warning about running unsigned binaries) where you
|
||||
select OK and you should then see Poky boot. Kernel messages can be seen by
|
||||
adding psplash=false to the kernel commandline.
|
||||
|
||||
|
||||
Nokia 770/N800/N810 Internet Tablets (nokia770 and nokia800)
|
||||
============================================================
|
||||
|
||||
Note: Nokia tablet support is highly experimental.
|
||||
|
||||
The Nokia internet tablet devices are OMAP based tablet formfactor devices
|
||||
with large screens (800x480), wifi and touchscreen.
|
||||
|
||||
To flash images to these devices you need the "flasher" utility which can be
|
||||
downloaded from the http://tablets-dev.nokia.com/d3.php?f=flasher-3.0. This
|
||||
utility needs to be run as root and the usb filesystem needs to be mounted
|
||||
although most distributions will have done this for you. Once you have this
|
||||
follow these steps:
|
||||
|
||||
1. Power down the device.
|
||||
2. Connect the device to the host machine via USB
|
||||
(connecting power to the device doesn't hurt either).
|
||||
3. Run "flasher -i"
|
||||
4. Power on the device.
|
||||
5. The program should give an indication it's found
|
||||
a tablet device. If not, recheck the cables, make sure you're
|
||||
root and usbfs/usbdevfs is mounted.
|
||||
6. Run "flasher -r <image> -k <kernel> -f", where <image> is the
|
||||
jffs2 image file to use as the root filesystem
|
||||
(e.g. ./tmp/deploy/images/poky-image-sato-nokia800.jffs2)
|
||||
and <kernel> is the kernel to use
|
||||
(e.g. ./tmp/deploy/images/zImage-nokia800.bin).
|
||||
7. Run "flasher -R" to reboot the device.
|
||||
8. The device should boot into Poky.
|
||||
|
||||
The nokia800 images and kernel will run on both the N800 and N810.
|
||||
|
||||
|
||||
Sharp Zaurus SL-C7x0 series (c7x0)
|
||||
==================================
|
||||
|
||||
The Sharp Zaurus c7x0 series (SL-C700, SL-C750, SL-C760, SL-C860, SL-7500)
|
||||
are PXA25x based handheld PDAs with VGA screens. To install Poky images on
|
||||
these devices follow these steps:
|
||||
|
||||
1. Obtain an SD/MMC or CF card with a vfat or ext2 filesystem.
|
||||
2. Copy a jffs2 image file (e.g. poky-image-sato-c7x0.jffs2) onto the
|
||||
card as "initrd.bin":
|
||||
|
||||
$ cp ./tmp/deploy/images/poky-image-sato-c7x0.jffs2 /path/to/my-cf-card/initrd.bin
|
||||
|
||||
3. Copy an Linux kernel file (zImage-c7x0.bin) onto the card as
|
||||
"zImage.bin":
|
||||
|
||||
$ cp ./tmp/deploy/images/zImage-c7x0.bin /path/to/my-cf-card/zImage.bin
|
||||
|
||||
4. Copy an updater script (updater.sh.c7x0) onto the card
|
||||
as "updater.sh":
|
||||
|
||||
$ cp ./tmp/deploy/images/updater.sh.c7x0 /path/to/my-cf-card/updater.sh
|
||||
|
||||
5. Power down the Zaurus.
|
||||
6. Hold "OK" key and power on the device. An update menu should appear
|
||||
(in Japanese).
|
||||
7. Choose "Update" (item 4).
|
||||
8. The next screen will ask for the source, choose the appropriate
|
||||
card (CF or SD).
|
||||
9. Make sure AC power is connected.
|
||||
10. The next screen asks for confirmation, choose "Yes" (the left button).
|
||||
11. The update process will start, flash the files on the card onto
|
||||
the device and the device will then reboot into Poky.
|
||||
|
||||
|
||||
Sharp Zaurus SL-C1000 (akita)
|
||||
=============================
|
||||
|
||||
The Sharp Zaurus SL-C1000 is a PXA270 based device otherwise similar to the
|
||||
c7x0. To install Poky images on this device follow the instructions for
|
||||
the c7x0 but replace "c7x0" with "akita" where appropriate.
|
||||
|
||||
|
||||
Sharp Zaurus SL-C3x00 series (spitz)
|
||||
====================================
|
||||
|
||||
The Sharp Zaurus SL-C3x00 devices are PXA270 based devices similar
|
||||
to akita but with an internal microdrive. The installation procedure
|
||||
assumes a standard microdrive based device where the root (first)
|
||||
partition has been enlarged to fit the image (at least 100MB,
|
||||
400MB for the SDK).
|
||||
|
||||
The procedure is the same as for the c7x0 and akita models with the
|
||||
following differences:
|
||||
|
||||
1. Instead of a jffs2 image you need to copy a compressed tarball of the
|
||||
root fileystem (e.g. poky-image-sato-spitz.tar.gz) onto the
|
||||
card as "hdimage1.tgz":
|
||||
|
||||
$ cp ./tmp/deploy/images/poky-image-sato-spitz.tar.gz /path/to/my-cf-card/hdimage1.tgz
|
||||
|
||||
2. You additionally need to copy a special tar utility (gnu-tar) onto
|
||||
the card as "gnu-tar":
|
||||
|
||||
$ cp ./tmp/deploy/images/gnu-tar /path/to/my-cf-card/gnu-tar
|
||||
|
||||
|
||||
|
||||
10
bitbake/AUTHORS
Normal file
10
bitbake/AUTHORS
Normal file
@@ -0,0 +1,10 @@
|
||||
Tim Ansell <mithro@mithis.net>
|
||||
Phil Blundell <pb@handhelds.org>
|
||||
Seb Frankengul <seb@frankengul.org>
|
||||
Holger Freyther <zecke@handhelds.org>
|
||||
Marcin Juszkiewicz <marcin@juszkiewicz.com.pl>
|
||||
Chris Larson <kergoth@handhelds.org>
|
||||
Ulrich Luckas <luckas@musoft.de>
|
||||
Mickey Lauer <mickey@Vanille.de>
|
||||
Richard Purdie <rpurdie@rpsys.net>
|
||||
Holger Schurig <holgerschurig@gmx.de>
|
||||
339
bitbake/COPYING
Normal file
339
bitbake/COPYING
Normal file
@@ -0,0 +1,339 @@
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
317
bitbake/ChangeLog
Normal file
317
bitbake/ChangeLog
Normal file
@@ -0,0 +1,317 @@
|
||||
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 dictionary 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
|
||||
|
||||
19
bitbake/HEADER
Normal file
19
bitbake/HEADER
Normal file
@@ -0,0 +1,19 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# <one line to give the program's name and a brief idea of what it does.>
|
||||
# Copyright (C) <year> <name of author>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License 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.
|
||||
|
||||
217
bitbake/bin/bitbake
Executable file
217
bitbake/bin/bitbake
Executable file
@@ -0,0 +1,217 @@
|
||||
#!/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 os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])),
|
||||
'lib'))
|
||||
|
||||
import optparse
|
||||
import warnings
|
||||
from traceback import format_exception
|
||||
import bb
|
||||
import bb.msg
|
||||
from bb import cooker
|
||||
from bb import ui
|
||||
from bb import server
|
||||
from bb.server import none
|
||||
#from bb.server import xmlrpc
|
||||
|
||||
__version__ = "1.11.0"
|
||||
|
||||
|
||||
|
||||
#============================================================================#
|
||||
# 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)
|
||||
self.pkgs_to_build = []
|
||||
|
||||
|
||||
def print_exception(exc, value, tb):
|
||||
"""Send exception information through bb.msg"""
|
||||
bb.fatal("".join(format_exception(exc, value, tb, limit=8)))
|
||||
|
||||
sys.excepthook = print_exception
|
||||
|
||||
|
||||
_warnings_showwarning = warnings.showwarning
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Display python warning messages using bb.msg"""
|
||||
if file is not None:
|
||||
if _warnings_showwarning is not None:
|
||||
_warnings_showwarning(message, category, filename, lineno, file, line)
|
||||
else:
|
||||
s = warnings.formatwarning(message, category, filename, lineno)
|
||||
s = s.split("\n")[0]
|
||||
bb.msg.warn(None, s)
|
||||
|
||||
warnings.showwarning = _showwarning
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
#============================================================================#
|
||||
# main
|
||||
#============================================================================#
|
||||
|
||||
def main():
|
||||
return_value = 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("-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("-S", "--dump-signatures", help = "don't execute, just dump out the signature construction information",
|
||||
action = "store_true", dest = "dump_signatures", 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.extend(args[1:])
|
||||
configuration.initial_path = os.environ['PATH']
|
||||
|
||||
#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")
|
||||
|
||||
bb.utils.init_logger(bb.msg, configuration.verbose, configuration.debug,
|
||||
configuration.debug_domains)
|
||||
|
||||
# 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 = bb.cooker.BBCooker(configuration, server)
|
||||
|
||||
cooker.parseCommandLine()
|
||||
|
||||
serverinfo = server.BitbakeServerInfo(cooker.server)
|
||||
|
||||
server.BitBakeServerFork(serverinfo, cooker.serve, cooker_logfile)
|
||||
del cooker
|
||||
|
||||
# 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.
|
||||
uimodule = __import__("bb.ui", fromlist = [ui])
|
||||
ui_init = getattr(uimodule, ui).init
|
||||
except AttributeError:
|
||||
print("FATAL: Invalid user interface '%s' specified. " % ui)
|
||||
print("Valid interfaces are 'ncurses', 'depexp' or the default, 'knotty'.")
|
||||
else:
|
||||
try:
|
||||
return_value = ui_init(serverConnection.connection, serverConnection.events)
|
||||
except Exception as e:
|
||||
print("FATAL: Unable to start to '%s' UI: %s" % (ui, e))
|
||||
raise
|
||||
finally:
|
||||
serverConnection.terminate()
|
||||
|
||||
return return_value
|
||||
|
||||
if __name__ == "__main__":
|
||||
ret = main()
|
||||
sys.exit(ret)
|
||||
12
bitbake/bin/bitbake-diffsigs
Executable file
12
bitbake/bin/bitbake-diffsigs
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
|
||||
import bb.siggen
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
bb.siggen.dump_sigfile(sys.argv[1])
|
||||
117
bitbake/bin/bitbake-runtask
Executable file
117
bitbake/bin/bitbake-runtask
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
|
||||
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.")
|
||||
|
||||
class BBConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self, debug, debug_domains):
|
||||
setattr(self, "data", {})
|
||||
setattr(self, "file", [])
|
||||
setattr(self, "cmd", None)
|
||||
setattr(self, "dump_signatures", True)
|
||||
setattr(self, "debug", debug)
|
||||
setattr(self, "debug_domains", debug_domains)
|
||||
|
||||
_warnings_showwarning = warnings.showwarning
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Display python warning messages using bb.msg"""
|
||||
if file is not None:
|
||||
if _warnings_showwarning is not None:
|
||||
_warnings_showwarning(message, category, filename, lineno, file, line)
|
||||
else:
|
||||
s = warnings.formatwarning(message, category, filename, lineno)
|
||||
s = s.split("\n")[0]
|
||||
bb.msg.warn(None, s)
|
||||
|
||||
warnings.showwarning = _showwarning
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
import bb.event
|
||||
|
||||
# Need to map our I/O correctly. stdout is a pipe to the server expecting
|
||||
# events. We save this and then map stdout to stderr.
|
||||
|
||||
eventfd = os.dup(sys.stdout.fileno())
|
||||
bb.event.worker_pipe = os.fdopen(eventfd, 'w', 0)
|
||||
|
||||
# map stdout to stderr
|
||||
os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
|
||||
|
||||
# Replace those fds with our own
|
||||
#logout = data.expand("${TMPDIR}/log/stdout.%s" % os.getpid(), self.cfgData, True)
|
||||
#mkdirhier(os.path.dirname(logout))
|
||||
#newso = open("/tmp/stdout.%s" % os.getpid(), 'w')
|
||||
#os.dup2(newso.fileno(), sys.stdout.fileno())
|
||||
#os.dup2(newso.fileno(), sys.stderr.fileno())
|
||||
|
||||
# Don't read from stdin from the parent
|
||||
si = file("/dev/null", 'r')
|
||||
os.dup2(si.fileno( ), sys.stdin.fileno( ))
|
||||
|
||||
# We don't want to see signals to our parent, e.g. Ctrl+C
|
||||
os.setpgrp()
|
||||
|
||||
# Save out the PID so that the event can include it the
|
||||
# events
|
||||
bb.event.worker_pid = os.getpid()
|
||||
bb.event.useStdout = False
|
||||
|
||||
hashfile = sys.argv[1]
|
||||
buildfile = sys.argv[2]
|
||||
taskname = sys.argv[3]
|
||||
|
||||
import bb.cooker
|
||||
|
||||
p = pickle.Unpickler(file(hashfile, "rb"))
|
||||
hashdata = p.load()
|
||||
|
||||
debug = hashdata["msg-debug"]
|
||||
debug_domains = hashdata["msg-debug-domains"]
|
||||
verbose = hashdata["verbose"]
|
||||
|
||||
bb.utils.init_logger(bb.msg, verbose, debug, debug_domains)
|
||||
|
||||
cooker = bb.cooker.BBCooker(BBConfiguration(debug, debug_domains), None)
|
||||
cooker.parseConfiguration()
|
||||
|
||||
cooker.bb_cache = bb.cache.init(cooker)
|
||||
cooker.status = bb.cache.CacheData()
|
||||
|
||||
(fn, cls) = cooker.bb_cache.virtualfn2realfn(buildfile)
|
||||
buildfile = cooker.matchFile(fn)
|
||||
fn = cooker.bb_cache.realfn2virtual(buildfile, cls)
|
||||
|
||||
cooker.buildSetVars()
|
||||
|
||||
# Load data into the cache for fn and parse the loaded cache data
|
||||
the_data = cooker.bb_cache.loadDataFull(fn, cooker.get_file_appends(fn), cooker.configuration.data)
|
||||
cooker.bb_cache.setData(fn, buildfile, the_data)
|
||||
cooker.bb_cache.handle_data(fn, cooker.status)
|
||||
|
||||
if taskname.endswith("_setscene"):
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
bb.parse.siggen.set_taskdata(hashdata["hashes"], hashdata["deps"])
|
||||
|
||||
for h in hashdata["hashes"]:
|
||||
bb.data.setVar("BBHASH_%s" % h, hashdata["hashes"][h], the_data)
|
||||
for h in hashdata["deps"]:
|
||||
bb.data.setVar("BBHASHDEPS_%s" % h, hashdata["deps"][h], the_data)
|
||||
|
||||
ret = 0
|
||||
if sys.argv[4] != "True":
|
||||
ret = bb.build.exec_task(fn, taskname, the_data)
|
||||
sys.exit(ret)
|
||||
|
||||
532
bitbake/bin/bitdoc
Executable file
532
bitbake/bin/bitdoc
Executable file
@@ -0,0 +1,532 @@
|
||||
#!/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 """<table class="navigation" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2">
|
||||
<tr valign="middle">
|
||||
<td><a accesskey="g" href="index.html">Home</a></td>
|
||||
<td><a accesskey="n" href="all_groups.html">Groups</a></td>
|
||||
<td><a accesskey="u" href="all_keys.html">Keys</a></td>
|
||||
</tr></table>
|
||||
"""
|
||||
|
||||
def relatedKeys(self, item):
|
||||
"""
|
||||
Create HTML to link to foreign keys
|
||||
"""
|
||||
|
||||
if len(item.related()) == 0:
|
||||
return ""
|
||||
|
||||
txt = "<p><b>See also:</b><br>"
|
||||
txts = []
|
||||
for it in item.related():
|
||||
txts.append("""<a href="key%(it)s.html">%(it)s</a>""" % vars() )
|
||||
|
||||
return txt + ",".join(txts)
|
||||
|
||||
def groups(self, item):
|
||||
"""
|
||||
Create HTML to link to related groups
|
||||
"""
|
||||
|
||||
if len(item.groups()) == 0:
|
||||
return ""
|
||||
|
||||
|
||||
txt = "<p><b>See also:</b><br>"
|
||||
txts = []
|
||||
for group in item.groups():
|
||||
txts.append( """<a href="group%s.html">%s</a> """ % (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 """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Key %s</title></head>
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
%s
|
||||
<h2><span class="refentrytitle">%s</span></h2>
|
||||
|
||||
<div class="refsynopsisdiv">
|
||||
<h2>Synopsis</h2>
|
||||
<p>
|
||||
%s
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="refsynopsisdiv">
|
||||
<h2>Related Keys</h2>
|
||||
<p>
|
||||
%s
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="refsynopsisdiv">
|
||||
<h2>Groups</h2>
|
||||
<p>
|
||||
%s
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
""" % (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 = sorted(doc.groups())
|
||||
for group in sorted_groups:
|
||||
groups += """<a href="group%s.html">%s</a><br>""" % (group, group)
|
||||
|
||||
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Group overview</title></head>
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
%s
|
||||
<h2>Available Groups</h2>
|
||||
%s
|
||||
</body>
|
||||
""" % (self.createNavigator(), groups)
|
||||
|
||||
def createIndex(self):
|
||||
"""
|
||||
Create the index file
|
||||
"""
|
||||
|
||||
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Bitbake Documentation</title></head>
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
%s
|
||||
<h2>Documentation Entrance</h2>
|
||||
<a href="all_groups.html">All available groups</a><br>
|
||||
<a href="all_keys.html">All available keys</a><br>
|
||||
</body>
|
||||
""" % self.createNavigator()
|
||||
|
||||
def createKeysSite(self, doc):
|
||||
"""
|
||||
Create Overview of all avilable keys
|
||||
"""
|
||||
keys = ""
|
||||
sorted_keys = sorted(doc.doc_keys())
|
||||
for key in sorted_keys:
|
||||
keys += """<a href="key%s.html">%s</a><br>""" % (key, key)
|
||||
|
||||
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Key overview</title></head>
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
%s
|
||||
<h2>Available Keys</h2>
|
||||
%s
|
||||
</body>
|
||||
""" % (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 += "<h2 Description of Grozp %s</h2>" % gr
|
||||
description += _description
|
||||
|
||||
items.sort(lambda x, y:cmp(x.name(), y.name()))
|
||||
for group in items:
|
||||
groups += """<a href="key%s.html">%s</a><br>""" % (group.name(), group.name())
|
||||
|
||||
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Group %s</title></head>
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
%s
|
||||
%s
|
||||
<div class="refsynopsisdiv">
|
||||
<h2>Keys in Group %s</h2>
|
||||
<pre class="synopsis">
|
||||
%s
|
||||
</pre>
|
||||
</div>
|
||||
</body>
|
||||
""" % (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()
|
||||
1
bitbake/contrib/README
Normal file
1
bitbake/contrib/README
Normal file
@@ -0,0 +1 @@
|
||||
This directory is for additional contributed files which may be useful.
|
||||
31
bitbake/contrib/bbdev.sh
Normal file
31
bitbake/contrib/bbdev.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
# This is a shell function to be sourced into your shell or placed in your .profile,
|
||||
# which makes setting things up for BitBake a bit easier.
|
||||
#
|
||||
# The author disclaims copyright to the contents of this file and places it in the
|
||||
# public domain.
|
||||
|
||||
bbdev () {
|
||||
local BBDIR PKGDIR BUILDDIR
|
||||
if test x"$1" = "x--help"; then echo >&2 "syntax: bbdev [bbdir [pkgdir [builddir]]]"; return 1; fi
|
||||
if test x"$1" = x; then BBDIR=`pwd`; else BBDIR=$1; fi
|
||||
if test x"$2" = x; then PKGDIR=`pwd`; else PKGDIR=$2; fi
|
||||
if test x"$3" = x; then BUILDDIR=`pwd`; else BUILDDIR=$3; fi
|
||||
|
||||
BBDIR=`readlink -f $BBDIR`
|
||||
PKGDIR=`readlink -f $PKGDIR`
|
||||
BUILDDIR=`readlink -f $BUILDDIR`
|
||||
if ! (test -d $BBDIR && test -d $PKGDIR && test -d $BUILDDIR); then
|
||||
echo >&2 "syntax: bbdev [bbdir [pkgdir [builddir]]]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
PATH=$BBDIR/bin:$PATH
|
||||
BBPATH=$BBDIR
|
||||
if test x"$BBDIR" != x"$PKGDIR"; then
|
||||
BBPATH=$PKGDIR:$BBPATH
|
||||
fi
|
||||
if test x"$PKGDIR" != x"$BUILDDIR"; then
|
||||
BBPATH=$BUILDDIR:$BBPATH
|
||||
fi
|
||||
export BBPATH
|
||||
}
|
||||
4
bitbake/contrib/vim/ftdetect/bitbake.vim
Normal file
4
bitbake/contrib/vim/ftdetect/bitbake.vim
Normal file
@@ -0,0 +1,4 @@
|
||||
au BufNewFile,BufRead *.bb setfiletype bitbake
|
||||
au BufNewFile,BufRead *.bbclass setfiletype bitbake
|
||||
au BufNewFile,BufRead *.inc setfiletype bitbake
|
||||
" au BufNewFile,BufRead *.conf setfiletype bitbake
|
||||
127
bitbake/contrib/vim/syntax/bitbake.vim
Normal file
127
bitbake/contrib/vim/syntax/bitbake.vim
Normal file
@@ -0,0 +1,127 @@
|
||||
" Vim syntax file
|
||||
"
|
||||
" Copyright (C) 2004 Chris Larson <kergoth@handhelds.org>
|
||||
" This file is licensed under the MIT license, see COPYING.MIT in
|
||||
" this source distribution for the terms.
|
||||
"
|
||||
" Language: BitBake
|
||||
" Maintainer: Chris Larson <kergoth@handhelds.org>
|
||||
" Filenames: *.bb, *.bbclass
|
||||
|
||||
if version < 600
|
||||
syntax clear
|
||||
elseif exists("b:current_syntax")
|
||||
finish
|
||||
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
|
||||
syn keyword bbTodo TODO FIXME XXX contained
|
||||
syn match bbDelimiter "[(){}=]" contained
|
||||
syn match bbQuote /['"]/ contained
|
||||
syn match bbArrayBrackets "[\[\]]" contained
|
||||
|
||||
|
||||
" BitBake strings
|
||||
|
||||
syn match bbContinue "\\$"
|
||||
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 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
|
||||
|
||||
" BitBake variable metadata flags
|
||||
syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag
|
||||
syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(=\)\@=" keepend excludenl contained contains=bbIdentifier nextgroup=bbVarEq
|
||||
"syn match bbVarFlagFlag "\[\([a-zA-Z0-9\-_\.]\+\)\]\s*\(=\)\@=" contains=bbIdentifier nextgroup=bbVarEq
|
||||
|
||||
|
||||
" Functions!
|
||||
syn match bbFunction "\h\w*" display contained
|
||||
|
||||
|
||||
" BitBake python metadata
|
||||
|
||||
syn keyword bbPythonFlag python contained nextgroup=bbFunction
|
||||
syn match bbPythonFuncDef "^\(python\s\+\)\(\w\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPythonFlag,bbFunction,bbDelimiter nextgroup=bbPythonFuncRegion skipwhite
|
||||
syn region bbPythonFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@python
|
||||
"hi def link bbPythonFuncRegion Comment
|
||||
|
||||
|
||||
" BitBake shell metadata
|
||||
syn include @shell syntax/sh.vim
|
||||
if exists("b:current_syntax")
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
|
||||
syn keyword bbFakerootFlag fakeroot contained nextgroup=bbFunction
|
||||
syn match bbShellFuncDef "^\(fakeroot\s*\)\?\(\w\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbFakerootFlag,bbFunction,bbDelimiter nextgroup=bbShellFuncRegion skipwhite
|
||||
syn region bbShellFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@shell
|
||||
"hi def link bbShellFuncRegion Comment
|
||||
|
||||
|
||||
" BitBake 'def'd python functions
|
||||
syn keyword bbDef def contained
|
||||
syn region bbDefRegion start='^def\s\+\w\+\s*([^)]*)\s*:\s*$' end='^\(\s\|$\)\@!' contains=@python
|
||||
|
||||
|
||||
" BitBake statements
|
||||
syn keyword bbStatement include inherit require addtask addhandler EXPORT_FUNCTIONS display contained
|
||||
syn match bbStatementLine "^\(include\|inherit\|require\|addtask\|addhandler\|EXPORT_FUNCTIONS\)\s\+" contains=bbStatement nextgroup=bbStatementRest
|
||||
syn match bbStatementRest ".*$" contained contains=bbString,bbVarDeref
|
||||
|
||||
" Highlight
|
||||
"
|
||||
hi def link bbArrayBrackets Statement
|
||||
hi def link bbUnmatched Error
|
||||
hi def link bbContinue Special
|
||||
hi def link bbDef Statement
|
||||
hi def link bbPythonFlag Type
|
||||
hi def link bbExportFlag Type
|
||||
hi def link bbFakerootFlag Type
|
||||
hi def link bbStatement Statement
|
||||
hi def link bbString String
|
||||
hi def link bbTodo Todo
|
||||
hi def link bbComment Comment
|
||||
hi def link bbOperator Operator
|
||||
hi def link bbError Error
|
||||
hi def link bbFunction Function
|
||||
hi def link bbDelimiter Delimiter
|
||||
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"
|
||||
339
bitbake/doc/COPYING.GPL
Normal file
339
bitbake/doc/COPYING.GPL
Normal file
@@ -0,0 +1,339 @@
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
17
bitbake/doc/COPYING.MIT
Normal file
17
bitbake/doc/COPYING.MIT
Normal file
@@ -0,0 +1,17 @@
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
121
bitbake/doc/bitbake.1
Normal file
121
bitbake/doc/bitbake.1
Normal file
@@ -0,0 +1,121 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH BITBAKE 1 "November 19, 2006"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
.\" .nh disable hyphenation
|
||||
.\" .hy enable hyphenation
|
||||
.\" .ad l left justify
|
||||
.\" .ad b justify to both left and right margins
|
||||
.\" .nf disable filling
|
||||
.\" .fi enable filling
|
||||
.\" .br insert line break
|
||||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.SH NAME
|
||||
BitBake \- simple tool for the execution of tasks
|
||||
.SH SYNOPSIS
|
||||
.B bitbake
|
||||
.RI [ options ] " packagenames"
|
||||
.br
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the
|
||||
.B bitbake
|
||||
command.
|
||||
.PP
|
||||
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
|
||||
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
|
||||
.\" respectively.
|
||||
\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 separated list of files to
|
||||
be executed. BBFILES does support wildcards.
|
||||
.br
|
||||
Default BBFILES are the .bb files in the current directory.
|
||||
.SH OPTIONS
|
||||
This program follow the usual GNU command line syntax, with long
|
||||
options starting with two dashes (`-').
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Show summary of options.
|
||||
.TP
|
||||
.B \-\-version
|
||||
Show version of program.
|
||||
.TP
|
||||
.B \-bBUILDFILE, \-\-buildfile=BUILDFILE
|
||||
execute the task against this .bb file, rather than a package from BBFILES.
|
||||
.TP
|
||||
.B \-k, \-\-continue
|
||||
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.
|
||||
.TP
|
||||
.B \-a, \-\-tryaltconfigs
|
||||
continue with builds by trying to use alternative providers where possible.
|
||||
.TP
|
||||
.B \-f, \-\-force
|
||||
force run of specified cmd, regardless of stamp status
|
||||
.TP
|
||||
.B \-i, \-\-interactive
|
||||
drop into the interactive mode also called the BitBake shell.
|
||||
.TP
|
||||
.B \-cCMD, \-\-cmd=CMD
|
||||
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 task is defined and will show
|
||||
available tasks.
|
||||
.TP
|
||||
.B \-rFILE, \-\-read=FILE
|
||||
read the specified file before bitbake.conf
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
output more chit-chat to the terminal
|
||||
.TP
|
||||
.B \-D, \-\-debug
|
||||
Increase the debug level. You can specify this more than once.
|
||||
.TP
|
||||
.B \-n, \-\-dry-run
|
||||
don't execute, just go through the motions
|
||||
.TP
|
||||
.B \-p, \-\-parse-only
|
||||
quit after parsing the BB files (developers only)
|
||||
.TP
|
||||
.B \-d, \-\-disable-psyco
|
||||
disable using the psyco just-in-time compiler (not recommended)
|
||||
.TP
|
||||
.B \-s, \-\-show-versions
|
||||
show current and preferred versions of all packages
|
||||
.TP
|
||||
.B \-e, \-\-environment
|
||||
show the global or per-package environment (this is what used to be bbread)
|
||||
.TP
|
||||
.B \-g, \-\-graphviz
|
||||
emit the dependency trees of the specified packages in the dot syntax
|
||||
.TP
|
||||
.B \-IIGNORED\_DOT\_DEPS, \-\-ignore-deps=IGNORED_DOT_DEPS
|
||||
Stop processing at the given list of dependencies when generating dependency
|
||||
graphs. This can help to make the graph more appealing
|
||||
.TP
|
||||
.B \-lDEBUG_DOMAINS, \-\-log-domains=DEBUG_DOMAINS
|
||||
Show debug logging for the specified logging domains
|
||||
.TP
|
||||
.B \-P, \-\-profile
|
||||
profile the command and print a report
|
||||
.TP
|
||||
|
||||
.SH AUTHORS
|
||||
BitBake was written by
|
||||
Phil Blundell,
|
||||
Holger Freyther,
|
||||
Chris Larson,
|
||||
Mickey Lauer,
|
||||
Richard Purdie,
|
||||
Holger Schurig
|
||||
.PP
|
||||
This manual page was written by Marcin Juszkiewicz <marcin@hrw.one.pl>
|
||||
for the Debian project (but may be used by others).
|
||||
56
bitbake/doc/manual/Makefile
Normal file
56
bitbake/doc/manual/Makefile
Normal file
@@ -0,0 +1,56 @@
|
||||
topdir = .
|
||||
manual = $(topdir)/usermanual.xml
|
||||
# types = pdf txt rtf ps xhtml html man tex texi dvi
|
||||
# types = pdf txt
|
||||
types = $(xmltotypes) $(htmltypes)
|
||||
xmltotypes = pdf txt
|
||||
htmltypes = html xhtml
|
||||
htmlxsl = $(if $(filter $@,$(foreach type,$(htmltypes),$(type)-nochunks)),http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl,http://docbook.sourceforge.net/release/xsl/current/$@/chunk.xsl)
|
||||
htmlcssfile = docbook.css
|
||||
htmlcss = $(topdir)/html.css
|
||||
# htmlcssfile =
|
||||
# htmlcss =
|
||||
cleanfiles = $(foreach i,$(types),$(topdir)/$(i))
|
||||
|
||||
ifdef DEBUG
|
||||
define command
|
||||
$(1)
|
||||
endef
|
||||
else
|
||||
define command
|
||||
@echo $(2) $(3) $(4)
|
||||
@$(1) >/dev/null
|
||||
endef
|
||||
endif
|
||||
|
||||
all: $(types)
|
||||
|
||||
lint: $(manual) FORCE
|
||||
$(call command,xmllint --xinclude --postvalid --noout $(manual),XMLLINT $(manual))
|
||||
|
||||
$(types) $(foreach type,$(htmltypes),$(type)-nochunks): lint FORCE
|
||||
|
||||
$(foreach type,$(htmltypes),$(type)-nochunks): $(if $(htmlcss),$(htmlcss)) $(manual)
|
||||
@mkdir -p $@
|
||||
ifdef htmlcss
|
||||
$(call command,install -m 0644 $(htmlcss) $@/$(htmlcssfile),CP $(htmlcss) $@/$(htmlcssfile))
|
||||
endif
|
||||
$(call command,xsltproc --stringparam base.dir $@/ $(if $(htmlcssfile),--stringparam html.stylesheet $(htmlcssfile)) $(htmlxsl) $(manual) > $@/index.$(patsubst %-nochunks,%,$@),XSLTPROC $@ $(manual))
|
||||
|
||||
$(htmltypes): $(if $(htmlcss),$(htmlcss)) $(manual)
|
||||
@mkdir -p $@
|
||||
ifdef htmlcss
|
||||
$(call command,install -m 0644 $(htmlcss) $@/$(htmlcssfile),CP $(htmlcss) $@/$(htmlcssfile))
|
||||
endif
|
||||
$(call command,xsltproc --stringparam base.dir $@/ $(if $(htmlcssfile),--stringparam html.stylesheet $(htmlcssfile)) $(htmlxsl) $(manual),XSLTPROC $@ $(manual))
|
||||
|
||||
$(xmltotypes): $(manual)
|
||||
$(call command,xmlto --extensions -o $(topdir)/$@ $@ $(manual),XMLTO $@ $(manual))
|
||||
|
||||
clean:
|
||||
rm -rf $(cleanfiles)
|
||||
|
||||
$(foreach i,$(types) $(foreach type,$(htmltypes),$(type)-nochunks),clean-$(i)):
|
||||
rm -rf $(patsubst clean-%,%,$@)
|
||||
|
||||
FORCE:
|
||||
281
bitbake/doc/manual/html.css
Normal file
281
bitbake/doc/manual/html.css
Normal file
@@ -0,0 +1,281 @@
|
||||
/* Feuille de style DocBook du projet Traduc.org */
|
||||
/* DocBook CSS stylesheet of the Traduc.org project */
|
||||
|
||||
/* (c) Jean-Philippe Gu<47>rard - 14 ao<61>t 2004 */
|
||||
/* (c) Jean-Philippe Gu<47>rard - 14 August 2004 */
|
||||
|
||||
/* Cette feuille de style est libre, vous pouvez la */
|
||||
/* redistribuer et la modifier selon les termes de la Licence */
|
||||
/* Art Libre. Vous trouverez un exemplaire de cette Licence sur */
|
||||
/* http://tigreraye.org/Petit-guide-du-traducteur.html#licence-art-libre */
|
||||
|
||||
/* This work of art is free, you can redistribute it and/or */
|
||||
/* modify it according to terms of the Free Art license. You */
|
||||
/* will find a specimen of this license on the Copyleft */
|
||||
/* Attitude web site: http://artlibre.org as well as on other */
|
||||
/* sites. */
|
||||
/* Please note that the French version of this licence as shown */
|
||||
/* on http://tigreraye.org/Petit-guide-du-traducteur.html#licence-art-libre */
|
||||
/* is only official licence of this document. The English */
|
||||
/* is only provided to help you understand this licence. */
|
||||
|
||||
/* La derni<6E>re version de cette feuille de style est toujours */
|
||||
/* disponible sur<75>: http://tigreraye.org/style.css */
|
||||
/* Elle est <20>galement disponible sur<75>: */
|
||||
/* http://www.traduc.org/docs/HOWTO/lecture/style.css */
|
||||
|
||||
/* The latest version of this stylesheet is available from: */
|
||||
/* http://tigreraye.org/style.css */
|
||||
/* It is also available on: */
|
||||
/* http://www.traduc.org/docs/HOWTO/lecture/style.css */
|
||||
|
||||
/* N'h<>sitez pas <20> envoyer vos commentaires et corrections <20> */
|
||||
/* Jean-Philippe Gu<47>rard <jean-philippe.guerard@tigreraye.org> */
|
||||
|
||||
/* Please send feedback and bug reports to */
|
||||
/* Jean-Philippe Gu<47>rard <jean-philippe.guerard@tigreraye.org> */
|
||||
|
||||
/* $Id: style.css,v 1.14 2004/09/10 20:12:09 fevrier Exp fevrier $ */
|
||||
|
||||
/* Pr<50>sentation g<>n<EFBFBD>rale du document */
|
||||
/* Overall document presentation */
|
||||
|
||||
body {
|
||||
/*
|
||||
font-family: Apolline, "URW Palladio L", Garamond, jGaramond,
|
||||
"Bitstream Cyberbit", "Palatino Linotype", serif;
|
||||
*/
|
||||
margin: 7%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* Taille du texte */
|
||||
/* Text size */
|
||||
|
||||
* { font-size: 100%; }
|
||||
|
||||
/* Gestion des textes mis en relief imbriqu<71>s */
|
||||
/* Embedded emphasis */
|
||||
|
||||
em { font-style: italic; }
|
||||
em em { font-style: normal; }
|
||||
em em em { font-style: italic; }
|
||||
|
||||
/* Titres */
|
||||
/* Titles */
|
||||
|
||||
h1 { font-size: 200%; font-weight: 900; }
|
||||
h2 { font-size: 160%; font-weight: 900; }
|
||||
h3 { font-size: 130%; font-weight: bold; }
|
||||
h4 { font-size: 115%; font-weight: bold; }
|
||||
h5 { font-size: 108%; font-weight: bold; }
|
||||
h6 { font-weight: bold; }
|
||||
|
||||
/* Nom de famille en petites majuscules (uniquement en fran<61>ais) */
|
||||
/* Last names in small caps (for French only) */
|
||||
|
||||
*[class~="surname"]:lang(fr) { font-variant: small-caps; }
|
||||
|
||||
/* Blocs de citation */
|
||||
/* Quotation blocs */
|
||||
|
||||
div[class~="blockquote"] {
|
||||
border: solid 2px #AAA;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div[class~="blockquote"] > table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Blocs lit<69>raux<75>: fond gris clair */
|
||||
/* Literal blocs: light gray background */
|
||||
|
||||
*[class~="literallayout"] {
|
||||
background: #f0f0f0;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/* Programmes et captures texte<74>: fond bleu clair */
|
||||
/* Listing and text screen snapshots: light blue background */
|
||||
|
||||
*[class~="programlisting"], *[class~="screen"] {
|
||||
background: #f0f0ff;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/* Les textes <20> remplacer sont surlign<67>s en vert p<>le */
|
||||
/* Replaceable text in highlighted in pale green */
|
||||
|
||||
*[class~="replaceable"] {
|
||||
background-color: #98fb98;
|
||||
font-style: normal; }
|
||||
|
||||
/* Tables<65>: fonds gris clair & bords simples */
|
||||
/* Tables: light gray background and solid borders */
|
||||
|
||||
*[class~="table"] *[class~="title"] { width:100%; border: 0px; }
|
||||
|
||||
table {
|
||||
border: 1px solid #aaa;
|
||||
border-collapse: collapse;
|
||||
padding: 2px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/* Listes simples en style table */
|
||||
/* Simples lists in table presentation */
|
||||
|
||||
table[class~="simplelist"] {
|
||||
background-color: #F0F0F0;
|
||||
margin: 5px;
|
||||
border: solid 1px #AAA;
|
||||
}
|
||||
|
||||
table[class~="simplelist"] td {
|
||||
border: solid 1px #AAA;
|
||||
}
|
||||
|
||||
/* Les tables */
|
||||
/* Tables */
|
||||
|
||||
*[class~="table"] table {
|
||||
background-color: #F0F0F0;
|
||||
border: solid 1px #AAA;
|
||||
}
|
||||
*[class~="informaltable"] table { background-color: #F0F0F0; }
|
||||
|
||||
th,td {
|
||||
vertical-align: baseline;
|
||||
text-align: left;
|
||||
padding: 0.1em 0.3em;
|
||||
empty-cells: show;
|
||||
}
|
||||
|
||||
/* Alignement des colonnes */
|
||||
/* Colunms alignment */
|
||||
|
||||
td[align=center] , th[align=center] { text-align: center; }
|
||||
td[align=right] , th[align=right] { text-align: right; }
|
||||
td[align=left] , th[align=left] { text-align: left; }
|
||||
td[align=justify] , th[align=justify] { text-align: justify; }
|
||||
|
||||
/* Pas de marge autour des images */
|
||||
/* No inside margins for images */
|
||||
|
||||
img { border: 0; }
|
||||
|
||||
/* Les liens ne sont pas soulign<67>s */
|
||||
/* No underlines for links */
|
||||
|
||||
:link , :visited , :active { text-decoration: none; }
|
||||
|
||||
/* Prudence<63>: cadre jaune et fond jaune clair */
|
||||
/* Caution: yellow border and light yellow background */
|
||||
|
||||
*[class~="caution"] {
|
||||
border: solid 2px yellow;
|
||||
background-color: #ffffe0;
|
||||
padding: 1em 6px 1em ;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
*[class~="caution"] th {
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
*[class~="caution"] table {
|
||||
background-color: #ffffe0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Note importante<74>: cadre jaune et fond jaune clair */
|
||||
/* Important: yellow border and light yellow background */
|
||||
|
||||
*[class~="important"] {
|
||||
border: solid 2px yellow;
|
||||
background-color: #ffffe0;
|
||||
padding: 1em 6px 1em;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
*[class~="important"] th {
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
*[class~="important"] table {
|
||||
background-color: #ffffe0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Mise en <20>vidence<63>: texte l<>g<EFBFBD>rement plus grand */
|
||||
/* Highlights: slightly larger texts */
|
||||
|
||||
*[class~="highlights"] {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
/* Note<74>: cadre bleu et fond bleu clair */
|
||||
/* Notes: blue border and light blue background */
|
||||
|
||||
*[class~="note"] {
|
||||
border: solid 2px #7099C5;
|
||||
background-color: #f0f0ff;
|
||||
padding: 1em 6px 1em ;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
*[class~="note"] th {
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
*[class~="note"] table {
|
||||
background-color: #f0f0ff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Astuce<63>: cadre vert et fond vert clair */
|
||||
/* Tip: green border and light green background */
|
||||
|
||||
*[class~="tip"] {
|
||||
border: solid 2px #00ff00;
|
||||
background-color: #f0ffff;
|
||||
padding: 1em 6px 1em ;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
*[class~="tip"] th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
*[class~="tip"] table {
|
||||
background-color: #f0ffff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Avertissement<6E>: cadre rouge et fond rouge clair */
|
||||
/* Warning: red border and light red background */
|
||||
|
||||
*[class~="warning"] {
|
||||
border: solid 2px #ff0000;
|
||||
background-color: #fff0f0;
|
||||
padding: 1em 6px 1em ;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
*[class~="warning"] th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
*[class~="warning"] table {
|
||||
background-color: #fff0f0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Fin */
|
||||
/* The End */
|
||||
|
||||
534
bitbake/doc/manual/usermanual.xml
Normal file
534
bitbake/doc/manual/usermanual.xml
Normal file
@@ -0,0 +1,534 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
ex:ts=4:sw=4:sts=4:et
|
||||
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
-->
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<book>
|
||||
<bookinfo>
|
||||
<title>BitBake User Manual</title>
|
||||
<authorgroup>
|
||||
<corpauthor>BitBake Team</corpauthor>
|
||||
</authorgroup>
|
||||
<copyright>
|
||||
<year>2004, 2005, 2006</year>
|
||||
<holder>Chris Larson</holder>
|
||||
<holder>Phil Blundell</holder>
|
||||
</copyright>
|
||||
<legalnotice>
|
||||
<para>This work is licensed under the Creative Commons Attribution License. To view a copy of this license, visit <ulink url="http://creativecommons.org/licenses/by/2.5/">http://creativecommons.org/licenses/by/2.5/</ulink> or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.</para>
|
||||
</legalnotice>
|
||||
</bookinfo>
|
||||
<chapter>
|
||||
<title>Introduction</title>
|
||||
<section>
|
||||
<title>Overview</title>
|
||||
<para>BitBake is, at its simplest, a tool for executing
|
||||
tasks and managing metadata. As such, its similarities to GNU make and other
|
||||
build tools are readily apparent. It was inspired by Portage, the package management system used by the Gentoo Linux distribution. BitBake is the basis of the <ulink url="http://www.openembedded.org/">OpenEmbedded</ulink> project, which is being used to build and maintain a number of embedded Linux distributions, including OpenZaurus and Familiar.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Background and Goals</title>
|
||||
<para>Prior to BitBake, no other build tool adequately met
|
||||
the needs of an aspiring embedded Linux distribution. All of the
|
||||
buildsystems used by traditional desktop Linux distributions lacked
|
||||
important functionality, and none of the ad-hoc
|
||||
<emphasis>buildroot</emphasis> systems, prevalent in the
|
||||
embedded space, were scalable or maintainable.</para>
|
||||
|
||||
<para>Some important goals for BitBake were:
|
||||
<itemizedlist>
|
||||
<listitem><para>Handle crosscompilation.</para></listitem>
|
||||
<listitem><para>Handle interpackage dependencies (build time on target architecture, build time on native architecture, and runtime).</para></listitem>
|
||||
<listitem><para>Support running any number of tasks within a given package, including, but not limited to, fetching upstream sources, unpacking them, patching them, configuring them, et cetera.</para></listitem>
|
||||
<listitem><para>Must be linux distribution agnostic (both build and target).</para></listitem>
|
||||
<listitem><para>Must be architecture agnostic</para></listitem>
|
||||
<listitem><para>Must support multiple build and target operating systems (including cygwin, the BSDs, etc).</para></listitem>
|
||||
<listitem><para>Must be able to be self contained, rather than tightly integrated into the build machine's root filesystem.</para></listitem>
|
||||
<listitem><para>There must be a way to handle conditional metadata (on target architecture, operating system, distribution, machine).</para></listitem>
|
||||
<listitem><para>It must be easy for the person using the tools to supply their own local metadata and packages to operate against.</para></listitem>
|
||||
<listitem><para>Must make it easy to collaborate
|
||||
between multiple projects using BitBake for their
|
||||
builds.</para></listitem>
|
||||
<listitem><para>Should provide an inheritance mechanism to
|
||||
share common metadata between many packages.</para></listitem>
|
||||
<listitem><para>Et cetera...</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>BitBake satisfies all these and many more. Flexibility and power have always been the priorities. It is highly extensible, supporting embedded Python code and execution of any arbitrary tasks.</para>
|
||||
</section>
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Metadata</title>
|
||||
<section>
|
||||
<title>Description</title>
|
||||
<itemizedlist>
|
||||
<para>BitBake metadata can be classified into 3 major areas:</para>
|
||||
<listitem>
|
||||
<para>Configuration Files</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>.bb Files</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Classes</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>What follows are a large number of examples of BitBake metadata. Any syntax which isn't supported in any of the aforementioned areas will be documented as such.</para>
|
||||
<section>
|
||||
<title>Basic variable setting</title>
|
||||
<para><screen><varname>VARIABLE</varname> = "value"</screen></para>
|
||||
<para>In this example, <varname>VARIABLE</varname> is <literal>value</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Variable expansion</title>
|
||||
<para>BitBake supports variables referencing one another's contents using a syntax which is similar to shell scripting</para>
|
||||
<para><screen><varname>A</varname> = "aval"
|
||||
<varname>B</varname> = "pre${A}post"</screen></para>
|
||||
<para>This results in <varname>A</varname> containing <literal>aval</literal> and <varname>B</varname> containing <literal>preavalpost</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Setting a default value (?=)</title>
|
||||
<para><screen><varname>A</varname> ?= "aval"</screen></para>
|
||||
<para>If <varname>A</varname> is set before the above is called, it will retain it's previous value. If <varname>A</varname> is unset prior to the above call, <varname>A</varname> will be set to <literal>aval</literal>. Note that this assignment is immediate, so if there are multiple ?= assignments to a single variable, the first of those will be used.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Setting a default value (??=)</title>
|
||||
<para><screen><varname>A</varname> ??= "somevalue"</screen></para>
|
||||
<para><screen><varname>A</varname> ??= "someothervalue"</screen></para>
|
||||
<para>If <varname>A</varname> is set before the above, it will retain that value. If <varname>A</varname> is unset prior to the above, <varname>A</varname> will be set to <literal>someothervalue</literal>. This is a lazy version of ??=, in that the assignment does not occur until the end of the parsing process, so that the last, rather than the first, ??= assignment to a given variable will be used.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Immediate variable expansion (:=)</title>
|
||||
<para>:= results in a variable's contents being expanded immediately, rather than when the variable is actually used.</para>
|
||||
<para><screen><varname>T</varname> = "123"
|
||||
<varname>A</varname> := "${B} ${A} test ${T}"
|
||||
<varname>T</varname> = "456"
|
||||
<varname>B</varname> = "${T} bval"
|
||||
|
||||
<varname>C</varname> = "cval"
|
||||
<varname>C</varname> := "${C}append"</screen></para>
|
||||
<para>In that example, <varname>A</varname> would contain <literal> test 123</literal>, <varname>B</varname> would contain <literal>456 bval</literal>, and <varname>C</varname> would be <literal>cvalappend</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Appending (+=) and prepending (=+)</title>
|
||||
<para><screen><varname>B</varname> = "bval"
|
||||
<varname>B</varname> += "additionaldata"
|
||||
<varname>C</varname> = "cval"
|
||||
<varname>C</varname> =+ "test"</screen></para>
|
||||
<para>In this example, <varname>B</varname> is now <literal>bval additionaldata</literal> and <varname>C</varname> is <literal>test cval</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Appending (.=) and prepending (=.) without spaces</title>
|
||||
<para><screen><varname>B</varname> = "bval"
|
||||
<varname>B</varname> .= "additionaldata"
|
||||
<varname>C</varname> = "cval"
|
||||
<varname>C</varname> =. "test"</screen></para>
|
||||
<para>In this example, <varname>B</varname> is now <literal>bvaladditionaldata</literal> and <varname>C</varname> is <literal>testcval</literal>. In contrast to the above Appending and Prepending operators no additional space
|
||||
will be introduced.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Conditional metadata set</title>
|
||||
<para>OVERRIDES is a <quote>:</quote> separated variable containing each item you want to satisfy conditions. So, if you have a variable which is conditional on <quote>arm</quote>, and <quote>arm</quote> is in OVERRIDES, then the <quote>arm</quote> specific version of the variable is used rather than the non-conditional version. Example:</para>
|
||||
<para><screen><varname>OVERRIDES</varname> = "architecture:os:machine"
|
||||
<varname>TEST</varname> = "defaultvalue"
|
||||
<varname>TEST_os</varname> = "osspecificvalue"
|
||||
<varname>TEST_condnotinoverrides</varname> = "othercondvalue"</screen></para>
|
||||
<para>In this example, <varname>TEST</varname> would be <literal>osspecificvalue</literal>, due to the condition <quote>os</quote> being in <varname>OVERRIDES</varname>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Conditional appending</title>
|
||||
<para>BitBake also supports appending and prepending to variables based on whether something is in OVERRIDES. Example:</para>
|
||||
<para><screen><varname>DEPENDS</varname> = "glibc ncurses"
|
||||
<varname>OVERRIDES</varname> = "machine:local"
|
||||
<varname>DEPENDS_append_machine</varname> = " libmad"</screen></para>
|
||||
<para>In this example, <varname>DEPENDS</varname> is set to <literal>glibc ncurses libmad</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Inclusion</title>
|
||||
<para>Next, there is the <literal>include</literal> directive, which causes BitBake to parse in whatever file you specify, and insert it at that location, which is not unlike <command>make</command>. However, if the path specified on the <literal>include</literal> line is a relative path, BitBake will locate the first one it can find within <envar>BBPATH</envar>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Requiring Inclusion</title>
|
||||
<para>In contrast to the <literal>include</literal> directive, <literal>require</literal> will
|
||||
raise an ParseError if the to be included file can not be found. Otherwise it will behave just like the <literal>
|
||||
include</literal> directive.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Python variable expansion</title>
|
||||
<para><screen><varname>DATE</varname> = "${@time.strftime('%Y%m%d',time.gmtime())}"</screen></para>
|
||||
<para>This would result in the <varname>DATE</varname> variable containing today's date.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Defining executable metadata</title>
|
||||
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
|
||||
<para><screen>do_mytask () {
|
||||
echo "Hello, world!"
|
||||
}</screen></para>
|
||||
<para>This is essentially identical to setting a variable, except that this variable happens to be executable shell code.</para>
|
||||
<para><screen>python do_printdate () {
|
||||
import time
|
||||
print time.strftime('%Y%m%d', time.gmtime())
|
||||
}</screen></para>
|
||||
<para>This is the similar to the previous, but flags it as python so that BitBake knows it is python code.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Defining python functions into the global python namespace</title>
|
||||
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
|
||||
<para><screen>def get_depends(bb, d):
|
||||
if bb.data.getVar('SOMECONDITION', d, True):
|
||||
return "dependencywithcond"
|
||||
else:
|
||||
return "dependency"
|
||||
|
||||
<varname>SOMECONDITION</varname> = "1"
|
||||
<varname>DEPENDS</varname> = "${@get_depends(bb, d)}"</screen></para>
|
||||
<para>This would result in <varname>DEPENDS</varname> containing <literal>dependencywithcond</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Variable Flags</title>
|
||||
<para>Variables can have associated flags which provide a way of tagging extra information onto a variable. Several flags are used internally by bitbake but they can be used externally too if needed. The standard operations mentioned above also work on flags.</para>
|
||||
<para><screen><varname>VARIABLE</varname>[<varname>SOMEFLAG</varname>] = "value"</screen></para>
|
||||
<para>In this example, <varname>VARIABLE</varname> has a flag, <varname>SOMEFLAG</varname> which is set to <literal>value</literal>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Inheritance</title>
|
||||
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
|
||||
<para>The <literal>inherit</literal> 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 <envar>BBPATH</envar>, where filename is what you inherited.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Tasks</title>
|
||||
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
|
||||
<para>In BitBake, each step that needs to be run for a given .bb is known as a task. There is a command <literal>addtask</literal> to add new tasks (must be a defined python executable metadata and must start with <quote>do_</quote>) and describe intertask dependencies.</para>
|
||||
<para><screen>python do_printdate () {
|
||||
import time
|
||||
print time.strftime('%Y%m%d', time.gmtime())
|
||||
}
|
||||
|
||||
addtask printdate before do_build</screen></para>
|
||||
<para>This defines the necessary python function and adds it as a task which is now a dependency of do_build (the default task). If anyone executes the do_build task, that will result in do_printdate being run first.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Events</title>
|
||||
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
|
||||
<para>BitBake allows to install event handlers. Events are triggered at certain points during operation, such as, the beginning of operation against a given .bb, the start of a given task, task failure, task success, et cetera. The intent was to make it easy to do things like email notifications on build failure.</para>
|
||||
<para><screen>addhandler myclass_eventhandler
|
||||
python myclass_eventhandler() {
|
||||
from bb.event import getName
|
||||
from bb import data
|
||||
|
||||
print("The name of the Event is %s" % getName(e))
|
||||
print("The file we run for is %s" % data.getVar('FILE', e.data, True))
|
||||
}
|
||||
</screen></para><para>
|
||||
This event handler gets called every time an event is triggered. A global variable <varname>e</varname> is defined. <varname>e</varname>.data contains an instance of bb.data. With the getName(<varname>e</varname>)
|
||||
method one can get the name of the triggered event.</para><para>The above event handler prints the name
|
||||
of the event and the content of the <varname>FILE</varname> variable.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Variants</title>
|
||||
<para>Two Bitbake features exist to facilitate the creation of multiple buildable incarnations from a single recipe file.</para>
|
||||
<para>The first is <varname>BBCLASSEXTEND</varname>. This variable is a space separated list of classes to utilize to "extend" the recipe for each variant. As an example, setting <screen>BBCLASSEXTEND = "native"</screen> results in a second incarnation of the current recipe being available. This second incarantion will have the "native" class inherited.</para>
|
||||
<para>The second feature is <varname>BBVERSIONS</varname>. This variable allows a single recipe to be able to build multiple versions of a project from a single recipe file, and allows you to specify conditional metadata (using the <varname>OVERRIDES</varname> mechanism) for a single version, or an optionally named range of versions:</para>
|
||||
<para><screen>BBVERSIONS = "1.0 2.0 git"
|
||||
SRC_URI_git = "git://someurl/somepath.git"</screen></para>
|
||||
<para><screen>BBVERSIONS = "1.0.[0-6]:1.0.0+ \
|
||||
1.0.[7-9]:1.0.7+"
|
||||
SRC_URI_append_1.0.7+ = "file://some_patch_which_the_new_versions_need.patch;patch=1"</screen></para>
|
||||
<para>Note that the name of the range will default to the original version of the recipe, so given OE, a recipe file of foo_1.0.0+.bb will default the name of its versions to 1.0.0+. This is useful, as the range name is not only placed into overrides, it's also made available for the metadata to use in the form of the <varname>BPV</varname> variable, for use in file:// search paths (<varname>FILESPATH</varname>).</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Dependency Handling</title>
|
||||
<para>Bitbake 1.7.x onwards works with the metadata at the task level since this is optimal when dealing with multiple threads of execution. A robust method of specifing task dependencies is therefore needed. </para>
|
||||
<section>
|
||||
<title>Dependencies internal to the .bb file</title>
|
||||
<para>Where the dependencies are internal to a given .bb file, the dependencies are handled by the previously detailed addtask directive.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>DEPENDS</title>
|
||||
<para>DEPENDS is taken to specify build time dependencies. The 'deptask' flag for tasks is used to signify the task of each DEPENDS which must have completed before that task can be executed.</para>
|
||||
<para><screen>do_configure[deptask] = "do_populate_staging"</screen></para>
|
||||
<para>means the do_populate_staging task of each item in DEPENDS must have completed before do_configure can execute.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>RDEPENDS</title>
|
||||
<para>RDEPENDS is taken to specify runtime dependencies. The 'rdeptask' flag for tasks is used to signify the task of each RDEPENDS which must have completed before that task can be executed.</para>
|
||||
<para><screen>do_package_write[rdeptask] = "do_package"</screen></para>
|
||||
<para>means the do_package task of each item in RDEPENDS must have completed before do_package_write can execute.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Recursive DEPENDS</title>
|
||||
<para>These are specified with the 'recdeptask' flag and is used signify the task(s) of each DEPENDS which must have completed before that task can be executed. It applies recursively so also, the DEPENDS of each item in the original DEPENDS must be met and so on.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Recursive RDEPENDS</title>
|
||||
<para>These are specified with the 'recrdeptask' flag and is used signify the task(s) of each RDEPENDS which must have completed before that task can be executed. It applies recursively so also, the RDEPENDS of each item in the original RDEPENDS must be met and so on. It also runs all DEPENDS first too.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Inter Task</title>
|
||||
<para>The 'depends' flag for tasks is a more generic form of which allows an interdependency on specific tasks rather than specifying the data in DEPENDS or RDEPENDS.</para>
|
||||
<para><screen>do_patch[depends] = "quilt-native:do_populate_staging"</screen></para>
|
||||
<para>means the do_populate_staging task of the target quilt-native must have completed before the do_patch can execute.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Parsing</title>
|
||||
<section>
|
||||
<title>Configuration Files</title>
|
||||
<para>The first of the classifications of metadata in BitBake is configuration metadata. This metadata is global, and therefore affects <emphasis>all</emphasis> packages and tasks which are executed.</para>
|
||||
<para>Bitbake will first search the current working directory for an optional "conf/bblayers.conf" configuration file. This file is expected to contain a BBLAYERS variable which is a space delimited list of 'layer' directories. For each directory in this list a "conf/layer.conf" file will be searched for and parsed with the LAYERDIR variable being set to the directory where the layer was found. The idea is these files will setup BBPATH and other variables correctly for a given build directory automatically for the user.</para>
|
||||
<para>Bitbake will then expect to find 'conf/bitbake.conf' somewhere in the user specified <envar>BBPATH</envar>. That configuration file generally has include directives to pull in any other metadata (generally files specific to architecture, machine, <emphasis>local</emphasis> and so on.</para>
|
||||
<para>Only variable definitions and include directives are allowed in .conf files.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Classes</title>
|
||||
<para>BitBake classes are our rudimentary inheritance mechanism. As briefly mentioned in the metadata introduction, they're parsed when an <literal>inherit</literal> directive is encountered, and they are located in classes/ relative to the dirs in <envar>BBPATH</envar>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>.bb Files</title>
|
||||
<para>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 <varname>BBFILES</varname> variable, which is set to a space separated list of .bb files, and does handle wildcards.</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>File Download support</title>
|
||||
<section>
|
||||
<title>Overview</title>
|
||||
<para>BitBake provides support to download files this procedure is called fetching. The SRC_URI is normally used to indicate BitBake which files to fetch. The next sections will describe th available fetchers and the options they have. Each Fetcher honors a set of Variables and
|
||||
a per URI parameters separated by a <quote>;</quote> consisting of a key and a value. The semantic of the Variables and Parameters are defined by the Fetcher. BitBakes tries to have a consistent semantic between the different Fetchers.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Local File Fetcher</title>
|
||||
<para>The URN for the Local File Fetcher is <emphasis>file</emphasis>. The filename can be either absolute or relative. If the filename is relative <varname>FILESPATH</varname> and <varname>FILESDIR</varname> will be used to find the appropriate relative file depending on the <varname>OVERRIDES</varname>. Single files and complete directories can be specified.
|
||||
<screen><varname>SRC_URI</varname>= "file://relativefile.patch"
|
||||
<varname>SRC_URI</varname>= "file://relativefile.patch;this=ignored"
|
||||
<varname>SRC_URI</varname>= "file:///Users/ich/very_important_software"
|
||||
</screen>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>CVS File Fetcher</title>
|
||||
<para>The URN for the CVS Fetcher is <emphasis>cvs</emphasis>. This Fetcher honors the variables <varname>DL_DIR</varname>, <varname>SRCDATE</varname>, <varname>FETCHCOMMAND_cvs</varname>, <varname>UPDATECOMMAND_cvs</varname>. <varname>DL_DIR</varname> specifies where a temporary checkout is saved, <varname>SRCDATE</varname> specifies which date to use when doing the fetching (the special value of "now" will cause the checkout to be updated on every build), <varname>FETCHCOMMAND</varname> and <varname>UPDATECOMMAND</varname> specify which executables should be used when doing the CVS checkout or update.
|
||||
</para>
|
||||
<para>The supported Parameters are <varname>module</varname>, <varname>tag</varname>, <varname>date</varname>, <varname>method</varname>, <varname>localdir</varname>, <varname>rsh</varname>. The <varname>module</varname> specifies which module to check out, the <varname>tag</varname> describes which CVS TAG should be used for the checkout by default the TAG is empty. A <varname>date</varname> can be specified to override the SRCDATE of the configuration to checkout a specific date. The special value of "now" will cause the checkout to be updated on every build.<varname>method</varname> is by default <emphasis>pserver</emphasis>, if <emphasis>ext</emphasis> is used the <varname>rsh</varname> parameter will be evaluated and <varname>CVS_RSH</varname> will be set. Finally <varname>localdir</varname> is used to checkout into a special directory relative to <varname>CVSDIR</varname>.
|
||||
<screen><varname>SRC_URI</varname> = "cvs://CVSROOT;module=mymodule;tag=some-version;method=ext"
|
||||
<varname>SRC_URI</varname> = "cvs://CVSROOT;module=mymodule;date=20060126;localdir=usethat"
|
||||
</screen>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>HTTP/FTP Fetcher</title>
|
||||
<para>The URNs for the HTTP/FTP are <emphasis>http</emphasis>, <emphasis>https</emphasis> and <emphasis>ftp</emphasis>. This Fetcher honors the variables <varname>DL_DIR</varname>, <varname>FETCHCOMMAND_wget</varname>, <varname>PREMIRRORS</varname>, <varname>MIRRORS</varname>. The <varname>DL_DIR</varname> defines where to store the fetched file, <varname>FETCHCOMMAND</varname> contains the command used for fetching. <quote>${URI}</quote> and <quote>${FILES}</quote> will be replaced by the uri and basename of the to be fetched file. <varname>PREMIRRORS</varname>
|
||||
will be tried first when fetching a file if that fails the actual file will be tried and finally all <varname>MIRRORS</varname> will be tried.
|
||||
</para>
|
||||
<para>The only supported Parameter is <varname>md5sum</varname>. After a fetch the <varname>md5sum</varname> of the file will be calculated and the two sums will be compared.
|
||||
</para>
|
||||
<para><screen><varname>SRC_URI</varname> = "http://oe.handhelds.org/not_there.aac;md5sum=12343"
|
||||
<varname>SRC_URI</varname> = "ftp://oe.handhelds.org/not_there_as_well.aac;md5sum=1234"
|
||||
<varname>SRC_URI</varname> = "ftp://you@oe.handheld.sorg/home/you/secret.plan;md5sum=1234"
|
||||
</screen></para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>SVK Fetcher</title>
|
||||
<para>
|
||||
<emphasis>Currently NOT supported</emphasis>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>SVN Fetcher</title>
|
||||
<para>The URN for the SVN Fetcher is <emphasis>svn</emphasis>.
|
||||
</para>
|
||||
<para>This Fetcher honors the variables <varname>FETCHCOMMAND_svn</varname>, <varname>DL_DIR</varname>, <varname>SRCDATE</varname>. <varname>FETCHCOMMAND</varname> contains the subversion command, <varname>DL_DIR</varname> is the directory where tarballs will be saved, <varname>SRCDATE</varname> specifies which date to use when doing the fetching (the special value of "now" will cause the checkout to be updated on every build).
|
||||
</para>
|
||||
<para>The supported Parameters are <varname>proto</varname>, <varname>rev</varname>. <varname>proto</varname> is the subversion prototype, <varname>rev</varname> is the subversions revision.
|
||||
</para>
|
||||
<para><screen><varname>SRC_URI</varname> = "svn://svn.oe.handhelds.org/svn;module=vip;proto=http;rev=667"
|
||||
<varname>SRC_URI</varname> = "svn://svn.oe.handhelds.org/svn/;module=opie;proto=svn+ssh;date=20060126"
|
||||
</screen></para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>GIT Fetcher</title>
|
||||
<para>The URN for the GIT Fetcher is <emphasis>git</emphasis>.
|
||||
</para>
|
||||
<para>The Variables <varname>DL_DIR</varname>, <varname>GITDIR</varname> are used. <varname>DL_DIR</varname> will be used to store the checkedout version. <varname>GITDIR</varname> will be used as the base directory where the git tree is cloned to.
|
||||
</para>
|
||||
<para>The Parameters are <emphasis>tag</emphasis>, <emphasis>protocol</emphasis>. <emphasis>tag</emphasis> is a git tag, the default is <quote>master</quote>. <emphasis>protocol</emphasis> is the git protocol to use and defaults to <quote>rsync</quote>.
|
||||
</para>
|
||||
<para><screen><varname>SRC_URI</varname> = "git://git.oe.handhelds.org/git/vip.git;tag=version-1"
|
||||
<varname>SRC_URI</varname> = "git://git.oe.handhelds.org/git/vip.git;protocol=http"
|
||||
</screen></para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
||||
|
||||
<chapter>
|
||||
<title>The bitbake command</title>
|
||||
<section>
|
||||
<title>Introduction</title>
|
||||
<para>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.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Usage and Syntax</title>
|
||||
<para>
|
||||
<screen><prompt>$ </prompt>bitbake --help
|
||||
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 separated list of files to
|
||||
be executed. BBFILES does support wildcards.
|
||||
Default BBFILES are the .bb files in the current directory.
|
||||
|
||||
options:
|
||||
--version show program's version number and exit
|
||||
-h, --help show this help message and exit
|
||||
-b BUILDFILE, --buildfile=BUILDFILE
|
||||
execute the task against this .bb file, rather than a
|
||||
package from BBFILES.
|
||||
-k, --continue 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.
|
||||
-f, --force force run of specified cmd, regardless of stamp status
|
||||
-i, --interactive drop into the interactive mode also called the BitBake
|
||||
shell.
|
||||
-c CMD, --cmd=CMD 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 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
|
||||
-D, --debug Increase the debug level. You can specify this more
|
||||
than once.
|
||||
-n, --dry-run don't execute, just go through the motions
|
||||
-p, --parse-only quit after parsing the BB files (developers only)
|
||||
-d, --disable-psyco disable using the psyco just-in-time compiler (not
|
||||
recommended)
|
||||
-s, --show-versions show current and preferred versions of all packages
|
||||
-e, --environment show the global or per-package environment (this is
|
||||
what used to be bbread)
|
||||
-g, --graphviz emit the dependency trees of the specified packages in
|
||||
the dot syntax
|
||||
-I IGNORED_DOT_DEPS, --ignore-deps=IGNORED_DOT_DEPS
|
||||
Stop processing at the given list of dependencies when
|
||||
generating dependency graphs. This can help to make
|
||||
the graph more appealing
|
||||
-l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS
|
||||
Show debug logging for the specified logging domains
|
||||
-P, --profile profile the command and print a report
|
||||
|
||||
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
<example>
|
||||
<title>Executing a task against a single .bb</title>
|
||||
<para>Executing tasks for a single file is relatively simple. You specify the file in question, and bitbake parses it and executes the specified task (or <quote>build</quote> by default). It obeys intertask dependencies when doing so.</para>
|
||||
<para><quote>clean</quote> task:</para>
|
||||
<para><screen><prompt>$ </prompt>bitbake -b blah_1.0.bb -c clean</screen></para>
|
||||
<para><quote>build</quote> task:</para>
|
||||
<para><screen><prompt>$ </prompt>bitbake -b blah_1.0.bb</screen></para>
|
||||
</example>
|
||||
</para>
|
||||
<para>
|
||||
<example>
|
||||
<title>Executing tasks against a set of .bb files</title>
|
||||
<para>There are a number of additional complexities introduced when one wants to manage multiple .bb files. Clearly there needs to be a way to tell bitbake what files are available, and of those, which we want to execute at this time. There also needs to be a way for each .bb to express its dependencies, both for build time and runtime. There must be a way for the user to express their preferences when multiple .bb's provide the same functionality, or when there are multiple versions of a .bb.</para>
|
||||
<para>The next section, Metadata, outlines how one goes about specifying such things.</para>
|
||||
<para>Note that the bitbake command, when not using --buildfile, accepts a <varname>PROVIDER</varname>, not a filename or anything else. By default, a .bb generally PROVIDES its packagename, packagename-version, and packagename-version-revision.</para>
|
||||
<screen><prompt>$ </prompt>bitbake blah</screen>
|
||||
<screen><prompt>$ </prompt>bitbake blah-1.0</screen>
|
||||
<screen><prompt>$ </prompt>bitbake blah-1.0-r0</screen>
|
||||
<screen><prompt>$ </prompt>bitbake -c clean blah</screen>
|
||||
<screen><prompt>$ </prompt>bitbake virtual/whatever</screen>
|
||||
<screen><prompt>$ </prompt>bitbake -c clean virtual/whatever</screen>
|
||||
</example>
|
||||
<example>
|
||||
<title>Generating dependency graphs</title>
|
||||
<para>BitBake is able to generate dependency graphs using the dot syntax. These graphs can be converted
|
||||
to images using the <application>dot</application> application from <ulink url="http://www.graphviz.org">graphviz</ulink>.
|
||||
Two files will be written into the current working directory, <emphasis>depends.dot</emphasis> containing dependency information at the package level and <emphasis>task-depends.dot</emphasis> containing a breakdown of the dependencies at the task level. To stop depending on common depends one can use the <prompt>-I depend</prompt> to omit these from the graph. This can lead to more readable graphs. E.g. this way <varname>DEPENDS</varname> from inherited classes, e.g. base.bbclass, can be removed from the graph.</para>
|
||||
<screen><prompt>$ </prompt>bitbake -g blah</screen>
|
||||
<screen><prompt>$ </prompt>bitbake -g -I virtual/whatever -I bloom blah</screen>
|
||||
</example>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Special variables</title>
|
||||
<para>Certain variables affect bitbake operation:</para>
|
||||
<section>
|
||||
<title><varname>BB_NUMBER_THREADS</varname></title>
|
||||
<para> The number of threads bitbake should run at once (default: 1).</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Metadata</title>
|
||||
<para>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.
|
||||
<example>
|
||||
<title>Setting BBFILES</title>
|
||||
<programlisting><varname>BBFILES</varname> = "/path/to/bbfiles/*.bb"</programlisting>
|
||||
</example></para>
|
||||
<para>With regard to dependencies, it expects the .bb to define a <varname>DEPENDS</varname> variable, which contains a space separated list of <quote>package names</quote>, which themselves are the <varname>PN</varname> variable. The <varname>PN</varname> variable is, in general, by default, set to a component of the .bb filename.</para>
|
||||
<example>
|
||||
<title>Depending on another .bb</title>
|
||||
<para>a.bb:
|
||||
<screen>PN = "package-a"
|
||||
DEPENDS += "package-b"</screen>
|
||||
</para>
|
||||
<para>b.bb:
|
||||
<screen>PN = "package-b"</screen>
|
||||
</para>
|
||||
</example>
|
||||
<example>
|
||||
<title>Using PROVIDES</title>
|
||||
<para>This example shows the usage of the PROVIDES variable, which allows a given .bb to specify what functionality it provides.</para>
|
||||
<para>package1.bb:
|
||||
<screen>PROVIDES += "virtual/package"</screen>
|
||||
</para>
|
||||
<para>package2.bb:
|
||||
<screen>DEPENDS += "virtual/package"</screen>
|
||||
</para>
|
||||
<para>package3.bb:
|
||||
<screen>PROVIDES += "virtual/package"</screen>
|
||||
</para>
|
||||
<para>As you can see, here there are two different .bb's that provide the same functionality (virtual/package). Clearly, there needs to be a way for the person running bitbake to control which of those providers gets used. There is, indeed, such a way.</para>
|
||||
<para>The following would go into a .conf file, to select package1:
|
||||
<screen>PREFERRED_PROVIDER_virtual/package = "package1"</screen>
|
||||
</para>
|
||||
</example>
|
||||
<example>
|
||||
<title>Specifying version preference</title>
|
||||
<para>When there are multiple <quote>versions</quote> of a given package, bitbake defaults to selecting the most recent version, unless otherwise specified. If the .bb in question has a <varname>DEFAULT_PREFERENCE</varname> set lower than the other .bb's (default is 0), then it will not be selected. This allows the person or persons maintaining the repository of .bb files to specify their preferences for the default selected version. In addition, the user can specify their preferences with regard to version.</para>
|
||||
<para>If the first .bb is named <filename>a_1.1.bb</filename>, then the <varname>PN</varname> variable will be set to <quote>a</quote>, and the <varname>PV</varname> variable will be set to 1.1.</para>
|
||||
<para>If we then have an <filename>a_1.2.bb</filename>, bitbake will choose 1.2 by default. However, if we define the following variable in a .conf that bitbake parses, we can change that.
|
||||
<screen>PREFERRED_VERSION_a = "1.1"</screen>
|
||||
</para>
|
||||
</example>
|
||||
<example>
|
||||
<title>Using <quote>bbfile collections</quote></title>
|
||||
<para>bbfile collections exist to allow the user to have multiple repositories of bbfiles that contain the same exact package. For example, one could easily use them to make one's own local copy of an upstream repository, but with custom modifications that one does not want upstream. Usage:</para>
|
||||
<screen>BBFILES = "/stuff/openembedded/*/*.bb /stuff/openembedded.modified/*/*.bb"
|
||||
BBFILE_COLLECTIONS = "upstream local"
|
||||
BBFILE_PATTERN_upstream = "^/stuff/openembedded/"
|
||||
BBFILE_PATTERN_local = "^/stuff/openembedded.modified/"
|
||||
BBFILE_PRIORITY_upstream = "5"
|
||||
BBFILE_PRIORITY_local = "10"</screen>
|
||||
</example>
|
||||
</section>
|
||||
</chapter>
|
||||
</book>
|
||||
323
bitbake/lib/bb/COW.py
Normal file
323
bitbake/lib/bb/COW.py
Normal file
@@ -0,0 +1,323 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import copy
|
||||
import types
|
||||
ImmutableTypes = (
|
||||
types.NoneType,
|
||||
bool,
|
||||
complex,
|
||||
float,
|
||||
int,
|
||||
long,
|
||||
tuple,
|
||||
frozenset,
|
||||
basestring
|
||||
)
|
||||
|
||||
MUTABLE = "__mutable__"
|
||||
|
||||
class COWMeta(type):
|
||||
pass
|
||||
|
||||
class COWDictMeta(COWMeta):
|
||||
__warn__ = False
|
||||
__hasmutable__ = False
|
||||
__marker__ = tuple()
|
||||
|
||||
def __str__(cls):
|
||||
# FIXME: I have magic numbers!
|
||||
return "<COWDict Level: %i Current Keys: %i>" % (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, 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("Warning: Doing a copy because %s is a mutable type." % key, file=cls.__warn__)
|
||||
try:
|
||||
value = value.copy()
|
||||
except AttributeError as 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 as 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 __contains__(cls, key):
|
||||
return cls.has_key(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("Warning: If you arn't going to change any of the values call with True.", file=cls.__warn__)
|
||||
return cls.iter("values", readonly)
|
||||
def iteritems(cls, readonly=False):
|
||||
if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
|
||||
print("Warning: If you arn't going to change any of the values call with True.", file=cls.__warn__)
|
||||
return cls.iter("items", readonly)
|
||||
|
||||
class COWSetMeta(COWDictMeta):
|
||||
def __str__(cls):
|
||||
# FIXME: I have magic numbers!
|
||||
return "<COWSet Level: %i Current Keys: %i>" % (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 as 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()
|
||||
100
bitbake/lib/bb/__init__.py
Normal file
100
bitbake/lib/bb/__init__.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# 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.11.0"
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2, 6, 0):
|
||||
raise RuntimeError("Sorry, python 2.6.0 or later is required for this version of bitbake")
|
||||
|
||||
import os
|
||||
import bb.msg
|
||||
|
||||
if "BBDEBUG" in os.environ:
|
||||
level = int(os.environ["BBDEBUG"])
|
||||
if level:
|
||||
bb.msg.set_debug_level(level)
|
||||
|
||||
|
||||
# Messaging convenience functions
|
||||
def plain(*args):
|
||||
bb.msg.plain(''.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(None, ''.join(args))
|
||||
|
||||
def error(*args):
|
||||
bb.msg.error(None, ''.join(args))
|
||||
|
||||
def fatal(*args):
|
||||
bb.msg.fatal(None, ''.join(args))
|
||||
|
||||
|
||||
def deprecated(func, name = None, advice = ""):
|
||||
"""This is a decorator which can be used to mark functions
|
||||
as deprecated. It will result in a warning being emmitted
|
||||
when the function is used."""
|
||||
import warnings
|
||||
|
||||
if advice:
|
||||
advice = ": %s" % advice
|
||||
if name is None:
|
||||
name = func.__name__
|
||||
|
||||
def newFunc(*args, **kwargs):
|
||||
warnings.warn("Call to deprecated function %s%s." % (name,
|
||||
advice),
|
||||
category = PendingDeprecationWarning,
|
||||
stacklevel = 2)
|
||||
return func(*args, **kwargs)
|
||||
newFunc.__name__ = func.__name__
|
||||
newFunc.__doc__ = func.__doc__
|
||||
newFunc.__dict__.update(func.__dict__)
|
||||
return newFunc
|
||||
|
||||
# For compatibility
|
||||
def deprecate_import(current, modulename, fromlist, renames = None):
|
||||
"""Import objects from one module into another, wrapping them with a DeprecationWarning"""
|
||||
import sys
|
||||
|
||||
module = __import__(modulename, fromlist = fromlist)
|
||||
for position, objname in enumerate(fromlist):
|
||||
obj = getattr(module, objname)
|
||||
newobj = deprecated(obj, "{0}.{1}".format(current, objname),
|
||||
"Please use {0}.{1} instead".format(modulename, objname))
|
||||
if renames:
|
||||
newname = renames[position]
|
||||
else:
|
||||
newname = objname
|
||||
|
||||
setattr(sys.modules[current], newname, newobj)
|
||||
|
||||
deprecate_import(__name__, "bb.fetch", ("MalformedUrl", "encodeurl", "decodeurl"))
|
||||
deprecate_import(__name__, "bb.utils", ("mkdirhier", "movefile", "copyfile", "which"))
|
||||
deprecate_import(__name__, "bb.utils", ["vercmp_string"], ["vercmp"])
|
||||
445
bitbake/lib/bb/build.py
Normal file
445
bitbake/lib/bb/build.py
Normal file
@@ -0,0 +1,445 @@
|
||||
# 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
|
||||
import bb.utils
|
||||
|
||||
# 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 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 TaskInvalid(TaskBase):
|
||||
"""Invalid Task"""
|
||||
|
||||
# functions
|
||||
|
||||
def exec_func(func, d, dirs = None):
|
||||
"""Execute an BB 'function'"""
|
||||
|
||||
body = data.getVar(func, d)
|
||||
if not body:
|
||||
bb.warn("Function %s doesn't exist" % func)
|
||||
return
|
||||
|
||||
flags = data.getVarFlags(func, d)
|
||||
for item in ['deps', 'check', 'interactive', 'python', 'cleandirs', 'dirs', 'lockfiles', 'fakeroot', 'task']:
|
||||
if not item in flags:
|
||||
flags[item] = None
|
||||
|
||||
ispython = flags['python']
|
||||
|
||||
cleandirs = flags['cleandirs']
|
||||
if cleandirs:
|
||||
for cdir in data.expand(cleandirs, d).split():
|
||||
os.system("rm -rf %s" % cdir)
|
||||
|
||||
if dirs is None:
|
||||
dirs = flags['dirs']
|
||||
if dirs:
|
||||
dirs = data.expand(dirs, d).split()
|
||||
|
||||
if dirs:
|
||||
for adir in dirs:
|
||||
bb.utils.mkdirhier(adir)
|
||||
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 scriptfile
|
||||
t = data.getVar('T', d, 1)
|
||||
if not t:
|
||||
raise SystemExit("T variable not set, unable to build")
|
||||
bb.utils.mkdirhier(t)
|
||||
runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
|
||||
logfile = d.getVar("BB_LOGFILE", True)
|
||||
|
||||
# Change to correct directory (if specified)
|
||||
if adir and os.access(adir, os.F_OK):
|
||||
os.chdir(adir)
|
||||
|
||||
locks = []
|
||||
lockfiles = flags['lockfiles']
|
||||
if lockfiles:
|
||||
for lock in data.expand(lockfiles, d).split():
|
||||
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)
|
||||
|
||||
def exec_func_python(func, d, runfile, logfile):
|
||||
"""Execute a python BB 'function'"""
|
||||
|
||||
bbfile = bb.data.getVar('FILE', d, 1)
|
||||
tmp = "def " + func + "(d):\n%s" % data.getVar(func, d)
|
||||
tmp += '\n' + func + '(d)'
|
||||
|
||||
f = open(runfile, "w")
|
||||
f.write(tmp)
|
||||
comp = utils.better_compile(tmp, func, bbfile)
|
||||
try:
|
||||
utils.better_exec(comp, {"d": d}, tmp, bbfile)
|
||||
except:
|
||||
(t, value, tb) = sys.exc_info()
|
||||
|
||||
if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
|
||||
raise
|
||||
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_func(func, 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:
|
||||
raise FuncFailed("Function not specified for exec_func_shell")
|
||||
|
||||
# execute function
|
||||
if flags['fakeroot'] and not flags['task']:
|
||||
bb.fatal("Function %s specifies fakeroot but isn't a task?!" % func)
|
||||
|
||||
lang_environment = "LC_ALL=C "
|
||||
ret = os.system('%ssh -e %s' % (lang_environment, runfile))
|
||||
|
||||
if ret == 0:
|
||||
return
|
||||
|
||||
raise FuncFailed("function %s failed" % func, logfile)
|
||||
|
||||
|
||||
def exec_task(fn, 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):
|
||||
event.fire(TaskInvalid(task, d), d)
|
||||
bb.msg.error(bb.msg.domain.Build, "No such task: %s" % task)
|
||||
return 1
|
||||
|
||||
quieterr = False
|
||||
if d.getVarFlag(task, "quieterrors") is not None:
|
||||
quieterr = True
|
||||
|
||||
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)
|
||||
data.setVar('BB_FILENAME', fn, d)
|
||||
data.setVar('BB_CURRENTTASK', task[3:], d)
|
||||
event.fire(TaskStarted(task, localdata), localdata)
|
||||
|
||||
# Setup logfiles
|
||||
t = data.getVar('T', d, 1)
|
||||
if not t:
|
||||
raise SystemExit("T variable not set, unable to build")
|
||||
bb.utils.mkdirhier(t)
|
||||
loglink = "%s/log.%s" % (t, task)
|
||||
logfile = "%s/log.%s.%s" % (t, task, str(os.getpid()))
|
||||
d.setVar("BB_LOGFILE", logfile)
|
||||
|
||||
# Even though the log file has not yet been opened, lets create the link
|
||||
if loglink:
|
||||
try:
|
||||
os.remove(loglink)
|
||||
except OSError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
os.symlink(logfile, loglink)
|
||||
except OSError as e:
|
||||
pass
|
||||
|
||||
# Handle logfiles
|
||||
si = file('/dev/null', 'r')
|
||||
try:
|
||||
so = file(logfile, 'w')
|
||||
except OSError as 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])
|
||||
|
||||
# Since we've remapped stdout and stderr, its safe for log messages to be printed there now
|
||||
# exec_func can nest so we have to save state
|
||||
origstdout = bb.event.useStdout
|
||||
bb.event.useStdout = True
|
||||
|
||||
|
||||
prefuncs = (data.getVarFlag(task, 'prefuncs', localdata) or "").split()
|
||||
for func in prefuncs:
|
||||
exec_func(func, localdata)
|
||||
exec_func(task, localdata)
|
||||
postfuncs = (data.getVarFlag(task, 'postfuncs', localdata) or "").split()
|
||||
for func in postfuncs:
|
||||
exec_func(func, localdata)
|
||||
|
||||
event.fire(TaskSucceeded(task, localdata), localdata)
|
||||
|
||||
# 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)
|
||||
|
||||
except FuncFailed as message:
|
||||
# Try to extract the optional logfile
|
||||
try:
|
||||
(msg, logfile) = message
|
||||
except:
|
||||
logfile = None
|
||||
msg = message
|
||||
if not quieterr:
|
||||
bb.msg.error(bb.msg.domain.Build, "Task failed: %s" % message )
|
||||
failedevent = TaskFailed(msg, logfile, task, d)
|
||||
event.fire(failedevent, d)
|
||||
return 1
|
||||
|
||||
except Exception:
|
||||
from traceback import format_exc
|
||||
if not quieterr:
|
||||
bb.msg.error(bb.msg.domain.Build, "Build of %s failed" % (task))
|
||||
bb.msg.error(bb.msg.domain.Build, format_exc())
|
||||
failedevent = TaskFailed("Task Failed", None, task, d)
|
||||
event.fire(failedevent, d)
|
||||
return 1
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
bb.event.useStdout = origstdout
|
||||
|
||||
# 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 logfile and 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)
|
||||
try:
|
||||
os.remove(loglink)
|
||||
except OSError as e:
|
||||
pass
|
||||
|
||||
# Close the backup fds
|
||||
os.close(osi[0])
|
||||
os.close(oso[0])
|
||||
os.close(ose[0])
|
||||
|
||||
return 0
|
||||
|
||||
def extract_stamp(d, fn):
|
||||
"""
|
||||
Extracts stamp format which is either a data dictionary (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)
|
||||
bb.utils.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')
|
||||
getTask('fakeroot')
|
||||
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)
|
||||
559
bitbake/lib/bb/cache.py
Normal file
559
bitbake/lib/bb/cache.py
Normal file
@@ -0,0 +1,559 @@
|
||||
# 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
|
||||
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__ = "132"
|
||||
|
||||
class Cache:
|
||||
"""
|
||||
BitBake Cache implementation
|
||||
"""
|
||||
def __init__(self, data):
|
||||
|
||||
|
||||
self.cachedir = bb.data.getVar("CACHE", 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 = <directory> 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)
|
||||
bb.utils.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", data)
|
||||
|
||||
old_mtimes = [old_mtime for f, old_mtime in deps]
|
||||
old_mtimes.append(newest_mtime)
|
||||
newest_mtime = max(old_mtimes)
|
||||
|
||||
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:
|
||||
if os.path.isfile(self.cachefile):
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Out of date cache found, rebuilding...")
|
||||
|
||||
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]
|
||||
|
||||
self.depends_cache.setdefault(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) or set()
|
||||
self.depends_cache.setdefault(fn, {})
|
||||
if "__depends" not in self.depends_cache[fn] or not self.depends_cache[fn]["__depends"]:
|
||||
self.depends_cache[fn]["__depends"] = depends
|
||||
else:
|
||||
self.depends_cache[fn]["__depends"].update(depends)
|
||||
|
||||
# Make sure the variants always make it into the cache too
|
||||
self.getVar('__VARIANTS', 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, appends, 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, appends, cfgData)
|
||||
return bb_data[cls]
|
||||
|
||||
def loadData(self, fn, appends, 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('__VARIANTS', 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, appends, cfgData)
|
||||
|
||||
for data in bb_data:
|
||||
virtualfn = self.realfn2virtual(fn, data)
|
||||
self.setData(virtualfn, fn, bb_data[data])
|
||||
if self.getVar("__SKIPPED", virtualfn):
|
||||
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 no 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] = ""
|
||||
|
||||
invalid = False
|
||||
# Mark extended class data as clean too
|
||||
multi = self.getVar('__VARIANTS', fn, True)
|
||||
for cls in (multi or "").split():
|
||||
virtualfn = self.realfn2virtual(fn, cls)
|
||||
self.clean[virtualfn] = ""
|
||||
if not virtualfn in self.depends_cache:
|
||||
bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s is not cached" % virtualfn)
|
||||
invalid = True
|
||||
|
||||
# If any one of the varients is not present, mark cache as invalid for all
|
||||
if invalid:
|
||||
for cls in (multi or "").split():
|
||||
virtualfn = self.realfn2virtual(fn, cls)
|
||||
bb.msg.debug(2, bb.msg.domain.Cache, "Cache: Removing %s from cache" % virtualfn)
|
||||
del self.clean[virtualfn]
|
||||
bb.msg.debug(2, bb.msg.domain.Cache, "Cache: Removing %s from cache" % fn)
|
||||
del self.clean[fn]
|
||||
return False
|
||||
|
||||
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.copy(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.msg.note(1, bb.msg.domain.Cache, "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)
|
||||
|
||||
# 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)
|
||||
|
||||
cacheData.tasks[file_name] = self.getVar('__BBTASKS', file_name, True)
|
||||
for t in cacheData.tasks[file_name]:
|
||||
cacheData.basetaskhash[file_name + "." + t] = self.getVar("BB_BASEHASH_task-%s" % t, 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)
|
||||
|
||||
cacheData.hashfn[file_name] = self.getVar('BB_HASHFILENAME', file_name, True)
|
||||
|
||||
# Touch this to make sure its in the cache
|
||||
self.getVar('__BB_DONT_CACHE', file_name, True)
|
||||
self.getVar('__VARIANTS', file_name, True)
|
||||
|
||||
def load_bbfile(self, bbfile, appends, config):
|
||||
"""
|
||||
Load and parse one .bb build file
|
||||
Return the data and whether parsing resulted in the file being skipped
|
||||
"""
|
||||
chdir_back = False
|
||||
|
||||
from bb import data, parse
|
||||
|
||||
# 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())
|
||||
parse.cached_mtime_noerror(bbfile_loc)
|
||||
bb_data = data.init_db(config)
|
||||
# The ConfHandler first looks if there is a TOPDIR and if not
|
||||
# then it would call getcwd().
|
||||
# Previously, we chdir()ed to bbfile_loc, called the handler
|
||||
# and finally chdir()ed back, a couple of thousand times. We now
|
||||
# just fill in TOPDIR to point to bbfile_loc if there is no TOPDIR yet.
|
||||
if not data.getVar('TOPDIR', bb_data):
|
||||
chdir_back = True
|
||||
data.setVar('TOPDIR', bbfile_loc, bb_data)
|
||||
try:
|
||||
if appends:
|
||||
data.setVar('__BBAPPEND', " ".join(appends), bb_data)
|
||||
bb_data = parse.handle(bbfile, bb_data) # read .bb data
|
||||
if chdir_back: os.chdir(oldpath)
|
||||
return bb_data
|
||||
except:
|
||||
if chdir_back: 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.configuration.data)
|
||||
|
||||
|
||||
|
||||
#============================================================================#
|
||||
# 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 = {}
|
||||
self.tasks = {}
|
||||
self.basetaskhash = {}
|
||||
self.hashfn = {}
|
||||
|
||||
"""
|
||||
Indirect Cache variables
|
||||
(set elsewhere)
|
||||
"""
|
||||
self.ignored_dependencies = []
|
||||
self.world_target = set()
|
||||
self.bbfile_priority = {}
|
||||
self.bbfile_config_priorities = []
|
||||
329
bitbake/lib/bb/codeparser.py
Normal file
329
bitbake/lib/bb/codeparser.py
Normal file
@@ -0,0 +1,329 @@
|
||||
from pysh import pyshyacc, pyshlex
|
||||
from itertools import chain
|
||||
from bb import msg, utils
|
||||
import ast
|
||||
import codegen
|
||||
|
||||
PARSERCACHE_VERSION = 2
|
||||
|
||||
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.")
|
||||
|
||||
def check_indent(codestr):
|
||||
"""If the code is indented, add a top level piece of code to 'remove' the indentation"""
|
||||
|
||||
i = 0
|
||||
while codestr[i] in ["\n", " ", " "]:
|
||||
i = i + 1
|
||||
|
||||
if i == 0:
|
||||
return codestr
|
||||
|
||||
if codestr[i-1] is " " or codestr[i-1] is " ":
|
||||
return "if 1:\n" + codestr
|
||||
|
||||
return codestr
|
||||
|
||||
pythonparsecache = {}
|
||||
shellparsecache = {}
|
||||
|
||||
def parser_cachefile(d):
|
||||
cachedir = bb.data.getVar("PERSISTENT_DIR", d, True) or bb.data.getVar("CACHE", d, True)
|
||||
if cachedir in [None, '']:
|
||||
return None
|
||||
bb.utils.mkdirhier(cachedir)
|
||||
cachefile = os.path.join(cachedir, "bb_codeparser.dat")
|
||||
bb.msg.debug(1, bb.msg.domain.Cache, "Using cache in '%s' for codeparser cache" % cachefile)
|
||||
return cachefile
|
||||
|
||||
def parser_cache_init(d):
|
||||
|
||||
cachefile = parser_cachefile(d)
|
||||
if not cachefile:
|
||||
return
|
||||
|
||||
try:
|
||||
p = pickle.Unpickler(file(cachefile, "rb"))
|
||||
data, version = p.load()
|
||||
except:
|
||||
return
|
||||
|
||||
if version != PARSERCACHE_VERSION:
|
||||
return
|
||||
|
||||
bb.codeparser.pythonparsecache = data[0]
|
||||
bb.codeparser.shellparsecache = data[1]
|
||||
|
||||
def parser_cache_save(d):
|
||||
|
||||
cachefile = parser_cachefile(d)
|
||||
if not cachefile:
|
||||
return
|
||||
|
||||
p = pickle.Pickler(file(cachefile, "wb"), -1)
|
||||
p.dump([[bb.codeparser.pythonparsecache, bb.codeparser.shellparsecache], PARSERCACHE_VERSION])
|
||||
|
||||
class PythonParser():
|
||||
class ValueVisitor():
|
||||
"""Visitor to traverse a python abstract syntax tree and obtain
|
||||
the variables referenced via bitbake metadata APIs, and the external
|
||||
functions called.
|
||||
"""
|
||||
|
||||
getvars = ("d.getVar", "bb.data.getVar", "data.getVar")
|
||||
expands = ("d.expand", "bb.data.expand", "data.expand")
|
||||
execs = ("bb.build.exec_func", "bb.build.exec_task")
|
||||
|
||||
@classmethod
|
||||
def _compare_name(cls, strparts, node):
|
||||
"""Given a sequence of strings representing a python name,
|
||||
where the last component is the actual Name and the prior
|
||||
elements are Attribute nodes, determine if the supplied node
|
||||
matches.
|
||||
"""
|
||||
|
||||
if not strparts:
|
||||
return True
|
||||
|
||||
current, rest = strparts[0], strparts[1:]
|
||||
if isinstance(node, ast.Attribute):
|
||||
if current == node.attr:
|
||||
return cls._compare_name(rest, node.value)
|
||||
elif isinstance(node, ast.Name):
|
||||
if current == node.id:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def compare_name(cls, value, node):
|
||||
"""Convenience function for the _compare_node method, which
|
||||
can accept a string (which is split by '.' for you), or an
|
||||
iterable of strings, in which case it checks to see if any of
|
||||
them match, similar to isinstance.
|
||||
"""
|
||||
|
||||
if isinstance(value, basestring):
|
||||
return cls._compare_name(tuple(reversed(value.split("."))),
|
||||
node)
|
||||
else:
|
||||
return any(cls.compare_name(item, node) for item in value)
|
||||
|
||||
def __init__(self, value):
|
||||
self.var_references = set()
|
||||
self.var_execs = set()
|
||||
self.direct_func_calls = set()
|
||||
self.var_expands = set()
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def warn(cls, func, arg):
|
||||
"""Warn about calls of bitbake APIs which pass a non-literal
|
||||
argument for the variable name, as we're not able to track such
|
||||
a reference.
|
||||
"""
|
||||
|
||||
try:
|
||||
funcstr = codegen.to_source(func)
|
||||
argstr = codegen.to_source(arg)
|
||||
except TypeError:
|
||||
msg.debug(2, None, "Failed to convert function and argument to source form")
|
||||
else:
|
||||
msg.debug(1, None, "Warning: in call to '%s', argument '%s' is not a literal" %
|
||||
(funcstr, argstr))
|
||||
|
||||
def visit_Call(self, node):
|
||||
if self.compare_name(self.getvars, node.func):
|
||||
if isinstance(node.args[0], ast.Str):
|
||||
self.var_references.add(node.args[0].s)
|
||||
else:
|
||||
self.warn(node.func, node.args[0])
|
||||
elif self.compare_name(self.expands, node.func):
|
||||
if isinstance(node.args[0], ast.Str):
|
||||
self.warn(node.func, node.args[0])
|
||||
self.var_expands.update(node.args[0].s)
|
||||
elif isinstance(node.args[0], ast.Call) and \
|
||||
self.compare_name(self.getvars, node.args[0].func):
|
||||
pass
|
||||
else:
|
||||
self.warn(node.func, node.args[0])
|
||||
elif self.compare_name(self.execs, node.func):
|
||||
if isinstance(node.args[0], ast.Str):
|
||||
self.var_execs.add(node.args[0].s)
|
||||
else:
|
||||
self.warn(node.func, node.args[0])
|
||||
elif isinstance(node.func, ast.Name):
|
||||
self.direct_func_calls.add(node.func.id)
|
||||
elif isinstance(node.func, ast.Attribute):
|
||||
# We must have a qualified name. Therefore we need
|
||||
# to walk the chain of 'Attribute' nodes to determine
|
||||
# the qualification.
|
||||
attr_node = node.func.value
|
||||
identifier = node.func.attr
|
||||
while isinstance(attr_node, ast.Attribute):
|
||||
identifier = attr_node.attr + "." + identifier
|
||||
attr_node = attr_node.value
|
||||
if isinstance(attr_node, ast.Name):
|
||||
identifier = attr_node.id + "." + identifier
|
||||
self.direct_func_calls.add(identifier)
|
||||
|
||||
def __init__(self):
|
||||
#self.funcdefs = set()
|
||||
self.execs = set()
|
||||
#self.external_cmds = set()
|
||||
self.references = set()
|
||||
|
||||
def parse_python(self, node):
|
||||
|
||||
h = hash(str(node))
|
||||
|
||||
if h in pythonparsecache:
|
||||
self.references = pythonparsecache[h]["refs"]
|
||||
self.execs = pythonparsecache[h]["execs"]
|
||||
return
|
||||
|
||||
code = compile(check_indent(str(node)), "<string>", "exec",
|
||||
ast.PyCF_ONLY_AST)
|
||||
|
||||
visitor = self.ValueVisitor(code)
|
||||
for n in ast.walk(code):
|
||||
if n.__class__.__name__ == "Call":
|
||||
visitor.visit_Call(n)
|
||||
|
||||
self.references.update(visitor.var_references)
|
||||
self.references.update(visitor.var_execs)
|
||||
self.execs = visitor.direct_func_calls
|
||||
|
||||
pythonparsecache[h] = {}
|
||||
pythonparsecache[h]["refs"] = self.references
|
||||
pythonparsecache[h]["execs"] = self.execs
|
||||
|
||||
class ShellParser():
|
||||
def __init__(self):
|
||||
self.funcdefs = set()
|
||||
self.allexecs = set()
|
||||
self.execs = set()
|
||||
|
||||
def parse_shell(self, value):
|
||||
"""Parse the supplied shell code in a string, returning the external
|
||||
commands it executes.
|
||||
"""
|
||||
|
||||
h = hash(str(value))
|
||||
|
||||
if h in shellparsecache:
|
||||
self.execs = shellparsecache[h]["execs"]
|
||||
return self.execs
|
||||
|
||||
try:
|
||||
tokens, _ = pyshyacc.parse(value, eof=True, debug=False)
|
||||
except pyshlex.NeedMore:
|
||||
raise ShellSyntaxError("Unexpected EOF")
|
||||
|
||||
for token in tokens:
|
||||
self.process_tokens(token)
|
||||
self.execs = set(cmd for cmd in self.allexecs if cmd not in self.funcdefs)
|
||||
|
||||
shellparsecache[h] = {}
|
||||
shellparsecache[h]["execs"] = self.execs
|
||||
|
||||
return self.execs
|
||||
|
||||
def process_tokens(self, tokens):
|
||||
"""Process a supplied portion of the syntax tree as returned by
|
||||
pyshyacc.parse.
|
||||
"""
|
||||
|
||||
def function_definition(value):
|
||||
self.funcdefs.add(value.name)
|
||||
return [value.body], None
|
||||
|
||||
def case_clause(value):
|
||||
# Element 0 of each item in the case is the list of patterns, and
|
||||
# Element 1 of each item in the case is the list of commands to be
|
||||
# executed when that pattern matches.
|
||||
words = chain(*[item[0] for item in value.items])
|
||||
cmds = chain(*[item[1] for item in value.items])
|
||||
return cmds, words
|
||||
|
||||
def if_clause(value):
|
||||
main = chain(value.cond, value.if_cmds)
|
||||
rest = value.else_cmds
|
||||
if isinstance(rest, tuple) and rest[0] == "elif":
|
||||
return chain(main, if_clause(rest[1]))
|
||||
else:
|
||||
return chain(main, rest)
|
||||
|
||||
def simple_command(value):
|
||||
return None, chain(value.words, (assign[1] for assign in value.assigns))
|
||||
|
||||
token_handlers = {
|
||||
"and_or": lambda x: ((x.left, x.right), None),
|
||||
"async": lambda x: ([x], None),
|
||||
"brace_group": lambda x: (x.cmds, None),
|
||||
"for_clause": lambda x: (x.cmds, x.items),
|
||||
"function_definition": function_definition,
|
||||
"if_clause": lambda x: (if_clause(x), None),
|
||||
"pipeline": lambda x: (x.commands, None),
|
||||
"redirect_list": lambda x: ([x.cmd], None),
|
||||
"subshell": lambda x: (x.cmds, None),
|
||||
"while_clause": lambda x: (chain(x.condition, x.cmds), None),
|
||||
"until_clause": lambda x: (chain(x.condition, x.cmds), None),
|
||||
"simple_command": simple_command,
|
||||
"case_clause": case_clause,
|
||||
}
|
||||
|
||||
for token in tokens:
|
||||
name, value = token
|
||||
try:
|
||||
more_tokens, words = token_handlers[name](value)
|
||||
except KeyError:
|
||||
raise NotImplementedError("Unsupported token type " + name)
|
||||
|
||||
if more_tokens:
|
||||
self.process_tokens(more_tokens)
|
||||
|
||||
if words:
|
||||
self.process_words(words)
|
||||
|
||||
def process_words(self, words):
|
||||
"""Process a set of 'words' in pyshyacc parlance, which includes
|
||||
extraction of executed commands from $() blocks, as well as grabbing
|
||||
the command name argument.
|
||||
"""
|
||||
|
||||
words = list(words)
|
||||
for word in list(words):
|
||||
wtree = pyshlex.make_wordtree(word[1])
|
||||
for part in wtree:
|
||||
if not isinstance(part, list):
|
||||
continue
|
||||
|
||||
if part[0] in ('`', '$('):
|
||||
command = pyshlex.wordtree_as_string(part[1:-1])
|
||||
self.parse_shell(command)
|
||||
|
||||
if word[0] in ("cmd_name", "cmd_word"):
|
||||
if word in words:
|
||||
words.remove(word)
|
||||
|
||||
usetoken = False
|
||||
for word in words:
|
||||
if word[0] in ("cmd_name", "cmd_word") or \
|
||||
(usetoken and word[0] == "TOKEN"):
|
||||
if "=" in word[1]:
|
||||
usetoken = True
|
||||
continue
|
||||
|
||||
cmd = word[1]
|
||||
if cmd.startswith("$"):
|
||||
msg.debug(1, None, "Warning: execution of non-literal command '%s'" % cmd)
|
||||
elif cmd == "eval":
|
||||
command = " ".join(word for _, word in words[1:])
|
||||
self.parse_shell(command)
|
||||
else:
|
||||
self.allexecs.add(cmd)
|
||||
break
|
||||
|
||||
280
bitbake/lib/bb/command.py
Normal file
280
bitbake/lib/bb/command.py
Normal file
@@ -0,0 +1,280 @@
|
||||
"""
|
||||
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.event
|
||||
import bb.cooker
|
||||
import bb.data
|
||||
|
||||
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 KeyboardInterrupt as exc:
|
||||
self.finishAsyncCommand("Interrupted")
|
||||
return False
|
||||
except SystemExit as exc:
|
||||
arg = exc.args[0]
|
||||
if isinstance(arg, basestring):
|
||||
self.finishAsyncCommand(arg)
|
||||
else:
|
||||
self.finishAsyncCommand("Exited with %s" % arg)
|
||||
return False
|
||||
except Exception:
|
||||
import traceback
|
||||
self.finishAsyncCommand(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def finishAsyncCommand(self, error = None):
|
||||
if error:
|
||||
bb.event.fire(CookerCommandFailed(error), self.cooker.configuration.event_data)
|
||||
else:
|
||||
bb.event.fire(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)
|
||||
1023
bitbake/lib/bb/cooker.py
Normal file
1023
bitbake/lib/bb/cooker.py
Normal file
File diff suppressed because it is too large
Load Diff
190
bitbake/lib/bb/daemonize.py
Normal file
190
bitbake/lib/bb/daemonize.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""
|
||||
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 as 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 as 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)
|
||||
330
bitbake/lib/bb/data.py
Normal file
330
bitbake/lib/bb/data.py
Normal file
@@ -0,0 +1,330 @@
|
||||
# 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
|
||||
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 itertools import groupby
|
||||
|
||||
from bb import data_smart
|
||||
from bb import codeparser
|
||||
import bb
|
||||
|
||||
_dict_type = data_smart.DataSmart
|
||||
|
||||
def init():
|
||||
"""Return a new object representing the Bitbake data"""
|
||||
return _dict_type()
|
||||
|
||||
def init_db(parent = None):
|
||||
"""Return a new object representing the Bitbake data,
|
||||
optionally based on an existing object"""
|
||||
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"""
|
||||
d.setVar(var, value)
|
||||
|
||||
|
||||
def getVar(var, d, exp = 0):
|
||||
"""Gets the value of a variable"""
|
||||
return d.getVar(var, exp)
|
||||
|
||||
|
||||
def renameVar(key, newkey, d):
|
||||
"""Renames a variable from key to newkey"""
|
||||
d.renameVar(key, newkey)
|
||||
|
||||
def delVar(var, d):
|
||||
"""Removes a variable from the data set"""
|
||||
d.delVar(var)
|
||||
|
||||
def setVarFlag(var, flag, flagvalue, d):
|
||||
"""Set a flag for a given variable to a given value"""
|
||||
d.setVarFlag(var, flag, flagvalue)
|
||||
|
||||
def getVarFlag(var, flag, d):
|
||||
"""Gets given flag from given var"""
|
||||
return d.getVarFlag(var, flag)
|
||||
|
||||
def delVarFlag(var, flag, d):
|
||||
"""Removes a given flag from the variable's flags"""
|
||||
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
|
||||
"""
|
||||
d.setVarFlags(var, flags)
|
||||
|
||||
def getVarFlags(var, d):
|
||||
"""Gets a variable's flags"""
|
||||
return d.getVarFlags(var)
|
||||
|
||||
def delVarFlags(var, d):
|
||||
"""Removes a variable's flags"""
|
||||
d.delVarFlags(var)
|
||||
|
||||
def keys(d):
|
||||
"""Return a list of keys in d"""
|
||||
return d.keys()
|
||||
|
||||
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
|
||||
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
||||
|
||||
def expand(s, d, varname = None):
|
||||
"""Variable expansion using the data store"""
|
||||
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 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, bb.build.FuncFailed):
|
||||
raise
|
||||
except Exception, exc:
|
||||
o.write('# expansion of %s threw %s: %s\n' % (var, exc.__class__.__name__, str(exc)))
|
||||
return 0
|
||||
|
||||
if all:
|
||||
o.write('# %s=%s\n' % (var, oval))
|
||||
|
||||
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
|
||||
|
||||
if not val:
|
||||
return 0
|
||||
|
||||
val = str(val)
|
||||
|
||||
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."""
|
||||
|
||||
isfunc = lambda key: bool(d.getVarFlag(key, "func"))
|
||||
keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
|
||||
grouped = groupby(keys, isfunc)
|
||||
for isfunc, keys in grouped:
|
||||
for key in keys:
|
||||
emit_var(key, o, d, all and not isfunc) and o.write('\n')
|
||||
|
||||
def export_vars(d):
|
||||
keys = (key for key in d.keys() if d.getVarFlag(key, "export"))
|
||||
ret = {}
|
||||
for k in keys:
|
||||
try:
|
||||
v = d.getVar(k, True)
|
||||
if v:
|
||||
ret[k] = v
|
||||
except (KeyboardInterrupt, bb.build.FuncFailed):
|
||||
raise
|
||||
except Exception, exc:
|
||||
pass
|
||||
return ret
|
||||
|
||||
def emit_func(func, o=sys.__stdout__, d = init()):
|
||||
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
||||
|
||||
keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func"))
|
||||
for key in keys:
|
||||
emit_var(key, o, d, False) and o.write('\n')
|
||||
|
||||
emit_var(func, o, d, False) and o.write('\n')
|
||||
newdeps = bb.codeparser.ShellParser().parse_shell(d.getVar(func, True))
|
||||
seen = set()
|
||||
while newdeps:
|
||||
deps = newdeps
|
||||
seen |= deps
|
||||
newdeps = set()
|
||||
for dep in deps:
|
||||
if bb.data.getVarFlag(dep, "func", d):
|
||||
emit_var(dep, o, d, False) and o.write('\n')
|
||||
newdeps |= bb.codeparser.ShellParser().parse_shell(d.getVar(dep, True))
|
||||
newdeps -= seen
|
||||
|
||||
def update_data(d):
|
||||
"""Performs final steps upon the datastore, including application of overrides"""
|
||||
d.finalize()
|
||||
|
||||
def build_dependencies(key, keys, shelldeps, d):
|
||||
deps = set()
|
||||
try:
|
||||
if d.getVarFlag(key, "func"):
|
||||
if d.getVarFlag(key, "python"):
|
||||
parsedvar = d.expandWithRefs(d.getVar(key, False), key)
|
||||
parser = bb.codeparser.PythonParser()
|
||||
parser.parse_python(parsedvar.value)
|
||||
deps = deps | parser.references
|
||||
else:
|
||||
parsedvar = d.expandWithRefs(d.getVar(key, False), key)
|
||||
parser = bb.codeparser.ShellParser()
|
||||
parser.parse_shell(parsedvar.value)
|
||||
deps = deps | shelldeps
|
||||
deps = deps | parsedvar.references
|
||||
deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
|
||||
else:
|
||||
parser = d.expandWithRefs(d.getVar(key, False), key)
|
||||
deps |= parser.references
|
||||
deps = deps | (keys & parser.execs)
|
||||
deps |= set((d.getVarFlag(key, "vardeps") or "").split())
|
||||
except:
|
||||
bb.note("Error expanding variable %s" % key)
|
||||
raise
|
||||
return deps
|
||||
#bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
|
||||
#d.setVarFlag(key, "vardeps", deps)
|
||||
|
||||
def generate_dependencies(d):
|
||||
|
||||
keys = set(key for key in d.keys() if not key.startswith("__"))
|
||||
shelldeps = set(key for key in keys if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||
|
||||
deps = {}
|
||||
taskdeps = {}
|
||||
|
||||
tasklist = bb.data.getVar('__BBTASKS', d) or []
|
||||
for task in tasklist:
|
||||
deps[task] = build_dependencies(task, keys, shelldeps, d)
|
||||
|
||||
newdeps = deps[task]
|
||||
seen = set()
|
||||
while newdeps:
|
||||
nextdeps = newdeps
|
||||
seen |= nextdeps
|
||||
newdeps = set()
|
||||
for dep in nextdeps:
|
||||
if dep not in deps:
|
||||
deps[dep] = build_dependencies(dep, keys, shelldeps, d)
|
||||
newdeps |= deps[dep]
|
||||
newdeps -= seen
|
||||
taskdeps[task] = seen | newdeps
|
||||
#print "For %s: %s" % (task, str(taskdeps[task]))
|
||||
return taskdeps, deps
|
||||
|
||||
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
|
||||
371
bitbake/lib/bb/data_smart.py
Normal file
371
bitbake/lib/bb/data_smart.py
Normal file
@@ -0,0 +1,371 @@
|
||||
# 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, re, sys
|
||||
import bb
|
||||
from bb import utils
|
||||
from bb.COW import COWDictBase
|
||||
|
||||
|
||||
__setvar_keyword__ = ["_append", "_prepend"]
|
||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?')
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
|
||||
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
||||
|
||||
class VariableParse:
|
||||
def __init__(self, varname, d, val = None):
|
||||
self.varname = varname
|
||||
self.d = d
|
||||
self.value = val
|
||||
|
||||
self.references = set()
|
||||
self.execs = set()
|
||||
|
||||
def var_sub(self, match):
|
||||
key = match.group()[2:-1]
|
||||
if self.varname and key:
|
||||
if self.varname == key:
|
||||
raise Exception("variable %s references itself!" % self.varname)
|
||||
var = self.d.getVar(key, 1)
|
||||
if var is not None:
|
||||
self.references.add(key)
|
||||
return var
|
||||
else:
|
||||
return match.group()
|
||||
|
||||
def python_sub(self, match):
|
||||
code = match.group()[3:-1]
|
||||
codeobj = compile(code.strip(), self.varname or "<expansion>", "eval")
|
||||
|
||||
parser = bb.codeparser.PythonParser()
|
||||
parser.parse_python(code)
|
||||
self.references |= parser.references
|
||||
self.execs |= parser.execs
|
||||
|
||||
value = utils.better_eval(codeobj, {"d": self.d})
|
||||
return str(value)
|
||||
|
||||
|
||||
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 expandWithRefs(self, s, varname):
|
||||
|
||||
if not isinstance(s, basestring): # sanity check
|
||||
return VariableParse(varname, self, s)
|
||||
|
||||
if varname and varname in self.expand_cache:
|
||||
return self.expand_cache[varname]
|
||||
|
||||
varparse = VariableParse(varname, self)
|
||||
|
||||
while s.find('${') != -1:
|
||||
olds = s
|
||||
try:
|
||||
s = __expand_var_regexp__.sub(varparse.var_sub, s)
|
||||
s = __expand_python_regexp__.sub(varparse.python_sub, s)
|
||||
if s == olds:
|
||||
break
|
||||
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
|
||||
|
||||
varparse.value = s
|
||||
|
||||
if varname:
|
||||
self.expand_cache[varname] = varparse
|
||||
|
||||
return varparse
|
||||
|
||||
def expand(self, s, varname):
|
||||
return self.expandWithRefs(s, varname).value
|
||||
|
||||
|
||||
def finalize(self):
|
||||
"""Performs final steps upon the datastore, including application of overrides"""
|
||||
|
||||
overrides = (self.getVar("OVERRIDES", True) 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 o not in self._seen_overrides:
|
||||
continue
|
||||
|
||||
vars = self._seen_overrides[o]
|
||||
for var in vars:
|
||||
name = var[:-l]
|
||||
try:
|
||||
self[name] = self[var]
|
||||
except Exception:
|
||||
bb.msg.note(1, bb.msg.domain.Data, "Untracked delVar")
|
||||
|
||||
# now on to the appends and prepends
|
||||
if "_append" in self._special_values:
|
||||
appends = self._special_values["_append"] or []
|
||||
for append in appends:
|
||||
for (a, o) in self.getVarFlag(append, "_append") or []:
|
||||
# maybe the OVERRIDE was not yet added so keep the append
|
||||
if (o and o in overrides) or not o:
|
||||
self.delVarFlag(append, "_append")
|
||||
if o and not o in overrides:
|
||||
continue
|
||||
|
||||
sval = self.getVar(append, False) or ""
|
||||
sval += a
|
||||
self.setVar(append, sval)
|
||||
|
||||
|
||||
if "_prepend" in self._special_values:
|
||||
prepends = self._special_values["_prepend"] or []
|
||||
|
||||
for prepend in prepends:
|
||||
for (a, o) in self.getVarFlag(prepend, "_prepend") or []:
|
||||
# maybe the OVERRIDE was not yet added so keep the prepend
|
||||
if (o and o in overrides) or not o:
|
||||
self.delVarFlag(prepend, "_prepend")
|
||||
if o and not o in overrides:
|
||||
continue
|
||||
|
||||
sval = a + (self.getVar(prepend, False) or "")
|
||||
self.setVar(prepend, sval)
|
||||
|
||||
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:
|
||||
if var in dest:
|
||||
return dest[var]
|
||||
|
||||
if "_data" not in dest:
|
||||
break
|
||||
dest = dest["_data"]
|
||||
|
||||
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 KeyError:
|
||||
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 override not in self._seen_overrides:
|
||||
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 i in self._special_values 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:
|
||||
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:
|
||||
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)
|
||||
300
bitbake/lib/bb/event.py
Normal file
300
bitbake/lib/bb/event.py
Normal file
@@ -0,0 +1,300 @@
|
||||
# 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, sys
|
||||
import warnings
|
||||
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
|
||||
useStdout = True
|
||||
|
||||
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
|
||||
|
||||
# For compatibility
|
||||
bb.utils._context["NotHandled"] = NotHandled
|
||||
bb.utils._context["Handled"] = Handled
|
||||
|
||||
def fire_class_handlers(event, d):
|
||||
import bb.msg
|
||||
if isinstance(event, bb.msg.MsgBase):
|
||||
return
|
||||
|
||||
for handler in _handlers:
|
||||
h = _handlers[handler]
|
||||
event.data = d
|
||||
if type(h).__name__ == "code":
|
||||
locals = {"e": event}
|
||||
bb.utils.simple_exec(h, locals)
|
||||
ret = bb.utils.better_eval("tmpHandler(e)", locals)
|
||||
if ret is not None:
|
||||
warnings.warn("Using Handled/NotHandled in event handlers is deprecated",
|
||||
DeprecationWarning, stacklevel = 2)
|
||||
else:
|
||||
h(event)
|
||||
del event.data
|
||||
|
||||
def fire_ui_handlers(event, d):
|
||||
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 fire(event, d):
|
||||
"""Fire off an Event"""
|
||||
|
||||
# We can fire class handlers in the worker process context and this is
|
||||
# desired so they get the task based datastore.
|
||||
# UI handlers need to be fired in the server context so we defer this. They
|
||||
# don't have a datastore so the datastore context isn't a problem.
|
||||
|
||||
fire_class_handlers(event, d)
|
||||
if worker_pid != 0:
|
||||
worker_fire(event, d)
|
||||
else:
|
||||
fire_ui_handlers(event, d)
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_pipe.write(data)
|
||||
worker_pipe.flush()
|
||||
|
||||
def fire_from_worker(event, d):
|
||||
if not event.startswith("<event>") or not event.endswith("</event>"):
|
||||
print("Error, not an event %s" % event)
|
||||
return
|
||||
#print "Got event %s" % event
|
||||
event = pickle.loads(event[7:-8])
|
||||
fire_ui_handlers(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
|
||||
bb.event.useStdout = False
|
||||
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, dependees=None):
|
||||
Event.__init__(self)
|
||||
self._item = item
|
||||
self._runtime = runtime
|
||||
self._dependees = dependees
|
||||
|
||||
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
|
||||
784
bitbake/lib/bb/fetch/__init__.py
Normal file
784
bitbake/lib/bb/fetch/__init__.py
Normal file
@@ -0,0 +1,784 @@
|
||||
# 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
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
import os, re
|
||||
import bb
|
||||
from bb import data
|
||||
from bb import persist_data
|
||||
|
||||
class MalformedUrl(Exception):
|
||||
"""Exception raised when encountering an invalid url"""
|
||||
|
||||
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 decodeurl(url):
|
||||
"""Decodes an URL into the tokens (scheme, network location, path,
|
||||
user, password, parameters).
|
||||
"""
|
||||
|
||||
m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').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 and type.lower() != 'file':
|
||||
host = location[:locidx]
|
||||
path = location[locidx:]
|
||||
else:
|
||||
host = ""
|
||||
path = location
|
||||
if user:
|
||||
m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').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).
|
||||
"""
|
||||
|
||||
(type, host, path, user, pswd, p) = decoded
|
||||
|
||||
if not type or not path:
|
||||
raise MissingParameterError("Type or path url components missing when encoding %s" % decoded)
|
||||
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:
|
||||
url += ";%s=%s" % (parm, p[parm])
|
||||
|
||||
return url
|
||||
|
||||
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(decodeurl(uri))
|
||||
uri_find_decoded = list(decodeurl(uri_find))
|
||||
uri_replace_decoded = list(decodeurl(uri_replace))
|
||||
result_decoded = ['', '', '', '', '', {}]
|
||||
for i in uri_find_decoded:
|
||||
loc = uri_find_decoded.index(i)
|
||||
result_decoded[loc] = uri_decoded[loc]
|
||||
if isinstance(i, basestring):
|
||||
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:
|
||||
# FIXME: apply replacements against options
|
||||
return encodeurl(result_decoded)
|
||||
|
||||
methods = []
|
||||
urldata_cache = {}
|
||||
saved_headrevs = {}
|
||||
persistent_database_connection = {}
|
||||
|
||||
def fetcher_init(d):
|
||||
"""
|
||||
Called to initialize the fetchers once the configuration data is known.
|
||||
Calls before this must not hit the cache.
|
||||
"""
|
||||
pd = persist_data.PersistData(d, persistent_database_connection)
|
||||
# When to drop SCM head revisions controlled 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:
|
||||
raise FetchError("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, persistent_database_connection)
|
||||
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 mirror_from_string(data):
|
||||
return [ i.split() for i in (data or "").replace('\\n','\n').split('\n') if i ]
|
||||
|
||||
def removefile(f):
|
||||
try:
|
||||
os.remove(f)
|
||||
except:
|
||||
pass
|
||||
|
||||
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
|
||||
localpath = ""
|
||||
|
||||
if not ud.localfile:
|
||||
continue
|
||||
|
||||
lf = bb.utils.lockfile(ud.lockfile)
|
||||
|
||||
if m.try_premirror(u, ud, d):
|
||||
# First try fetching uri, u, from PREMIRRORS
|
||||
mirrors = mirror_from_string(bb.data.getVar('PREMIRRORS', d, True))
|
||||
localpath = try_mirrors(d, u, mirrors, False, m.forcefetch(u, ud, d))
|
||||
elif os.path.exists(ud.localfile):
|
||||
localpath = ud.localfile
|
||||
|
||||
# Need to re-test forcefetch() which will return true if our copy is too old
|
||||
if m.forcefetch(u, ud, d) or not localpath:
|
||||
# Next try fetching from the original uri, u
|
||||
try:
|
||||
m.go(u, ud, d)
|
||||
localpath = ud.localpath
|
||||
except FetchError:
|
||||
# Remove any incomplete file
|
||||
removefile(ud.localpath)
|
||||
# Finally, try fetching uri, u, from MIRRORS
|
||||
mirrors = mirror_from_string(bb.data.getVar('MIRRORS', d, True))
|
||||
localpath = try_mirrors (d, u, mirrors)
|
||||
if not localpath or not os.path.exists(localpath):
|
||||
raise FetchError("Unable to fetch URL %s from any source." % u)
|
||||
|
||||
ud.localpath = localpath
|
||||
if os.path.exists(ud.md5):
|
||||
# Touch the md5 file to show active use of the download
|
||||
try:
|
||||
os.utime(ud.md5, None)
|
||||
except:
|
||||
# Errors aren't fatal here
|
||||
pass
|
||||
else:
|
||||
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.debug(1, bb.msg.domain.Fetcher, "Testing URL %s" % u)
|
||||
# First try checking uri, u, from PREMIRRORS
|
||||
mirrors = mirror_from_string(bb.data.getVar('PREMIRRORS', d, True))
|
||||
ret = try_mirrors(d, u, mirrors, True)
|
||||
if not ret:
|
||||
# Next try checking from the original uri, u
|
||||
try:
|
||||
ret = m.checkstatus(u, ud, d)
|
||||
except:
|
||||
# Finally, try checking uri, u, from MIRRORS
|
||||
mirrors = mirror_from_string(bb.data.getVar('MIRRORS', d, True))
|
||||
ret = try_mirrors (d, u, mirrors, True)
|
||||
|
||||
if not ret:
|
||||
raise FetchError("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
|
||||
|
||||
if bb.data.getVar('BB_SRCREV_POLICY', d, True) != "cache":
|
||||
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',
|
||||
'https_proxy', 'no_proxy', 'ALL_PROXY', 'all_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 True:
|
||||
line = stdout_handle.readline()
|
||||
if not line:
|
||||
break
|
||||
if not quiet:
|
||||
print(line, end=' ')
|
||||
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
|
||||
|
||||
def try_mirrors(d, uri, mirrors, check = False, force = False):
|
||||
"""
|
||||
Try to use a mirrored version of the sources.
|
||||
This method will be automatically called before the fetchers go.
|
||||
|
||||
d Is a bb.data instance
|
||||
uri is the original uri we're trying to download
|
||||
mirrors is the list of mirrors we're going to try
|
||||
"""
|
||||
fpath = os.path.join(data.getVar("DL_DIR", d, 1), os.path.basename(uri))
|
||||
if not check and os.access(fpath, os.R_OK) and not force:
|
||||
bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists, skipping checkout." % fpath)
|
||||
return fpath
|
||||
|
||||
ld = d.createCopy()
|
||||
for (find, replace) in mirrors:
|
||||
newuri = uri_replace(uri, find, replace, ld)
|
||||
if newuri != uri:
|
||||
try:
|
||||
ud = FetchData(newuri, ld)
|
||||
except bb.fetch.NoMethodError:
|
||||
bb.msg.debug(1, bb.msg.domain.Fetcher, "No method for %s" % uri)
|
||||
continue
|
||||
|
||||
ud.setup_localpath(ld)
|
||||
|
||||
try:
|
||||
if check:
|
||||
ud.method.checkstatus(newuri, ud, ld)
|
||||
else:
|
||||
ud.method.go(newuri, ud, ld)
|
||||
return ud.localpath
|
||||
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, "Mirror fetch failure: %s" % value)
|
||||
removefile(ud.localpath)
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
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) = 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"]
|
||||
self.basename = os.path.basename(self.localpath)
|
||||
else:
|
||||
premirrors = bb.data.getVar('PREMIRRORS', d, True)
|
||||
local = ""
|
||||
if premirrors and self.url:
|
||||
aurl = self.url.split(";")[0]
|
||||
mirrors = mirror_from_string(premirrors)
|
||||
for (find, replace) in mirrors:
|
||||
if replace.startswith("file://"):
|
||||
path = aurl.split("://")[1]
|
||||
path = path.split(";")[0]
|
||||
local = replace.split("://")[1] + os.path.basename(path)
|
||||
if local == aurl or not os.path.exists(local) or os.path.isdir(local):
|
||||
local = ""
|
||||
self.localpath = local
|
||||
if not local:
|
||||
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)
|
||||
|
||||
if self.localpath is not None:
|
||||
# Note: These files should always be in DL_DIR whereas localpath may not be.
|
||||
basepath = bb.data.expand("${DL_DIR}/%s" % os.path.basename(self.localpath), d)
|
||||
self.md5 = basepath + '.md5'
|
||||
self.lockfile = basepath + '.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 try_premirror(self, url, urldata, d):
|
||||
"""
|
||||
Should premirrors be used?
|
||||
"""
|
||||
if urldata.method.forcefetch(url, urldata, d):
|
||||
return True
|
||||
elif os.path.exists(urldata.md5) and os.path.exists(urldata.localfile):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
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_%s_pn-%s" % (ud.parm['name'], pn), d, 1)
|
||||
if not rev:
|
||||
rev = data.getVar("SRCREV_pn-%s_%s" % (pn, ud.parm['name']), d, 1)
|
||||
if not rev:
|
||||
rev = data.getVar("SRCREV_%s" % (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 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, persistent_database_connection)
|
||||
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, persistent_database_connection)
|
||||
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 "")
|
||||
|
||||
from . import cvs
|
||||
from . import git
|
||||
from . import local
|
||||
from . import svn
|
||||
from . import wget
|
||||
from . import svk
|
||||
from . import ssh
|
||||
from . import perforce
|
||||
from . import bzr
|
||||
from . import hg
|
||||
from . import osc
|
||||
from . import repo
|
||||
|
||||
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())
|
||||
methods.append(repo.Repo())
|
||||
147
bitbake/lib/bb/fetch/bzr.py
Normal file
147
bitbake/lib/bb/fetch/bzr.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""
|
||||
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"""
|
||||
|
||||
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
|
||||
177
bitbake/lib/bb/fetch/cvs.py
Normal file
177
bitbake/lib/bb/fetch/cvs.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# 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):
|
||||
|
||||
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)
|
||||
254
bitbake/lib/bb/fetch/git.py
Normal file
254
bitbake/lib/bb/fetch/git.py
Normal file
@@ -0,0 +1,254 @@
|
||||
# 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)
|
||||
|
||||
ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
|
||||
|
||||
if 'noclone' in ud.parm:
|
||||
ud.localfile = None
|
||||
return None
|
||||
|
||||
return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
|
||||
|
||||
def forcefetch(self, url, ud, d):
|
||||
if 'fullclone' in ud.parm:
|
||||
return True
|
||||
if 'noclone' in ud.parm:
|
||||
return False
|
||||
if os.path.exists(ud.localpath):
|
||||
return False
|
||||
if not self._contains_ref(ud.tag, d):
|
||||
return True
|
||||
return False
|
||||
|
||||
def try_premirror(self, u, ud, d):
|
||||
if 'noclone' in ud.parm:
|
||||
return False
|
||||
if os.path.exists(ud.clonedir):
|
||||
return False
|
||||
if os.path.exists(ud.localpath):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def go(self, loc, ud, d):
|
||||
"""Fetch url"""
|
||||
|
||||
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 we have no existing clone and no mirror tarball, try and obtain one
|
||||
if not os.path.exists(ud.clonedir) and not os.path.exists(repofile):
|
||||
try:
|
||||
Fetch.try_mirrors(ud.mirrortarball)
|
||||
except:
|
||||
pass
|
||||
|
||||
# If the checkout doesn't exist and the mirror tarball does, extract it
|
||||
if not os.path.exists(ud.clonedir) and os.path.exists(repofile):
|
||||
bb.mkdirhier(ud.clonedir)
|
||||
os.chdir(ud.clonedir)
|
||||
runfetchcmd("tar -xzf %s" % (repofile), d)
|
||||
|
||||
# If the repo still doesn't exist, fallback to cloning it
|
||||
if not os.path.exists(ud.clonedir):
|
||||
runfetchcmd("%s clone -n %s://%s%s%s %s" % (ud.basecmd, ud.proto, username, ud.host, ud.path, ud.clonedir), d)
|
||||
|
||||
os.chdir(ud.clonedir)
|
||||
# Update the checkout if needed
|
||||
if not self._contains_ref(ud.tag, d) or 'fullclone' in ud.parm:
|
||||
# Remove all but the .git directory
|
||||
runfetchcmd("rm * -Rf", d)
|
||||
if 'fullclone' in ud.parm:
|
||||
runfetchcmd("%s fetch --all" % (ud.basecmd), d)
|
||||
else:
|
||||
runfetchcmd("%s fetch %s://%s%s%s %s" % (ud.basecmd, ud.proto, username, ud.host, ud.path, ud.branch), d)
|
||||
runfetchcmd("%s fetch --tags %s://%s%s%s" % (ud.basecmd, ud.proto, username, ud.host, ud.path), d)
|
||||
runfetchcmd("%s prune-packed" % ud.basecmd, d)
|
||||
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
|
||||
|
||||
# Generate a mirror tarball if needed
|
||||
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("%s read-tree %s%s" % (ud.basecmd, ud.tag, readpathspec), d)
|
||||
runfetchcmd("%s checkout-index -q -f --prefix=%s -a" % (ud.basecmd, 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):
|
||||
basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
|
||||
output = runfetchcmd("%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (basecmd, 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('/', '.') + ud.branch
|
||||
|
||||
def _latest_revision(self, url, ud, d):
|
||||
"""
|
||||
Compute the HEAD revision for the url
|
||||
"""
|
||||
if ud.user:
|
||||
username = ud.user + '@'
|
||||
else:
|
||||
username = ""
|
||||
|
||||
basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
|
||||
cmd = "%s ls-remote %s://%s%s%s %s" % (basecmd, 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("%s rev-list %s -- 2> /dev/null | wc -l" % (ud.basecmd, 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, ud.clonedir, buildindex, rev))
|
||||
return buildindex
|
||||
172
bitbake/lib/bb/fetch/hg.py
Normal file
172
bitbake/lib/bb/fetch/hg.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# 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"""
|
||||
|
||||
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
|
||||
73
bitbake/lib/bb/fetch/local.py
Normal file
73
bitbake/lib/bb/fetch/local.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# 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
|
||||
import bb.utils
|
||||
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.utils.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
|
||||
150
bitbake/lib/bb/fetch/osc.py
Normal file
150
bitbake/lib/bb/fetch/osc.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# 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
|
||||
"""
|
||||
|
||||
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
|
||||
208
bitbake/lib/bb/fetch/perforce.py
Normal file
208
bitbake/lib/bb/fetch/perforce.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# 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
|
||||
|
||||
from future_builtins import zip
|
||||
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
|
||||
"""
|
||||
|
||||
(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.msg.error(bb.msg.domain.Fetcher, "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.msg.error(bb.msg.domain.Fetcher, "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.msg.error(bb.msg.domain.Fetcher, "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)
|
||||
105
bitbake/lib/bb/fetch/repo.py
Normal file
105
bitbake/lib/bb/fetch/repo.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
"""
|
||||
BitBake "Fetch" repo (git) implementation
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (C) 2009 Tom Rini <trini@embeddedalley.com>
|
||||
#
|
||||
# Based on git.py which is:
|
||||
#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 Repo(Fetch):
|
||||
"""Class to fetch a module or modules from repo (git) repositories"""
|
||||
def supports(self, url, ud, d):
|
||||
"""
|
||||
Check to see if a given url can be fetched with repo.
|
||||
"""
|
||||
return ud.type in ["repo"]
|
||||
|
||||
def localpath(self, url, ud, d):
|
||||
"""
|
||||
We don"t care about the git rev of the manifests repository, but
|
||||
we do care about the manifest to use. The default is "default".
|
||||
We also care about the branch or tag to be used. The default is
|
||||
"master".
|
||||
"""
|
||||
|
||||
if "protocol" in ud.parm:
|
||||
ud.proto = ud.parm["protocol"]
|
||||
else:
|
||||
ud.proto = "git"
|
||||
|
||||
if "branch" in ud.parm:
|
||||
ud.branch = ud.parm["branch"]
|
||||
else:
|
||||
ud.branch = "master"
|
||||
|
||||
if "manifest" in ud.parm:
|
||||
manifest = ud.parm["manifest"]
|
||||
if manifest.endswith(".xml"):
|
||||
ud.manifest = manifest
|
||||
else:
|
||||
ud.manifest = manifest + ".xml"
|
||||
else:
|
||||
ud.manifest = "default.xml"
|
||||
|
||||
ud.localfile = data.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch), d)
|
||||
|
||||
return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
|
||||
|
||||
def go(self, loc, ud, d):
|
||||
"""Fetch url"""
|
||||
|
||||
if os.access(os.path.join(data.getVar("DL_DIR", d, True), ud.localfile), os.R_OK):
|
||||
bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists (or was stashed). Skipping repo init / sync." % ud.localpath)
|
||||
return
|
||||
|
||||
gitsrcname = "%s%s" % (ud.host, ud.path.replace("/", "."))
|
||||
repodir = data.getVar("REPODIR", d, True) or os.path.join(data.getVar("DL_DIR", d, True), "repo")
|
||||
codir = os.path.join(repodir, gitsrcname, ud.manifest)
|
||||
|
||||
if ud.user:
|
||||
username = ud.user + "@"
|
||||
else:
|
||||
username = ""
|
||||
|
||||
bb.mkdirhier(os.path.join(codir, "repo"))
|
||||
os.chdir(os.path.join(codir, "repo"))
|
||||
if not os.path.exists(os.path.join(codir, "repo", ".repo")):
|
||||
runfetchcmd("repo init -m %s -b %s -u %s://%s%s%s" % (ud.manifest, ud.branch, ud.proto, username, ud.host, ud.path), d)
|
||||
|
||||
runfetchcmd("repo sync", d)
|
||||
os.chdir(codir)
|
||||
|
||||
# Create a cache
|
||||
runfetchcmd("tar --exclude=.repo --exclude=.git -czf %s %s" % (ud.localpath, os.path.join(".", "*") ), d)
|
||||
|
||||
def suppports_srcrev(self):
|
||||
return False
|
||||
|
||||
def _build_revision(self, url, ud, d):
|
||||
return ud.manifest
|
||||
|
||||
def _want_sortable_revision(self, url, ud, d):
|
||||
return False
|
||||
118
bitbake/lib/bb/fetch/ssh.py
Normal file
118
bitbake/lib/bb/fetch/ssh.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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<user>\S+) # username
|
||||
(:(?P<pass>\S+))? # colon followed by the password (optional)
|
||||
)?
|
||||
(?P<cparam>(;[^;]+)*)? # connection parameters block (optional)
|
||||
@
|
||||
(?P<host>\S+?) # non-greedy match of the host
|
||||
(:(?P<port>[0-9]+))? # colon followed by the port (optional)
|
||||
/
|
||||
(?P<path>[^;]+) # path on the remote system, may be absolute or relative,
|
||||
# and may include the use of '~' to reference the remote home
|
||||
# directory
|
||||
(?P<sparam>(;[^;]+)*)? # 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)
|
||||
106
bitbake/lib/bb/fetch/svk.py
Normal file
106
bitbake/lib/bb/fetch/svk.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# 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"""
|
||||
|
||||
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)
|
||||
201
bitbake/lib/bb/fetch/svn.py
Normal file
201
bitbake/lib/bb/fetch/svn.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# 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"""
|
||||
|
||||
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
|
||||
95
bitbake/lib/bb/fetch/wget.py
Normal file
95
bitbake/lib/bb/fetch/wget.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# 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 encodeurl, decodeurl
|
||||
from bb.fetch import runfetchcmd
|
||||
|
||||
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 = 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(decodeurl(uri))
|
||||
uri_type = uri_decoded[0]
|
||||
uri_host = uri_decoded[1]
|
||||
|
||||
fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0])
|
||||
fetchcmd = fetchcmd.replace("${FILE}", ud.basename)
|
||||
|
||||
bb.msg.note(1, bb.msg.domain.Fetcher, "fetch " + uri)
|
||||
bb.msg.debug(2, bb.msg.domain.Fetcher, "executing " + fetchcmd)
|
||||
runfetchcmd(fetchcmd, d)
|
||||
|
||||
# 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)
|
||||
|
||||
if fetch_uri(uri, ud, localdata):
|
||||
return True
|
||||
|
||||
raise FetchError(uri)
|
||||
|
||||
|
||||
def checkstatus(self, uri, ud, d):
|
||||
return self.go(uri, ud, d, True)
|
||||
84
bitbake/lib/bb/methodpool.py
Normal file
84
bitbake/lib/bb/methodpool.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# 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, modulename, fn )
|
||||
better_exec(comp, None, 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
|
||||
144
bitbake/lib/bb/msg.py
Normal file
144
bitbake/lib/bb/msg.py
Normal file
@@ -0,0 +1,144 @@
|
||||
# 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
|
||||
import collections
|
||||
import bb
|
||||
import bb.event
|
||||
|
||||
debug_level = collections.defaultdict(lambda: 0)
|
||||
verbose = False
|
||||
|
||||
def _NamedTuple(name, fields):
|
||||
Tuple = collections.namedtuple(name, " ".join(fields))
|
||||
return Tuple(*range(len(fields)))
|
||||
|
||||
domain = _NamedTuple("Domain", (
|
||||
"Default",
|
||||
"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
|
||||
bb.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):
|
||||
for d in domain:
|
||||
debug_level[d] = level
|
||||
debug_level[domain.Default] = level
|
||||
|
||||
def get_debug_level(msgdomain = domain.Default):
|
||||
return debug_level[msgdomain]
|
||||
|
||||
def set_verbose(level):
|
||||
verbose = level
|
||||
|
||||
def set_debug_domains(strdomains):
|
||||
for domainstr in strdomains:
|
||||
for d in domain:
|
||||
if domain._fields[d] == domainstr:
|
||||
debug_level[d] += 1
|
||||
break
|
||||
else:
|
||||
warn(None, "Logging domain %s is not valid, ignoring" % domainstr)
|
||||
|
||||
#
|
||||
# Message handling functions
|
||||
#
|
||||
|
||||
def debug(level, msgdomain, msg, fn = None):
|
||||
if not msgdomain:
|
||||
msgdomain = domain.Default
|
||||
|
||||
if debug_level[msgdomain] >= level:
|
||||
bb.event.fire(MsgDebug(msg), None)
|
||||
if bb.event.useStdout:
|
||||
print('DEBUG: %s' % (msg))
|
||||
|
||||
def note(level, msgdomain, msg, fn = None):
|
||||
if not msgdomain:
|
||||
msgdomain = domain.Default
|
||||
|
||||
if level == 1 or verbose or debug_level[msgdomain] >= 1:
|
||||
bb.event.fire(MsgNote(msg), None)
|
||||
if bb.event.useStdout:
|
||||
print('NOTE: %s' % (msg))
|
||||
|
||||
def warn(msgdomain, msg, fn = None):
|
||||
bb.event.fire(MsgWarn(msg), None)
|
||||
if bb.event.useStdout:
|
||||
print('WARNING: %s' % (msg))
|
||||
|
||||
def error(msgdomain, msg, fn = None):
|
||||
bb.event.fire(MsgError(msg), None)
|
||||
if bb.event.useStdout:
|
||||
print('ERROR: %s' % (msg))
|
||||
|
||||
def fatal(msgdomain, msg, fn = None):
|
||||
bb.event.fire(MsgFatal(msg), None)
|
||||
|
||||
if bb.event.useStdout:
|
||||
print('FATAL: %s' % (msg))
|
||||
sys.exit(1)
|
||||
|
||||
def plain(msg, fn = None):
|
||||
bb.event.fire(MsgPlain(msg), None)
|
||||
if bb.event.useStdout:
|
||||
print(msg)
|
||||
118
bitbake/lib/bb/parse/__init__.py
Normal file
118
bitbake/lib/bb/parse/__init__.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
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
|
||||
|
||||
handlers = []
|
||||
|
||||
import bb, os
|
||||
import bb.utils
|
||||
import bb.siggen
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Exception raised when parsing fails"""
|
||||
|
||||
class SkipPackage(Exception):
|
||||
"""Exception raised to skip this package"""
|
||||
|
||||
__mtime_cache = {}
|
||||
def cached_mtime(f):
|
||||
if f not in __mtime_cache:
|
||||
__mtime_cache[f] = os.stat(f)[8]
|
||||
return __mtime_cache[f]
|
||||
|
||||
def cached_mtime_noerror(f):
|
||||
if f not in __mtime_cache:
|
||||
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 set()
|
||||
deps.update([(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)
|
||||
|
||||
def init_parser(d, dumpsigs):
|
||||
bb.parse.siggen = bb.siggen.init(d, dumpsigs)
|
||||
|
||||
def resolve_file(fn, d):
|
||||
if not os.path.isabs(fn):
|
||||
bbpath = bb.data.getVar("BBPATH", d, True)
|
||||
newfn = bb.which(bbpath, fn)
|
||||
if not newfn:
|
||||
raise IOError("file %s not found in %s" % (fn, bbpath))
|
||||
fn = newfn
|
||||
|
||||
bb.msg.debug(2, bb.msg.domain.Parsing, "LOAD %s" % fn)
|
||||
return fn
|
||||
|
||||
# Used by OpenEmbedded metadata
|
||||
__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
|
||||
|
||||
from bb.parse.parse_py import __version__, ConfHandler, BBHandler
|
||||
445
bitbake/lib/bb/parse/ast.py
Normal file
445
bitbake/lib/bb/parse/ast.py
Normal file
@@ -0,0 +1,445 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
"""
|
||||
AbstractSyntaxTree classes for the Bitbake language
|
||||
"""
|
||||
|
||||
# Copyright (C) 2003, 2004 Chris Larson
|
||||
# Copyright (C) 2003, 2004 Phil Blundell
|
||||
# Copyright (C) 2009 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future_builtins import filter
|
||||
import bb, re, string
|
||||
from bb import methodpool
|
||||
import itertools
|
||||
|
||||
__word__ = re.compile(r"\S+")
|
||||
__parsed_methods__ = bb.methodpool.get_parsed_dict()
|
||||
_bbversions_re = re.compile(r"\[(?P<from>[0-9]+)-(?P<to>[0-9]+)\]")
|
||||
|
||||
class StatementGroup(list):
|
||||
def eval(self, data):
|
||||
for statement in self:
|
||||
statement.eval(data)
|
||||
|
||||
class AstNode(object):
|
||||
pass
|
||||
|
||||
class IncludeNode(AstNode):
|
||||
def __init__(self, what_file, fn, lineno, force):
|
||||
self.what_file = what_file
|
||||
self.from_fn = fn
|
||||
self.from_lineno = lineno
|
||||
self.force = force
|
||||
|
||||
def eval(self, data):
|
||||
"""
|
||||
Include the file and evaluate the statements
|
||||
"""
|
||||
s = bb.data.expand(self.what_file, data)
|
||||
bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (self.from_fn, self.from_lineno, s))
|
||||
|
||||
# TODO: Cache those includes... maybe not here though
|
||||
if self.force:
|
||||
bb.parse.ConfHandler.include(self.from_fn, s, data, "include required")
|
||||
else:
|
||||
bb.parse.ConfHandler.include(self.from_fn, s, data, False)
|
||||
|
||||
class ExportNode(AstNode):
|
||||
def __init__(self, var):
|
||||
self.var = var
|
||||
|
||||
def eval(self, data):
|
||||
bb.data.setVarFlag(self.var, "export", 1, data)
|
||||
|
||||
class DataNode(AstNode):
|
||||
"""
|
||||
Various data related updates. For the sake of sanity
|
||||
we have one class doing all this. This means that all
|
||||
this need to be re-evaluated... we might be able to do
|
||||
that faster with multiple classes.
|
||||
"""
|
||||
def __init__(self, groupd):
|
||||
self.groupd = groupd
|
||||
|
||||
def getFunc(self, key, data):
|
||||
if 'flag' in self.groupd and self.groupd['flag'] != None:
|
||||
return bb.data.getVarFlag(key, self.groupd['flag'], data)
|
||||
else:
|
||||
return bb.data.getVar(key, data)
|
||||
|
||||
def eval(self, data):
|
||||
groupd = self.groupd
|
||||
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 = self.getFunc(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" % ((self.getFunc(key, data) or ""), groupd["value"])
|
||||
elif "prepend" in groupd and groupd["prepend"] != None:
|
||||
val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
|
||||
elif "postdot" in groupd and groupd["postdot"] != None:
|
||||
val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
|
||||
elif "predot" in groupd and groupd["predot"] != None:
|
||||
val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
|
||||
else:
|
||||
val = groupd["value"]
|
||||
|
||||
if 'flag' in groupd and groupd['flag'] != None:
|
||||
bb.data.setVarFlag(key, groupd['flag'], val, data)
|
||||
elif groupd["lazyques"]:
|
||||
assigned = bb.data.getVar("__lazy_assigned", data) or []
|
||||
assigned.append(key)
|
||||
bb.data.setVar("__lazy_assigned", assigned, data)
|
||||
bb.data.setVarFlag(key, "defaultval", val, data)
|
||||
else:
|
||||
bb.data.setVar(key, val, data)
|
||||
|
||||
class MethodNode:
|
||||
def __init__(self, func_name, body, lineno, fn):
|
||||
self.func_name = func_name
|
||||
self.body = body
|
||||
self.fn = fn
|
||||
self.lineno = lineno
|
||||
|
||||
def eval(self, data):
|
||||
if self.func_name == "__anonymous":
|
||||
funcname = ("__anon_%s_%s" % (self.lineno, self.fn.translate(string.maketrans('/.+-', '____'))))
|
||||
if not funcname in bb.methodpool._parsed_fns:
|
||||
text = "def %s(d):\n" % (funcname) + '\n'.join(self.body)
|
||||
bb.methodpool.insert_method(funcname, text, self.fn)
|
||||
anonfuncs = bb.data.getVar('__BBANONFUNCS', data) or []
|
||||
anonfuncs.append(funcname)
|
||||
bb.data.setVar('__BBANONFUNCS', anonfuncs, data)
|
||||
else:
|
||||
bb.data.setVarFlag(self.func_name, "func", 1, data)
|
||||
bb.data.setVar(self.func_name, '\n'.join(self.body), data)
|
||||
|
||||
class PythonMethodNode(AstNode):
|
||||
def __init__(self, funcname, root, body, fn):
|
||||
self.func_name = funcname
|
||||
self.root = root
|
||||
self.body = body
|
||||
self.fn = fn
|
||||
|
||||
def eval(self, data):
|
||||
# Note we will add root to parsedmethods after having parse
|
||||
# 'this' file. This means we will not parse methods from
|
||||
# bb classes twice
|
||||
text = '\n'.join(self.body)
|
||||
if not bb.methodpool.parsed_module(self.root):
|
||||
bb.methodpool.insert_method(self.root, text, self.fn)
|
||||
bb.data.setVarFlag(self.func_name, "func", 1, data)
|
||||
bb.data.setVarFlag(self.func_name, "python", 1, data)
|
||||
bb.data.setVar(self.func_name, text, data)
|
||||
|
||||
class MethodFlagsNode(AstNode):
|
||||
def __init__(self, key, m):
|
||||
self.key = key
|
||||
self.m = m
|
||||
|
||||
def eval(self, data):
|
||||
if bb.data.getVar(self.key, data):
|
||||
# clean up old version of this piece of metadata, as its
|
||||
# flags could cause problems
|
||||
bb.data.setVarFlag(self.key, 'python', None, data)
|
||||
bb.data.setVarFlag(self.key, 'fakeroot', None, data)
|
||||
if self.m.group("py") is not None:
|
||||
bb.data.setVarFlag(self.key, "python", "1", data)
|
||||
else:
|
||||
bb.data.delVarFlag(self.key, "python", data)
|
||||
if self.m.group("fr") is not None:
|
||||
bb.data.setVarFlag(self.key, "fakeroot", "1", data)
|
||||
else:
|
||||
bb.data.delVarFlag(self.key, "fakeroot", data)
|
||||
|
||||
class ExportFuncsNode(AstNode):
|
||||
def __init__(self, fns, classes):
|
||||
self.n = __word__.findall(fns)
|
||||
self.classes = classes
|
||||
|
||||
def eval(self, data):
|
||||
for f in self.n:
|
||||
allvars = []
|
||||
allvars.append(f)
|
||||
allvars.append(self.classes[-1] + "_" + f)
|
||||
|
||||
vars = [[ allvars[0], allvars[1] ]]
|
||||
if len(self.classes) > 1 and self.classes[-2] is not None:
|
||||
allvars.append(self.classes[-2] + "_" + f)
|
||||
vars = []
|
||||
vars.append([allvars[2], allvars[1]])
|
||||
vars.append([allvars[0], allvars[2]])
|
||||
|
||||
for (var, calledvar) in vars:
|
||||
if bb.data.getVar(var, data) and not bb.data.getVarFlag(var, 'export_func', data):
|
||||
continue
|
||||
|
||||
if bb.data.getVar(var, data):
|
||||
bb.data.setVarFlag(var, 'python', None, data)
|
||||
bb.data.setVarFlag(var, 'func', None, data)
|
||||
|
||||
for flag in [ "func", "python" ]:
|
||||
if bb.data.getVarFlag(calledvar, flag, data):
|
||||
bb.data.setVarFlag(var, flag, bb.data.getVarFlag(calledvar, flag, data), data)
|
||||
for flag in [ "dirs" ]:
|
||||
if bb.data.getVarFlag(var, flag, data):
|
||||
bb.data.setVarFlag(calledvar, flag, bb.data.getVarFlag(var, flag, data), data)
|
||||
|
||||
if bb.data.getVarFlag(calledvar, "python", data):
|
||||
bb.data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", data)
|
||||
else:
|
||||
bb.data.setVar(var, "\t" + calledvar + "\n", data)
|
||||
bb.data.setVarFlag(var, 'export_func', '1', data)
|
||||
|
||||
class AddTaskNode(AstNode):
|
||||
def __init__(self, func, before, after):
|
||||
self.func = func
|
||||
self.before = before
|
||||
self.after = after
|
||||
|
||||
def eval(self, data):
|
||||
var = self.func
|
||||
if self.func[:3] != "do_":
|
||||
var = "do_" + self.func
|
||||
|
||||
bb.data.setVarFlag(var, "task", 1, data)
|
||||
bbtasks = bb.data.getVar('__BBTASKS', data) or []
|
||||
if not var in bbtasks:
|
||||
bbtasks.append(var)
|
||||
bb.data.setVar('__BBTASKS', bbtasks, data)
|
||||
|
||||
existing = bb.data.getVarFlag(var, "deps", data) or []
|
||||
if self.after is not None:
|
||||
# set up deps for function
|
||||
for entry in self.after.split():
|
||||
if entry not in existing:
|
||||
existing.append(entry)
|
||||
bb.data.setVarFlag(var, "deps", existing, data)
|
||||
if self.before is not None:
|
||||
# set up things that depend on this func
|
||||
for entry in self.before.split():
|
||||
existing = bb.data.getVarFlag(entry, "deps", data) or []
|
||||
if var not in existing:
|
||||
bb.data.setVarFlag(entry, "deps", [var] + existing, data)
|
||||
|
||||
class BBHandlerNode(AstNode):
|
||||
def __init__(self, fns):
|
||||
self.hs = __word__.findall(fns)
|
||||
|
||||
def eval(self, data):
|
||||
bbhands = bb.data.getVar('__BBHANDLERS', data) or []
|
||||
for h in self.hs:
|
||||
bbhands.append(h)
|
||||
bb.data.setVarFlag(h, "handler", 1, data)
|
||||
bb.data.setVar('__BBHANDLERS', bbhands, data)
|
||||
|
||||
class InheritNode(AstNode):
|
||||
def __init__(self, files):
|
||||
self.n = __word__.findall(files)
|
||||
|
||||
def eval(self, data):
|
||||
bb.parse.BBHandler.inherit(self.n, data)
|
||||
|
||||
def handleInclude(statements, m, fn, lineno, force):
|
||||
statements.append(IncludeNode(m.group(1), fn, lineno, force))
|
||||
|
||||
def handleExport(statements, m):
|
||||
statements.append(ExportNode(m.group(1)))
|
||||
|
||||
def handleData(statements, groupd):
|
||||
statements.append(DataNode(groupd))
|
||||
|
||||
def handleMethod(statements, func_name, lineno, fn, body):
|
||||
statements.append(MethodNode(func_name, body, lineno, fn))
|
||||
|
||||
def handlePythonMethod(statements, funcname, root, body, fn):
|
||||
statements.append(PythonMethodNode(funcname, root, body, fn))
|
||||
|
||||
def handleMethodFlags(statements, key, m):
|
||||
statements.append(MethodFlagsNode(key, m))
|
||||
|
||||
def handleExportFuncs(statements, m, classes):
|
||||
statements.append(ExportFuncsNode(m.group(1), classes))
|
||||
|
||||
def handleAddTask(statements, m):
|
||||
func = m.group("func")
|
||||
before = m.group("before")
|
||||
after = m.group("after")
|
||||
if func is None:
|
||||
return
|
||||
|
||||
statements.append(AddTaskNode(func, before, after))
|
||||
|
||||
def handleBBHandlers(statements, m):
|
||||
statements.append(BBHandlerNode(m.group(1)))
|
||||
|
||||
def handleInherit(statements, m):
|
||||
files = m.group(1)
|
||||
n = __word__.findall(files)
|
||||
statements.append(InheritNode(m.group(1)))
|
||||
|
||||
def finalize(fn, d, variant = None):
|
||||
for lazykey in bb.data.getVar("__lazy_assigned", d) or ():
|
||||
if bb.data.getVar(lazykey, d) is None:
|
||||
val = bb.data.getVarFlag(lazykey, "defaultval", d)
|
||||
bb.data.setVar(lazykey, val, d)
|
||||
|
||||
bb.data.expandKeys(d)
|
||||
bb.data.update_data(d)
|
||||
code = []
|
||||
for funcname in bb.data.getVar("__BBANONFUNCS", d) or []:
|
||||
code.append("%s(d)" % funcname)
|
||||
bb.utils.simple_exec("\n".join(code), {"d": d})
|
||||
bb.data.update_data(d)
|
||||
|
||||
all_handlers = {}
|
||||
for var in bb.data.getVar('__BBHANDLERS', d) or []:
|
||||
# try to add the handler
|
||||
handler = bb.data.getVar(var, d)
|
||||
bb.event.register(var, handler)
|
||||
|
||||
tasklist = bb.data.getVar('__BBTASKS', d) or []
|
||||
bb.build.add_tasks(tasklist, d)
|
||||
|
||||
bb.parse.siggen.finalise(fn, d, variant)
|
||||
|
||||
bb.event.fire(bb.event.RecipeParsed(fn), d)
|
||||
|
||||
def _create_variants(datastores, names, function):
|
||||
def create_variant(name, orig_d, arg = None):
|
||||
new_d = bb.data.createCopy(orig_d)
|
||||
function(arg or name, new_d)
|
||||
datastores[name] = new_d
|
||||
|
||||
for variant, variant_d in datastores.items():
|
||||
for name in names:
|
||||
if not variant:
|
||||
# Based on main recipe
|
||||
create_variant(name, variant_d)
|
||||
else:
|
||||
create_variant("%s-%s" % (variant, name), variant_d, name)
|
||||
|
||||
def _expand_versions(versions):
|
||||
def expand_one(version, start, end):
|
||||
for i in xrange(start, end + 1):
|
||||
ver = _bbversions_re.sub(str(i), version, 1)
|
||||
yield ver
|
||||
|
||||
versions = iter(versions)
|
||||
while True:
|
||||
try:
|
||||
version = next(versions)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
range_ver = _bbversions_re.search(version)
|
||||
if not range_ver:
|
||||
yield version
|
||||
else:
|
||||
newversions = expand_one(version, int(range_ver.group("from")),
|
||||
int(range_ver.group("to")))
|
||||
versions = itertools.chain(newversions, versions)
|
||||
|
||||
def multi_finalize(fn, d):
|
||||
appends = (d.getVar("__BBAPPEND", True) or "").split()
|
||||
for append in appends:
|
||||
bb.msg.debug(2, bb.msg.domain.Parsing, "Appending .bbappend file " + append + " to " + fn)
|
||||
bb.parse.BBHandler.handle(append, d, True)
|
||||
|
||||
safe_d = d
|
||||
d = bb.data.createCopy(safe_d)
|
||||
try:
|
||||
finalize(fn, d)
|
||||
except bb.parse.SkipPackage:
|
||||
bb.data.setVar("__SKIPPED", True, d)
|
||||
datastores = {"": safe_d}
|
||||
|
||||
versions = (d.getVar("BBVERSIONS", True) or "").split()
|
||||
if versions:
|
||||
pv = orig_pv = d.getVar("PV", True)
|
||||
baseversions = {}
|
||||
|
||||
def verfunc(ver, d, pv_d = None):
|
||||
if pv_d is None:
|
||||
pv_d = d
|
||||
|
||||
overrides = d.getVar("OVERRIDES", True).split(":")
|
||||
pv_d.setVar("PV", ver)
|
||||
overrides.append(ver)
|
||||
bpv = baseversions.get(ver) or orig_pv
|
||||
pv_d.setVar("BPV", bpv)
|
||||
overrides.append(bpv)
|
||||
d.setVar("OVERRIDES", ":".join(overrides))
|
||||
|
||||
versions = list(_expand_versions(versions))
|
||||
for pos, version in enumerate(list(versions)):
|
||||
try:
|
||||
pv, bpv = version.split(":", 2)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
versions[pos] = pv
|
||||
baseversions[pv] = bpv
|
||||
|
||||
if pv in versions and not baseversions.get(pv):
|
||||
versions.remove(pv)
|
||||
else:
|
||||
pv = versions.pop()
|
||||
|
||||
# This is necessary because our existing main datastore
|
||||
# has already been finalized with the old PV, we need one
|
||||
# that's been finalized with the new PV.
|
||||
d = bb.data.createCopy(safe_d)
|
||||
verfunc(pv, d, safe_d)
|
||||
try:
|
||||
finalize(fn, d)
|
||||
except bb.parse.SkipPackage:
|
||||
bb.data.setVar("__SKIPPED", True, d)
|
||||
|
||||
_create_variants(datastores, versions, verfunc)
|
||||
|
||||
extended = d.getVar("BBCLASSEXTEND", True) or ""
|
||||
if extended:
|
||||
pn = d.getVar("PN", True)
|
||||
def extendfunc(name, d):
|
||||
d.setVar("PN", "%s-%s" % (pn, name))
|
||||
bb.parse.BBHandler.inherit([name], d)
|
||||
|
||||
safe_d.setVar("BBCLASSEXTEND", extended)
|
||||
_create_variants(datastores, extended.split(), extendfunc)
|
||||
|
||||
for variant, variant_d in datastores.iteritems():
|
||||
if variant:
|
||||
try:
|
||||
finalize(fn, variant_d, variant)
|
||||
except bb.parse.SkipPackage:
|
||||
bb.data.setVar("__SKIPPED", True, variant_d)
|
||||
|
||||
if len(datastores) > 1:
|
||||
variants = filter(None, datastores.iterkeys())
|
||||
safe_d.setVar("__VARIANTS", " ".join(variants))
|
||||
|
||||
datastores[""] = d
|
||||
return datastores
|
||||
242
bitbake/lib/bb/parse/parse_py/BBHandler.py
Normal file
242
bitbake/lib/bb/parse/parse_py/BBHandler.py
Normal file
@@ -0,0 +1,242 @@
|
||||
#!/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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
import re, bb, os
|
||||
import bb.fetch, bb.build, bb.utils
|
||||
from bb import data
|
||||
|
||||
from . import ConfHandler
|
||||
from .. import resolve_file, ast
|
||||
from .ConfHandler import include, init
|
||||
|
||||
# For compatibility
|
||||
bb.deprecate_import(__name__, "bb.parse", ["vars_from_file"])
|
||||
|
||||
__func_start_regexp__ = re.compile( r"(((?P<py>python)|(?P<fr>fakeroot))\s*)*(?P<func>[\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<func>\w+)\s*((before\s*(?P<before>((.*(?=after))|(.*))))|(after\s*(?P<after>((.*(?=before))|(.*)))))*")
|
||||
__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
|
||||
__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
|
||||
__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
|
||||
|
||||
|
||||
__infunc__ = ""
|
||||
__inpython__ = False
|
||||
__body__ = []
|
||||
__classname__ = ""
|
||||
classes = [ None, ]
|
||||
|
||||
cached_statements = {}
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
def supports(fn, d):
|
||||
return fn[-3:] == ".bb" or fn[-8:] == ".bbclass" or fn[-4:] == ".inc"
|
||||
|
||||
def inherit(files, d):
|
||||
__inherit_cache = data.getVar('__inherit_cache', d) or []
|
||||
fn = ""
|
||||
lineno = 0
|
||||
for file in files:
|
||||
file = data.expand(file, d)
|
||||
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 get_statements(filename, absolute_filename, base_name):
|
||||
global cached_statements
|
||||
|
||||
try:
|
||||
return cached_statements[absolute_filename]
|
||||
except KeyError:
|
||||
file = open(absolute_filename, 'r')
|
||||
statements = ast.StatementGroup()
|
||||
|
||||
lineno = 0
|
||||
while True:
|
||||
lineno = lineno + 1
|
||||
s = file.readline()
|
||||
if not s: break
|
||||
s = s.rstrip()
|
||||
feeder(lineno, s, filename, base_name, statements)
|
||||
if __inpython__:
|
||||
# add a blank line to close out any python definition
|
||||
feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
|
||||
|
||||
if filename.endswith(".bbclass") or filename.endswith(".inc"):
|
||||
cached_statements[absolute_filename] = statements
|
||||
return statements
|
||||
|
||||
def handle(fn, d, include):
|
||||
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
|
||||
|
||||
abs_fn = resolve_file(fn, d)
|
||||
|
||||
if include:
|
||||
bb.parse.mark_dependency(d, abs_fn)
|
||||
|
||||
# actual loading
|
||||
statements = get_statements(fn, abs_fn, base_name)
|
||||
|
||||
# DONE WITH PARSING... time to evaluate
|
||||
if ext != ".bbclass":
|
||||
data.setVar('FILE', fn, d)
|
||||
|
||||
statements.eval(d)
|
||||
|
||||
if ext == ".bbclass":
|
||||
classes.remove(__classname__)
|
||||
else:
|
||||
if include == 0:
|
||||
return ast.multi_finalize(fn, d)
|
||||
|
||||
if oldfile:
|
||||
bb.data.setVar("FILE", oldfile, d)
|
||||
|
||||
# we have parsed the bb class now
|
||||
if ext == ".bbclass" or ext == ".inc":
|
||||
bb.methodpool.get_parsed_dict()[base_name] = 1
|
||||
|
||||
return d
|
||||
|
||||
def feeder(lineno, s, fn, root, statements):
|
||||
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('')
|
||||
ast.handleMethod(statements, __infunc__, lineno, fn, __body__)
|
||||
__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:
|
||||
ast.handlePythonMethod(statements, __inpython__, root, __body__, 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"
|
||||
ast.handleMethodFlags(statements, __infunc__, m)
|
||||
return
|
||||
|
||||
m = __def_regexp__.match(s)
|
||||
if m:
|
||||
__body__.append(s)
|
||||
__inpython__ = m.group(1)
|
||||
|
||||
return
|
||||
|
||||
m = __export_func_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleExportFuncs(statements, m, classes)
|
||||
return
|
||||
|
||||
m = __addtask_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleAddTask(statements, m)
|
||||
return
|
||||
|
||||
m = __addhandler_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleBBHandlers(statements, m)
|
||||
return
|
||||
|
||||
m = __inherit_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleInherit(statements, m)
|
||||
return
|
||||
|
||||
return ConfHandler.feeder(lineno, s, fn, statements)
|
||||
|
||||
# Add us to the handlers list
|
||||
from .. import handlers
|
||||
handlers.append({'supports': supports, 'handle': handle, 'init': init})
|
||||
del handlers
|
||||
138
bitbake/lib/bb/parse/parse_py/ConfHandler.py
Normal file
138
bitbake/lib/bb/parse/parse_py/ConfHandler.py
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/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
|
||||
import bb.utils
|
||||
from bb.parse import ParseError, resolve_file, ast
|
||||
|
||||
#__config_regexp__ = re.compile( r"(?P<exp>export\s*)?(?P<var>[a-zA-Z0-9\-_+.${}]+)\s*(?P<colon>:)?(?P<ques>\?)?=\s*(?P<apo>['\"]?)(?P<value>.*)(?P=apo)$")
|
||||
__config_regexp__ = re.compile( r"(?P<exp>export\s*)?(?P<var>[a-zA-Z0-9\-_+.${}/]+)(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?\s*((?P<colon>:=)|(?P<lazyques>\?\?=)|(?P<ques>\?=)|(?P<append>\+=)|(?P<prepend>=\+)|(?P<predot>=\.)|(?P<postdot>\.=)|=)\s*(?P<apo>['\"]?)(?P<value>.*)(?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:
|
||||
bb.data.setVar('TOPDIR', os.getcwd(), data)
|
||||
|
||||
|
||||
def supports(fn, d):
|
||||
return fn[-5:] == ".conf"
|
||||
|
||||
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.utils.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):
|
||||
init(data)
|
||||
|
||||
if include == 0:
|
||||
oldfile = None
|
||||
else:
|
||||
oldfile = bb.data.getVar('FILE', data)
|
||||
|
||||
abs_fn = resolve_file(fn, data)
|
||||
f = open(abs_fn, 'r')
|
||||
|
||||
if include:
|
||||
bb.parse.mark_dependency(data, abs_fn)
|
||||
|
||||
statements = ast.StatementGroup()
|
||||
lineno = 0
|
||||
while True:
|
||||
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, statements)
|
||||
|
||||
# DONE WITH PARSING... time to evaluate
|
||||
bb.data.setVar('FILE', fn, data)
|
||||
statements.eval(data)
|
||||
if oldfile:
|
||||
bb.data.setVar('FILE', oldfile, data)
|
||||
|
||||
return data
|
||||
|
||||
def feeder(lineno, s, fn, statements):
|
||||
m = __config_regexp__.match(s)
|
||||
if m:
|
||||
groupd = m.groupdict()
|
||||
ast.handleData(statements, groupd)
|
||||
return
|
||||
|
||||
m = __include_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleInclude(statements, m, fn, lineno, False)
|
||||
return
|
||||
|
||||
m = __require_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleInclude(statements, m, fn, lineno, True)
|
||||
return
|
||||
|
||||
m = __export_regexp__.match(s)
|
||||
if m:
|
||||
ast.handleExport(statements, m)
|
||||
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
|
||||
33
bitbake/lib/bb/parse/parse_py/__init__.py
Normal file
33
bitbake/lib/bb/parse/parse_py/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/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
|
||||
|
||||
from __future__ import absolute_import
|
||||
from . import ConfHandler
|
||||
from . import BBHandler
|
||||
|
||||
__version__ = '1.0'
|
||||
133
bitbake/lib/bb/persist_data.py
Normal file
133
bitbake/lib/bb/persist_data.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# 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
|
||||
import bb.utils
|
||||
|
||||
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, persistent_database_connection):
|
||||
if "connection" in persistent_database_connection:
|
||||
self.cursor = persistent_database_connection["connection"].cursor()
|
||||
return
|
||||
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.utils.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)
|
||||
|
||||
connection = sqlite3.connect(self.cachefile, timeout=5, isolation_level=None)
|
||||
persistent_database_connection["connection"] = connection
|
||||
self.cursor = persistent_database_connection["connection"].cursor()
|
||||
|
||||
def addDomain(self, domain):
|
||||
"""
|
||||
Should be called before any domain is used
|
||||
Creates it if it doesn't exist.
|
||||
"""
|
||||
self._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._execute("DROP TABLE IF EXISTS %s;" % domain)
|
||||
|
||||
def getKeyValues(self, domain):
|
||||
"""
|
||||
Return a list of key + value pairs for a domain
|
||||
"""
|
||||
ret = {}
|
||||
data = self._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._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._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])
|
||||
|
||||
#
|
||||
# We wrap the sqlite execute calls as on contended machines or single threaded
|
||||
# systems we can have multiple processes trying to access the DB at once and it seems
|
||||
# sqlite sometimes doesn't wait for the timeout. We therefore loop but put in an
|
||||
# emergency brake too
|
||||
#
|
||||
def _execute(self, *query):
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
ret = self.cursor.execute(*query)
|
||||
#print "Had to retry %s times" % count
|
||||
return ret
|
||||
except sqlite3.OperationalError as e:
|
||||
if 'database is locked' in str(e) and count < 500:
|
||||
count = count + 1
|
||||
continue
|
||||
raise
|
||||
323
bitbake/lib/bb/providers.py
Normal file
323
bitbake/lib/bb/providers.py
Normal file
@@ -0,0 +1,323 @@
|
||||
# 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)
|
||||
tmp_pn = []
|
||||
for pri in sorted(priorities, lambda a, b: a - b):
|
||||
tmp_pref = []
|
||||
for pref in sorted(priorities[pri], lambda a, b: b - a):
|
||||
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:
|
||||
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 versions
|
||||
for pn in sortpkg_pn:
|
||||
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
|
||||
1574
bitbake/lib/bb/runqueue.py
Normal file
1574
bitbake/lib/bb/runqueue.py
Normal file
File diff suppressed because it is too large
Load Diff
0
bitbake/lib/bb/server/__init__.py
Normal file
0
bitbake/lib/bb/server/__init__.py
Normal file
181
bitbake/lib/bb/server/none.py
Normal file
181
bitbake/lib/bb/server/none.py
Normal file
@@ -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 hasattr(function, '__call__')
|
||||
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()
|
||||
self.commands.runCommand(["stateShutdown"])
|
||||
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
|
||||
186
bitbake/lib/bb/server/xmlrpc.py
Normal file
186
bitbake/lib/bb/server/xmlrpc.py
Normal file
@@ -0,0 +1,186 @@
|
||||
#
|
||||
# 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 hasattr(function, '__call__')
|
||||
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
|
||||
824
bitbake/lib/bb/shell.py
Normal file
824
bitbake/lib/bb/shell.py
Normal file
@@ -0,0 +1,824 @@
|
||||
# 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 <mickey@Vanille.de>
|
||||
# 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 <zecke@handhelds.org>
|
||||
# * Justin Patrin <papercrane@reversefold.com>
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""
|
||||
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
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
from functools import reduce
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set
|
||||
import sys, os, readline, socket, httplib, urllib, commands, popen2, shlex, Queue, fnmatch
|
||||
from bb import data, parse, build, cache, taskdata, runqueue, providers as Providers
|
||||
|
||||
__version__ = "0.5.3.1"
|
||||
__credits__ = """BitBake Shell Version %s (C) 2005 Michael 'Mickey' Lauer <mickey@Vanille.de>
|
||||
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 = "<alias> <command>"
|
||||
|
||||
def buffer( self, params ):
|
||||
"""Dump specified output buffer"""
|
||||
index = params[0]
|
||||
print(self._shell.myout.buffer( int( index ) ))
|
||||
buffer.usage = "<index>"
|
||||
|
||||
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, 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 as fnids:
|
||||
for fnid in fnids:
|
||||
print("ERROR: '%s' failed" % td.fn_index[fnid])
|
||||
last_exception = runqueue.TaskFailure
|
||||
|
||||
except build.EventException as e:
|
||||
print("ERROR: Couldn't build '%s'" % names)
|
||||
last_exception = e
|
||||
|
||||
|
||||
build.usage = "<providee>"
|
||||
|
||||
def clean( self, params ):
|
||||
"""Clean a providee"""
|
||||
self.build( params, "clean" )
|
||||
clean.usage = "<providee>"
|
||||
|
||||
def compile( self, params ):
|
||||
"""Execute 'compile' on a providee"""
|
||||
self.build( params, "compile" )
|
||||
compile.usage = "<providee>"
|
||||
|
||||
def configure( self, params ):
|
||||
"""Execute 'configure' on a providee"""
|
||||
self.build( params, "configure" )
|
||||
configure.usage = "<providee>"
|
||||
|
||||
def install( self, params ):
|
||||
"""Execute 'install' on a providee"""
|
||||
self.build( params, "install" )
|
||||
install.usage = "<providee>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
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 as e:
|
||||
print("ERROR: Couldn't build '%s'" % name)
|
||||
last_exception = e
|
||||
|
||||
fileBuild.usage = "<bbfile>"
|
||||
|
||||
def fileClean( self, params ):
|
||||
"""Clean a .bb file"""
|
||||
self.fileBuild( params, "clean" )
|
||||
fileClean.usage = "<bbfile>"
|
||||
|
||||
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 = "<bbfile>"
|
||||
|
||||
def fileRebuild( self, params ):
|
||||
"""Rebuild (clean & build) a .bb file"""
|
||||
self.fileBuild( params, "rebuild" )
|
||||
fileRebuild.usage = "<bbfile>"
|
||||
|
||||
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 = "<bbfile>"
|
||||
|
||||
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)
|
||||
for cmd in sorted(cmds):
|
||||
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, globexpr ): print(key)
|
||||
elif what == "providers":
|
||||
self._checkParsed()
|
||||
for key in globfilter( cooker.status.pkg_pn, globexpr ): print(key)
|
||||
else:
|
||||
print("Usage: match %s" % self.print_.usage)
|
||||
match.usage = "<files|providers> <glob>"
|
||||
|
||||
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("""DESCRIPTION = ""
|
||||
SECTION = ""
|
||||
AUTHOR = ""
|
||||
HOMEPAGE = ""
|
||||
MAINTAINER = ""
|
||||
LICENSE = "GPL"
|
||||
PR = "r0"
|
||||
|
||||
SRC_URI = ""
|
||||
|
||||
#inherit base
|
||||
|
||||
#do_configure() {
|
||||
#
|
||||
#}
|
||||
|
||||
#do_compile() {
|
||||
#
|
||||
#}
|
||||
|
||||
#do_stage() {
|
||||
#
|
||||
#}
|
||||
|
||||
#do_install() {
|
||||
#
|
||||
#}
|
||||
""", file=newpackage)
|
||||
newpackage.close()
|
||||
os.system( "%s %s/%s" % ( os.environ.get( "EDITOR" ), fulldirname, filename ) )
|
||||
new.usage = "<directory> <filename>"
|
||||
|
||||
def package( self, params ):
|
||||
"""Execute 'package' on a providee"""
|
||||
self.build( params, "package" )
|
||||
package.usage = "<providee>"
|
||||
|
||||
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 = "<index>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
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 = "<variable>"
|
||||
|
||||
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 = "<providee> <variable>"
|
||||
|
||||
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 = "<providee> <variable> <value>"
|
||||
|
||||
def print_( self, params ):
|
||||
"""Dump all files or providers"""
|
||||
what = params[0]
|
||||
if what == "files":
|
||||
self._checkParsed()
|
||||
for key in cooker.status.pkg_fn: print(key)
|
||||
elif what == "providers":
|
||||
self._checkParsed()
|
||||
for key in cooker.status.providers: print(key)
|
||||
else:
|
||||
print("Usage: print %s" % self.print_.usage)
|
||||
print_.usage = "<files|providers>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
def setVar( self, params ):
|
||||
"""Set an outer BitBake environment variable"""
|
||||
var, value = params
|
||||
data.setVar( var, value, cooker.configuration.data )
|
||||
print("OK")
|
||||
setVar.usage = "<variable> <value>"
|
||||
|
||||
def rebuild( self, params ):
|
||||
"""Clean and rebuild a .bb file or a providee"""
|
||||
self.build( params, "clean" )
|
||||
self.build( params, "build" )
|
||||
rebuild.usage = "<providee>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
def status( self, params ):
|
||||
"""<just for testing>"""
|
||||
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 ):
|
||||
"""<just for testing>"""
|
||||
print("testCommand called with '%s'" % params)
|
||||
|
||||
def unpack( self, params ):
|
||||
"""Execute 'unpack' on a providee"""
|
||||
self.build( params, "unpack" )
|
||||
unpack.usage = "<providee>"
|
||||
|
||||
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 = "<providee>"
|
||||
|
||||
##########################################################################
|
||||
# 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:
|
||||
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 == "<variable>":
|
||||
allmatches = cooker.configuration.data.keys()
|
||||
elif u == "<bbfile>":
|
||||
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 ]
|
||||
elif u == "<providee>":
|
||||
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 "<not yet documented>"
|
||||
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.")
|
||||
260
bitbake/lib/bb/siggen.py
Normal file
260
bitbake/lib/bb/siggen.py
Normal file
@@ -0,0 +1,260 @@
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
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.")
|
||||
|
||||
def init(d, dumpsigs):
|
||||
siggens = [obj for obj in globals().itervalues()
|
||||
if type(obj) is type and issubclass(obj, SignatureGenerator)]
|
||||
|
||||
desired = bb.data.getVar("BB_SIGNATURE_HANDLER", d, True) or "noop"
|
||||
for sg in siggens:
|
||||
if desired == sg.name:
|
||||
return sg(d, dumpsigs)
|
||||
break
|
||||
else:
|
||||
bb.error("Invalid signature generator '%s', using default 'noop' generator" % desired)
|
||||
bb.error("Available generators: %s" % ", ".join(obj.name for obj in siggens))
|
||||
return SignatureGenerator(d, dumpsigs)
|
||||
|
||||
class SignatureGenerator(object):
|
||||
"""
|
||||
"""
|
||||
name = "noop"
|
||||
|
||||
def __init__(self, data, dumpsigs):
|
||||
return
|
||||
|
||||
def finalise(self, fn, d):
|
||||
return
|
||||
|
||||
class SignatureGeneratorBasic(SignatureGenerator):
|
||||
"""
|
||||
"""
|
||||
name = "basic"
|
||||
|
||||
def __init__(self, data, dumpsigs):
|
||||
self.basehash = {}
|
||||
self.taskhash = {}
|
||||
self.taskdeps = {}
|
||||
self.runtaskdeps = {}
|
||||
self.gendeps = {}
|
||||
self.lookupcache = {}
|
||||
self.basewhitelist = (data.getVar("BB_HASHBASE_WHITELIST", True) or "").split()
|
||||
self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST", True) or None
|
||||
|
||||
if self.taskwhitelist:
|
||||
self.twl = re.compile(self.taskwhitelist)
|
||||
else:
|
||||
self.twl = None
|
||||
|
||||
self.dumpsigs = dumpsigs
|
||||
|
||||
def _build_data(self, fn, d):
|
||||
|
||||
taskdeps, gendeps = bb.data.generate_dependencies(d)
|
||||
|
||||
basehash = {}
|
||||
lookupcache = {}
|
||||
|
||||
for task in taskdeps:
|
||||
data = d.getVar(task, False)
|
||||
lookupcache[task] = data
|
||||
for dep in sorted(taskdeps[task]):
|
||||
if dep in self.basewhitelist:
|
||||
continue
|
||||
if dep in lookupcache:
|
||||
var = lookupcache[dep]
|
||||
else:
|
||||
var = d.getVar(dep, False)
|
||||
lookupcache[dep] = var
|
||||
if var:
|
||||
data = data + var
|
||||
self.basehash[fn + "." + task] = hashlib.md5(data).hexdigest()
|
||||
#bb.note("Hash for %s is %s" % (task, tashhash[task]))
|
||||
|
||||
if self.dumpsigs:
|
||||
self.taskdeps[fn] = taskdeps
|
||||
self.gendeps[fn] = gendeps
|
||||
self.lookupcache[fn] = lookupcache
|
||||
|
||||
return taskdeps
|
||||
|
||||
def finalise(self, fn, d, variant):
|
||||
|
||||
if variant:
|
||||
fn = "virtual:" + variant + ":" + fn
|
||||
|
||||
taskdeps = self._build_data(fn, d)
|
||||
|
||||
#Slow but can be useful for debugging mismatched basehashes
|
||||
#for task in self.taskdeps[fn]:
|
||||
# self.dump_sigtask(fn, task, d.getVar("STAMP", True), False)
|
||||
|
||||
for task in taskdeps:
|
||||
d.setVar("BB_BASEHASH_task-%s" % task, self.basehash[fn + "." + task])
|
||||
|
||||
def get_taskhash(self, fn, task, deps, dataCache):
|
||||
k = fn + "." + task
|
||||
data = dataCache.basetaskhash[k]
|
||||
self.runtaskdeps[k] = deps
|
||||
for dep in sorted(deps):
|
||||
if self.twl and self.twl.search(dataCache.pkg_fn[fn]):
|
||||
#bb.note("Skipping %s" % dep)
|
||||
continue
|
||||
if dep not in self.taskhash:
|
||||
bb.fatal("%s is not in taskhash, caller isn't calling in dependency order?", dep)
|
||||
data = data + self.taskhash[dep]
|
||||
h = hashlib.md5(data).hexdigest()
|
||||
self.taskhash[k] = h
|
||||
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
||||
return h
|
||||
|
||||
def set_taskdata(self, hashes, deps):
|
||||
self.runtaskdeps = deps
|
||||
self.taskhash = hashes
|
||||
|
||||
def dump_sigtask(self, fn, task, stampbase, runtime):
|
||||
k = fn + "." + task
|
||||
if runtime == "customfile":
|
||||
sigfile = stampbase
|
||||
elif runtime:
|
||||
sigfile = stampbase + "." + task + ".sigdata" + "." + self.taskhash[k]
|
||||
else:
|
||||
sigfile = stampbase + "." + task + ".sigbasedata" + "." + self.basehash[k]
|
||||
|
||||
data = {}
|
||||
data['basewhitelist'] = self.basewhitelist
|
||||
data['taskwhitelist'] = self.taskwhitelist
|
||||
data['taskdeps'] = self.taskdeps[fn][task]
|
||||
data['basehash'] = self.basehash[k]
|
||||
data['gendeps'] = {}
|
||||
data['varvals'] = {}
|
||||
data['varvals'][task] = self.lookupcache[fn][task]
|
||||
for dep in self.taskdeps[fn][task]:
|
||||
if dep in self.basewhitelist:
|
||||
continue
|
||||
data['gendeps'][dep] = self.gendeps[fn][dep]
|
||||
data['varvals'][dep] = self.lookupcache[fn][dep]
|
||||
|
||||
if runtime:
|
||||
data['runtaskdeps'] = self.runtaskdeps[k]
|
||||
data['runtaskhashes'] = {}
|
||||
for dep in data['runtaskdeps']:
|
||||
data['runtaskhashes'][dep] = self.taskhash[dep]
|
||||
|
||||
p = pickle.Pickler(file(sigfile, "wb"), -1)
|
||||
p.dump(data)
|
||||
|
||||
def dump_sigs(self, dataCache):
|
||||
for fn in self.taskdeps:
|
||||
for task in self.taskdeps[fn]:
|
||||
k = fn + "." + task
|
||||
if k not in self.taskhash:
|
||||
continue
|
||||
if dataCache.basetaskhash[k] != self.basehash[k]:
|
||||
bb.error("Bitbake's cached basehash does not match the one we just generated!")
|
||||
bb.error("The mismatched hashes were %s and %s" % (dataCache.basetaskhash[k], self.basehash[k]))
|
||||
self.dump_sigtask(fn, task, dataCache.stamp[fn], True)
|
||||
|
||||
def dump_this_task(outfile, d):
|
||||
fn = d.getVar("BB_FILENAME", True)
|
||||
task = "do_" + d.getVar("BB_CURRENTTASK", True)
|
||||
bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile")
|
||||
|
||||
def compare_sigfiles(a, b):
|
||||
p1 = pickle.Unpickler(file(a, "rb"))
|
||||
a_data = p1.load()
|
||||
p2 = pickle.Unpickler(file(b, "rb"))
|
||||
b_data = p2.load()
|
||||
|
||||
#print "Checking"
|
||||
#print str(a_data)
|
||||
#print str(b_data)
|
||||
|
||||
def dict_diff(a, b):
|
||||
sa = set(a.keys())
|
||||
sb = set(b.keys())
|
||||
common = sa & sb
|
||||
changed = set()
|
||||
for i in common:
|
||||
if a[i] != b[i]:
|
||||
changed.add(i)
|
||||
added = sa - sb
|
||||
removed = sb - sa
|
||||
return changed, added, removed
|
||||
|
||||
if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
|
||||
print "basewhitelist changed from %s to %s" % (a_data['basewhitelist'], b_data['basewhitelist'])
|
||||
|
||||
if 'taskwhitelist' in a_data and a_data['taskwhitelist'] != b_data['taskwhitelist']:
|
||||
print "taskwhitelist changed from %s to %s" % (a_data['taskwhitelist'], b_data['taskwhitelist'])
|
||||
|
||||
if a_data['taskdeps'] != b_data['taskdeps']:
|
||||
print "Task dependencies changed from %s to %s" % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps']))
|
||||
|
||||
if a_data['basehash'] != b_data['basehash']:
|
||||
print "basehash changed from %s to %s" % (a_data['basehash'], b_data['basehash'])
|
||||
|
||||
changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
print "List of dependencies for variable %s changed from %s to %s" % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep])
|
||||
if added:
|
||||
for dep in added:
|
||||
print "Dependency on variable %s was added" % (dep)
|
||||
if removed:
|
||||
for dep in removed:
|
||||
print "Dependency on Variable %s was removed" % (dep)
|
||||
|
||||
|
||||
changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
print "Variable %s value changed from %s to %s" % (dep, a_data['varvals'][dep], b_data['varvals'][dep])
|
||||
#if added:
|
||||
# print "Dependency on variable %s was added (value %s)" % (dep, b_data['gendeps'][dep])
|
||||
#if removed:
|
||||
# print "Dependency on Variable %s was removed (value %s)" % (dep, a_data['gendeps'][dep])
|
||||
|
||||
if 'runtaskdeps' in a_data and 'runtaskdeps' in b_data and a_data['runtaskdeps'] != b_data['runtaskdeps']:
|
||||
print "Tasks this task depends on changed from %s to %s" % (a_data['taskdeps'], b_data['taskdeps'])
|
||||
|
||||
if 'runtaskhashes' in a_data:
|
||||
for dep in a_data['runtaskhashes']:
|
||||
if a_data['runtaskhashes'][dep] != b_data['runtaskhashes'][dep]:
|
||||
print "Hash for dependent task %s changed from %s to %s" % (dep, a_data['runtaskhashes'][dep], b_data['runtaskhashes'][dep])
|
||||
|
||||
def dump_sigfile(a):
|
||||
p1 = pickle.Unpickler(file(a, "rb"))
|
||||
a_data = p1.load()
|
||||
|
||||
print "basewhitelist: %s" % (a_data['basewhitelist'])
|
||||
|
||||
print "taskwhitelist: %s" % (a_data['taskwhitelist'])
|
||||
|
||||
print "Task dependencies: %s" % (sorted(a_data['taskdeps']))
|
||||
|
||||
print "basehash: %s" % (a_data['basehash'])
|
||||
|
||||
for dep in a_data['gendeps']:
|
||||
print "List of dependencies for variable %s is %s" % (dep, a_data['gendeps'][dep])
|
||||
|
||||
for dep in a_data['varvals']:
|
||||
print "Variable %s value is %s" % (dep, a_data['varvals'][dep])
|
||||
|
||||
if 'runtaskdeps' in a_data:
|
||||
print "Tasks this task depends on: %s" % (a_data['runtaskdeps'])
|
||||
|
||||
if 'runtaskhashes' in a_data:
|
||||
for dep in a_data['runtaskhashes']:
|
||||
print "Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
587
bitbake/lib/bb/taskdata.py
Normal file
587
bitbake/lib/bb/taskdata.py
Normal file
@@ -0,0 +1,587 @@
|
||||
#!/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'" % (dep, 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:
|
||||
raise
|
||||
self.remove_buildtarget(self.getbuild_id(item))
|
||||
|
||||
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:
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_rdependees_str(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.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(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.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.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item)), 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.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item)), 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.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.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:
|
||||
target = self.build_names_index[targetid]
|
||||
bb.msg.error(bb.msg.domain.Provider, "Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s" % (target, missing_list))
|
||||
raise bb.providers.NoProvider(target)
|
||||
|
||||
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 True:
|
||||
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:
|
||||
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 dependencies")
|
||||
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]))
|
||||
17
bitbake/lib/bb/ui/__init__.py
Normal file
17
bitbake/lib/bb/ui/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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.
|
||||
17
bitbake/lib/bb/ui/crumbs/__init__.py
Normal file
17
bitbake/lib/bb/ui/crumbs/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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.
|
||||
455
bitbake/lib/bb/ui/crumbs/buildmanager.py
Normal file
455
bitbake/lib/bb/ui/crumbs/buildmanager.py
Normal file
@@ -0,0 +1,455 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2008 Intel Corporation
|
||||
#
|
||||
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||
#
|
||||
# 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):
|
||||
|
||||
conf = BuildConfiguration()
|
||||
with open(filename, "r") as f:
|
||||
for line in f:
|
||||
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
|
||||
|
||||
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-<year><month><day>-<ordinal> 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-<xyz>-<n> 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 as 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)
|
||||
606
bitbake/lib/bb/ui/crumbs/puccho.glade
Normal file
606
bitbake/lib/bb/ui/crumbs/puccho.glade
Normal file
@@ -0,0 +1,606 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
|
||||
<!--Generated with glade3 3.4.5 on Mon Nov 10 12:24:12 2008 -->
|
||||
<glade-interface>
|
||||
<widget class="GtkDialog" id="build_dialog">
|
||||
<property name="title" translatable="yes">Start a build</property>
|
||||
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="build_table">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">3</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="status_alignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="status_hbox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkImage" id="status_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="stock">gtk-dialog-error</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="status_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">If you see this text something is wrong...</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Build configuration</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="image_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="image_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Image:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="distribution_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="distribution_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Distribution:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="machine_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="machine_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Machine:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="refresh_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-refresh</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="location_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">32</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Location:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Repository</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment3">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<widget class="GtkHButtonBox" id="dialog-action_area1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<widget class="GtkDialog" id="dialog2">
|
||||
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">3</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Repositories</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Additional packages</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment6">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xscale">0</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Location: </property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment7">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xscale">0</property>
|
||||
<child>
|
||||
<widget class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-remove</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-edit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-add</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment5">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Search:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment8">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<widget class="GtkHButtonBox" id="dialog-action_area2">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-close</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<widget class="GtkWindow" id="main_window">
|
||||
<child>
|
||||
<widget class="GtkVBox" id="main_window_vbox">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkToolbar" id="main_toolbar">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkToolButton" id="main_toolbutton_build">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Build</property>
|
||||
<property name="stock_id">gtk-execute</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkVPaned" id="vpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="results_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="progress_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</glade-interface>
|
||||
178
bitbake/lib/bb/ui/crumbs/runningbuild.py
Normal file
178
bitbake/lib/bb/ui/crumbs/runningbuild.py
Normal file
@@ -0,0 +1,178 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2008 Intel Corporation
|
||||
#
|
||||
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||
#
|
||||
# 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 hasattr(event, 'pid'):
|
||||
pid = event.pid
|
||||
if pid in self.pids_to_task:
|
||||
(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 ((package, None) in self.tasks_to_iter):
|
||||
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)
|
||||
271
bitbake/lib/bb/ui/depexp.py
Normal file
271
bitbake/lib/bb/ui/depexp.py
Normal file
@@ -0,0 +1,271 @@
|
||||
#
|
||||
# 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 as 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
|
||||
76
bitbake/lib/bb/ui/goggle.py
Normal file
76
bitbake/lib/bb/ui/goggle.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2008 Intel Corporation
|
||||
#
|
||||
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||
#
|
||||
# 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 as 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()
|
||||
192
bitbake/lib/bb/ui/knotty.py
Normal file
192
bitbake/lib/bb/ui/knotty.py
Normal file
@@ -0,0 +1,192 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import os
|
||||
import sys
|
||||
import itertools
|
||||
import xmlrpclib
|
||||
from bb import ui
|
||||
from bb.ui import uihelper
|
||||
|
||||
|
||||
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"])
|
||||
|
||||
helper = uihelper.BBUIHelper()
|
||||
|
||||
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 as 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
|
||||
helper.eventHandler(event)
|
||||
if isinstance(event, bb.runqueue.runQueueExitWait):
|
||||
if not shutdown:
|
||||
shutdown = 1
|
||||
if shutdown and helper.needUpdate:
|
||||
activetasks, failedtasks = helper.getTasks()
|
||||
if activetasks:
|
||||
print("Waiting for %s active tasks to finish:" % len(activetasks))
|
||||
tasknum = 1
|
||||
for task in activetasks:
|
||||
print("%s: %s (pid %s)" % (tasknum, activetasks[task]["title"], task))
|
||||
tasknum = tasknum + 1
|
||||
|
||||
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)
|
||||
continue
|
||||
if isinstance(event, bb.build.TaskFailed):
|
||||
return_value = 1
|
||||
logfile = event.logfile
|
||||
if logfile and os.path.exists(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 %%]" % ( next(parsespin), 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
|
||||
if isinstance(event, bb.event.MultipleProviders):
|
||||
print("NOTE: multiple providers are available for %s%s (%s)" % (event._is_runtime and "runtime " or "",
|
||||
event._item,
|
||||
", ".join(event._candidates)))
|
||||
print("NOTE: consider defining a PREFERRED_PROVIDER entry to match %s" % event._item)
|
||||
continue
|
||||
if isinstance(event, bb.event.NoProvider):
|
||||
if event._runtime:
|
||||
r = "R"
|
||||
else:
|
||||
r = ""
|
||||
|
||||
if event._dependees:
|
||||
print("ERROR: Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % (r, event._item, ", ".join(event._dependees), r))
|
||||
else:
|
||||
print("ERROR: Nothing %sPROVIDES '%s'" % (r, event._item))
|
||||
continue
|
||||
|
||||
# ignore
|
||||
if isinstance(event, (bb.event.BuildBase,
|
||||
bb.event.StampUpdate,
|
||||
bb.event.ConfigParsed,
|
||||
bb.event.RecipeParsed,
|
||||
bb.runqueue.runQueueEvent,
|
||||
bb.runqueue.runQueueExitWait)):
|
||||
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
|
||||
336
bitbake/lib/bb/ui/ncurses.py
Normal file
336
bitbake/lib/bb/ui/ncurses.py
Normal file
@@ -0,0 +1,336 @@
|
||||
#
|
||||
# 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:
|
||||
|
||||
|---------------------------------------------------------|
|
||||
| <Main Window> | <Thread Activity Window> |
|
||||
| | 0: foo do_compile complete|
|
||||
| Building Gtk+-2.6.10 | 1: bar do_patch complete |
|
||||
| Status: 60% | ... |
|
||||
| | ... |
|
||||
| | ... |
|
||||
|---------------------------------------------------------|
|
||||
|<Command Line Window> |
|
||||
|>>> which virtual/kernel |
|
||||
|openzaurus-kernel |
|
||||
|>>> _ |
|
||||
|---------------------------------------------------------|
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
|
||||
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 as 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.TaskBase):
|
||||
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 %%]" % ( next(parsespin), 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.itervalues():
|
||||
taw.appendText(task["title"])
|
||||
if failedtasks:
|
||||
taw.appendText("Failed Tasks:\n")
|
||||
for task in failedtasks:
|
||||
taw.appendText(task["title"])
|
||||
|
||||
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()
|
||||
425
bitbake/lib/bb/ui/puccho.py
Normal file
425
bitbake/lib/bb/ui/puccho.py
Normal file
@@ -0,0 +1,425 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2008 Intel Corporation
|
||||
#
|
||||
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||
#
|
||||
# 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
|
||||
import contextlib
|
||||
|
||||
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:
|
||||
with contextlib.closing (urllib2.urlopen (self.url)) as f:
|
||||
# Parse the metadata format. The format is....
|
||||
# <machine>;<default distro>|<distro>...;<default image>|<image>...;<type##url>|...
|
||||
for line in f:
|
||||
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 as e:
|
||||
gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
|
||||
"Repository metadata corrupt")
|
||||
except Exception as 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 (
|
||||
"""<i>Enter the repository location and press _Refresh</i>""")
|
||||
|
||||
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()
|
||||
124
bitbake/lib/bb/ui/uievent.py
Normal file
124
bitbake/lib/bb/ui/uievent.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# 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)
|
||||
|
||||
50
bitbake/lib/bb/ui/uihelper.py
Normal file
50
bitbake/lib/bb/ui/uihelper.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# 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[event.pid] = { 'title' : "%s %s" % (event._package, event._task) }
|
||||
self.needUpdate = True
|
||||
if isinstance(event, bb.build.TaskSucceeded):
|
||||
del self.running_tasks[event.pid]
|
||||
self.needUpdate = True
|
||||
if isinstance(event, bb.build.TaskFailed):
|
||||
del self.running_tasks[event.pid]
|
||||
self.failed_tasks.append( { 'title' : "%s %s" % (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):
|
||||
self.needUpdate = False
|
||||
return (self.running_tasks, self.failed_tasks)
|
||||
762
bitbake/lib/bb/utils.py
Normal file
762
bitbake/lib/bb/utils.py
Normal file
@@ -0,0 +1,762 @@
|
||||
# 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.
|
||||
|
||||
import re, fcntl, os, string, stat, shutil, time
|
||||
import sys
|
||||
import bb
|
||||
import errno
|
||||
import bb.msg
|
||||
from commands import getstatusoutput
|
||||
|
||||
# Version comparison
|
||||
separators = ".-"
|
||||
|
||||
# Context used in better_exec, eval
|
||||
_context = {
|
||||
"os": os,
|
||||
"bb": bb,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
def explode_version(s):
|
||||
r = []
|
||||
alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
|
||||
numeric_regexp = re.compile('^(\d+)(.*)$')
|
||||
while (s != ''):
|
||||
if s[0] in string.digits:
|
||||
m = numeric_regexp.match(s)
|
||||
r.append(int(m.group(1)))
|
||||
s = m.group(2)
|
||||
continue
|
||||
if s[0] in string.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 isinstance(ca, basestring):
|
||||
sa = ca in separators
|
||||
if isinstance(cb, basestring):
|
||||
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
|
||||
|
||||
_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.
|
||||
"""
|
||||
|
||||
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]
|
||||
|
||||
__vercmp_cache__ = {}
|
||||
|
||||
def vercmp_string(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.
|
||||
"""
|
||||
|
||||
# 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 = val1.split("-")
|
||||
if len(val1) == 2:
|
||||
val1[0] = val1[0] + "." + val1[1]
|
||||
val2 = val2.split("-")
|
||||
if len(val2) == 2:
|
||||
val2[0] = val2[0] + "." + val2[1]
|
||||
|
||||
val1 = val1[0].split('.')
|
||||
val2 = val2[0].split('.')
|
||||
|
||||
# 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 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 dictionary of dependencies and versions.
|
||||
"""
|
||||
r = {}
|
||||
l = s.replace(",", "").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 join_deps(deps):
|
||||
"""
|
||||
Take the result from explode_dep_versions and generate a dependency string
|
||||
"""
|
||||
result = []
|
||||
for dep in deps:
|
||||
if deps[dep]:
|
||||
result.append(dep + " (" + deps[dep] + ")")
|
||||
else:
|
||||
result.append(dep)
|
||||
return ", ".join(result)
|
||||
|
||||
def extend_deps(dest, src):
|
||||
"""
|
||||
Extend the results from explode_dep_versions by appending all of the items
|
||||
in the second list, avoiding duplicates.
|
||||
"""
|
||||
for dep in src:
|
||||
if dep not in dest:
|
||||
dest[dep] = src[dep]
|
||||
elif dest[dep] != src[dep]:
|
||||
dest[dep] = src[dep]
|
||||
|
||||
def _print_trace(body, line):
|
||||
"""
|
||||
Print the Environment of a Text Body
|
||||
"""
|
||||
# print the environment of the method
|
||||
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, mode = "exec"):
|
||||
"""
|
||||
A better compile method. This method
|
||||
will print the offending lines.
|
||||
"""
|
||||
try:
|
||||
return compile(text, file, mode)
|
||||
except Exception as e:
|
||||
# split the text into lines again
|
||||
body = text.split('\n')
|
||||
bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: %s" % (realfile))
|
||||
bb.msg.error(bb.msg.domain.Util, str(e))
|
||||
if e.lineno:
|
||||
bb.msg.error(bb.msg.domain.Util, "The lines leading to 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)
|
||||
else:
|
||||
bb.msg.error(bb.msg.domain.Util, "The function causing this error was:")
|
||||
for line in body:
|
||||
bb.msg.error(bb.msg.domain.Util, line)
|
||||
raise
|
||||
|
||||
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.parse
|
||||
try:
|
||||
exec(code, _context, 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))
|
||||
|
||||
# Strip 'us' from the stack (better_exec call)
|
||||
tb = tb.tb_next
|
||||
|
||||
import traceback
|
||||
tbextract = traceback.extract_tb(tb)
|
||||
tbextract = "\n".join(traceback.format_list(tbextract))
|
||||
bb.msg.error(bb.msg.domain.Util, "Traceback:")
|
||||
for line in tbextract.split('\n'):
|
||||
bb.msg.error(bb.msg.domain.Util, line)
|
||||
|
||||
line = traceback.tb_lineno(tb)
|
||||
bb.msg.error(bb.msg.domain.Util, "The lines leading to this error were:")
|
||||
_print_trace( text.split('\n'), line )
|
||||
|
||||
raise
|
||||
|
||||
def simple_exec(code, context):
|
||||
exec(code, _context, context)
|
||||
|
||||
def better_eval(source, locals):
|
||||
return eval(source, _context, locals)
|
||||
|
||||
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):
|
||||
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 as 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',
|
||||
'BB_TASKHASH',
|
||||
'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.
|
||||
"""
|
||||
|
||||
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.msg.debug(1, bb.msg.domain.Util, "Removed the following variables from the environment: %s" % (", ".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.data
|
||||
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) or ""
|
||||
|
||||
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
|
||||
|
||||
def mkdirhier(dir):
|
||||
"""Create a directory like 'mkdir -p', but does not complain if
|
||||
directory already exists like os.makedirs
|
||||
"""
|
||||
|
||||
bb.msg.debug(3, bb.msg.domain.Util, "mkdirhier(%s)" % dir)
|
||||
try:
|
||||
os.makedirs(dir)
|
||||
bb.msg.debug(2, bb.msg.domain.Util, "created " + dir)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise e
|
||||
|
||||
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 as 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 as 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 as 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:
|
||||
os.rename(src, dest)
|
||||
renamefailed = 0
|
||||
except Exception as e:
|
||||
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 as 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:
|
||||
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
|
||||
os.unlink(src)
|
||||
except Exception as 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.
|
||||
"""
|
||||
#print "copyfile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")"
|
||||
try:
|
||||
if not sstat:
|
||||
sstat = os.lstat(src)
|
||||
except Exception as 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 as 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 as e:
|
||||
print("copyfile: failed to properly create symlink:", dest, "->", target, e)
|
||||
return False
|
||||
|
||||
if stat.S_ISREG(sstat[stat.ST_MODE]):
|
||||
os.chmod(src, stat.S_IRUSR) # Make sure we can read it
|
||||
try: # For safety copy then move it over.
|
||||
shutil.copyfile(src, dest + "#new")
|
||||
os.rename(dest + "#new", dest)
|
||||
except Exception as e:
|
||||
print('copyfile: copy', src, '->', dest, 'failed.', e)
|
||||
return False
|
||||
finally:
|
||||
os.chmod(src, sstat[stat.ST_MODE])
|
||||
os.utime(src, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
|
||||
|
||||
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 as 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
|
||||
|
||||
def which(path, item, direction = 0):
|
||||
"""
|
||||
Locate a file in a PATH
|
||||
"""
|
||||
|
||||
paths = (path or "").split(':')
|
||||
if direction != 0:
|
||||
paths.reverse()
|
||||
|
||||
for p in paths:
|
||||
next = os.path.join(p, item)
|
||||
if os.path.exists(next):
|
||||
return next
|
||||
|
||||
return ""
|
||||
|
||||
def init_logger(logger, verbose, debug, debug_domains):
|
||||
"""
|
||||
Set verbosity and debug levels in the logger
|
||||
"""
|
||||
|
||||
if verbose:
|
||||
logger.set_verbose(True)
|
||||
|
||||
if debug:
|
||||
logger.set_debug_level(debug)
|
||||
else:
|
||||
logger.set_debug_level(0)
|
||||
|
||||
if debug_domains:
|
||||
logger.set_debug_domains(debug_domains)
|
||||
570
bitbake/lib/codegen.py
Normal file
570
bitbake/lib/codegen.py
Normal file
@@ -0,0 +1,570 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
codegen
|
||||
~~~~~~~
|
||||
|
||||
Extension to ast that allow ast -> python code generation.
|
||||
|
||||
:copyright: Copyright 2008 by Armin Ronacher.
|
||||
:license: BSD.
|
||||
"""
|
||||
from ast import *
|
||||
|
||||
BOOLOP_SYMBOLS = {
|
||||
And: 'and',
|
||||
Or: 'or'
|
||||
}
|
||||
|
||||
BINOP_SYMBOLS = {
|
||||
Add: '+',
|
||||
Sub: '-',
|
||||
Mult: '*',
|
||||
Div: '/',
|
||||
FloorDiv: '//',
|
||||
Mod: '%',
|
||||
LShift: '<<',
|
||||
RShift: '>>',
|
||||
BitOr: '|',
|
||||
BitAnd: '&',
|
||||
BitXor: '^'
|
||||
}
|
||||
|
||||
CMPOP_SYMBOLS = {
|
||||
Eq: '==',
|
||||
Gt: '>',
|
||||
GtE: '>=',
|
||||
In: 'in',
|
||||
Is: 'is',
|
||||
IsNot: 'is not',
|
||||
Lt: '<',
|
||||
LtE: '<=',
|
||||
NotEq: '!=',
|
||||
NotIn: 'not in'
|
||||
}
|
||||
|
||||
UNARYOP_SYMBOLS = {
|
||||
Invert: '~',
|
||||
Not: 'not',
|
||||
UAdd: '+',
|
||||
USub: '-'
|
||||
}
|
||||
|
||||
ALL_SYMBOLS = {}
|
||||
ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
|
||||
ALL_SYMBOLS.update(BINOP_SYMBOLS)
|
||||
ALL_SYMBOLS.update(CMPOP_SYMBOLS)
|
||||
ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
|
||||
|
||||
def to_source(node, indent_with=' ' * 4, add_line_information=False):
|
||||
"""This function can convert a node tree back into python sourcecode.
|
||||
This is useful for debugging purposes, especially if you're dealing with
|
||||
custom asts not generated by python itself.
|
||||
|
||||
It could be that the sourcecode is evaluable when the AST itself is not
|
||||
compilable / evaluable. The reason for this is that the AST contains some
|
||||
more data than regular sourcecode does, which is dropped during
|
||||
conversion.
|
||||
|
||||
Each level of indentation is replaced with `indent_with`. Per default this
|
||||
parameter is equal to four spaces as suggested by PEP 8, but it might be
|
||||
adjusted to match the application's styleguide.
|
||||
|
||||
If `add_line_information` is set to `True` comments for the line numbers
|
||||
of the nodes are added to the output. This can be used to spot wrong line
|
||||
number information of statement nodes.
|
||||
"""
|
||||
generator = SourceGenerator(indent_with, add_line_information)
|
||||
generator.visit(node)
|
||||
return ''.join(generator.result)
|
||||
|
||||
|
||||
class SourceGenerator(NodeVisitor):
|
||||
"""This visitor is able to transform a well formed syntax tree into python
|
||||
sourcecode. For more details have a look at the docstring of the
|
||||
`node_to_source` function.
|
||||
"""
|
||||
|
||||
def __init__(self, indent_with, add_line_information=False):
|
||||
self.result = []
|
||||
self.indent_with = indent_with
|
||||
self.add_line_information = add_line_information
|
||||
self.indentation = 0
|
||||
self.new_lines = 0
|
||||
|
||||
def write(self, x):
|
||||
if self.new_lines:
|
||||
if self.result:
|
||||
self.result.append('\n' * self.new_lines)
|
||||
self.result.append(self.indent_with * self.indentation)
|
||||
self.new_lines = 0
|
||||
self.result.append(x)
|
||||
|
||||
def newline(self, node=None, extra=0):
|
||||
self.new_lines = max(self.new_lines, 1 + extra)
|
||||
if node is not None and self.add_line_information:
|
||||
self.write('# line: %s' % node.lineno)
|
||||
self.new_lines = 1
|
||||
|
||||
def body(self, statements):
|
||||
self.new_line = True
|
||||
self.indentation += 1
|
||||
for stmt in statements:
|
||||
self.visit(stmt)
|
||||
self.indentation -= 1
|
||||
|
||||
def body_or_else(self, node):
|
||||
self.body(node.body)
|
||||
if node.orelse:
|
||||
self.newline()
|
||||
self.write('else:')
|
||||
self.body(node.orelse)
|
||||
|
||||
def signature(self, node):
|
||||
want_comma = []
|
||||
def write_comma():
|
||||
if want_comma:
|
||||
self.write(', ')
|
||||
else:
|
||||
want_comma.append(True)
|
||||
|
||||
padding = [None] * (len(node.args) - len(node.defaults))
|
||||
for arg, default in zip(node.args, padding + node.defaults):
|
||||
write_comma()
|
||||
self.visit(arg)
|
||||
if default is not None:
|
||||
self.write('=')
|
||||
self.visit(default)
|
||||
if node.vararg is not None:
|
||||
write_comma()
|
||||
self.write('*' + node.vararg)
|
||||
if node.kwarg is not None:
|
||||
write_comma()
|
||||
self.write('**' + node.kwarg)
|
||||
|
||||
def decorators(self, node):
|
||||
for decorator in node.decorator_list:
|
||||
self.newline(decorator)
|
||||
self.write('@')
|
||||
self.visit(decorator)
|
||||
|
||||
# Statements
|
||||
|
||||
def visit_Assign(self, node):
|
||||
self.newline(node)
|
||||
for idx, target in enumerate(node.targets):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(target)
|
||||
self.write(' = ')
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_AugAssign(self, node):
|
||||
self.newline(node)
|
||||
self.visit(node.target)
|
||||
self.write(BINOP_SYMBOLS[type(node.op)] + '=')
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_ImportFrom(self, node):
|
||||
self.newline(node)
|
||||
self.write('from %s%s import ' % ('.' * node.level, node.module))
|
||||
for idx, item in enumerate(node.names):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.write(item)
|
||||
|
||||
def visit_Import(self, node):
|
||||
self.newline(node)
|
||||
for item in node.names:
|
||||
self.write('import ')
|
||||
self.visit(item)
|
||||
|
||||
def visit_Expr(self, node):
|
||||
self.newline(node)
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_FunctionDef(self, node):
|
||||
self.newline(extra=1)
|
||||
self.decorators(node)
|
||||
self.newline(node)
|
||||
self.write('def %s(' % node.name)
|
||||
self.signature(node.args)
|
||||
self.write('):')
|
||||
self.body(node.body)
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
have_args = []
|
||||
def paren_or_comma():
|
||||
if have_args:
|
||||
self.write(', ')
|
||||
else:
|
||||
have_args.append(True)
|
||||
self.write('(')
|
||||
|
||||
self.newline(extra=2)
|
||||
self.decorators(node)
|
||||
self.newline(node)
|
||||
self.write('class %s' % node.name)
|
||||
for base in node.bases:
|
||||
paren_or_comma()
|
||||
self.visit(base)
|
||||
# XXX: the if here is used to keep this module compatible
|
||||
# with python 2.6.
|
||||
if hasattr(node, 'keywords'):
|
||||
for keyword in node.keywords:
|
||||
paren_or_comma()
|
||||
self.write(keyword.arg + '=')
|
||||
self.visit(keyword.value)
|
||||
if node.starargs is not None:
|
||||
paren_or_comma()
|
||||
self.write('*')
|
||||
self.visit(node.starargs)
|
||||
if node.kwargs is not None:
|
||||
paren_or_comma()
|
||||
self.write('**')
|
||||
self.visit(node.kwargs)
|
||||
self.write(have_args and '):' or ':')
|
||||
self.body(node.body)
|
||||
|
||||
def visit_If(self, node):
|
||||
self.newline(node)
|
||||
self.write('if ')
|
||||
self.visit(node.test)
|
||||
self.write(':')
|
||||
self.body(node.body)
|
||||
while True:
|
||||
else_ = node.orelse
|
||||
if len(else_) == 1 and isinstance(else_[0], If):
|
||||
node = else_[0]
|
||||
self.newline()
|
||||
self.write('elif ')
|
||||
self.visit(node.test)
|
||||
self.write(':')
|
||||
self.body(node.body)
|
||||
else:
|
||||
self.newline()
|
||||
self.write('else:')
|
||||
self.body(else_)
|
||||
break
|
||||
|
||||
def visit_For(self, node):
|
||||
self.newline(node)
|
||||
self.write('for ')
|
||||
self.visit(node.target)
|
||||
self.write(' in ')
|
||||
self.visit(node.iter)
|
||||
self.write(':')
|
||||
self.body_or_else(node)
|
||||
|
||||
def visit_While(self, node):
|
||||
self.newline(node)
|
||||
self.write('while ')
|
||||
self.visit(node.test)
|
||||
self.write(':')
|
||||
self.body_or_else(node)
|
||||
|
||||
def visit_With(self, node):
|
||||
self.newline(node)
|
||||
self.write('with ')
|
||||
self.visit(node.context_expr)
|
||||
if node.optional_vars is not None:
|
||||
self.write(' as ')
|
||||
self.visit(node.optional_vars)
|
||||
self.write(':')
|
||||
self.body(node.body)
|
||||
|
||||
def visit_Pass(self, node):
|
||||
self.newline(node)
|
||||
self.write('pass')
|
||||
|
||||
def visit_Print(self, node):
|
||||
# XXX: python 2.6 only
|
||||
self.newline(node)
|
||||
self.write('print ')
|
||||
want_comma = False
|
||||
if node.dest is not None:
|
||||
self.write(' >> ')
|
||||
self.visit(node.dest)
|
||||
want_comma = True
|
||||
for value in node.values:
|
||||
if want_comma:
|
||||
self.write(', ')
|
||||
self.visit(value)
|
||||
want_comma = True
|
||||
if not node.nl:
|
||||
self.write(',')
|
||||
|
||||
def visit_Delete(self, node):
|
||||
self.newline(node)
|
||||
self.write('del ')
|
||||
for idx, target in enumerate(node):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(target)
|
||||
|
||||
def visit_TryExcept(self, node):
|
||||
self.newline(node)
|
||||
self.write('try:')
|
||||
self.body(node.body)
|
||||
for handler in node.handlers:
|
||||
self.visit(handler)
|
||||
|
||||
def visit_TryFinally(self, node):
|
||||
self.newline(node)
|
||||
self.write('try:')
|
||||
self.body(node.body)
|
||||
self.newline(node)
|
||||
self.write('finally:')
|
||||
self.body(node.finalbody)
|
||||
|
||||
def visit_Global(self, node):
|
||||
self.newline(node)
|
||||
self.write('global ' + ', '.join(node.names))
|
||||
|
||||
def visit_Nonlocal(self, node):
|
||||
self.newline(node)
|
||||
self.write('nonlocal ' + ', '.join(node.names))
|
||||
|
||||
def visit_Return(self, node):
|
||||
self.newline(node)
|
||||
self.write('return ')
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_Break(self, node):
|
||||
self.newline(node)
|
||||
self.write('break')
|
||||
|
||||
def visit_Continue(self, node):
|
||||
self.newline(node)
|
||||
self.write('continue')
|
||||
|
||||
def visit_Raise(self, node):
|
||||
# XXX: Python 2.6 / 3.0 compatibility
|
||||
self.newline(node)
|
||||
self.write('raise')
|
||||
if hasattr(node, 'exc') and node.exc is not None:
|
||||
self.write(' ')
|
||||
self.visit(node.exc)
|
||||
if node.cause is not None:
|
||||
self.write(' from ')
|
||||
self.visit(node.cause)
|
||||
elif hasattr(node, 'type') and node.type is not None:
|
||||
self.visit(node.type)
|
||||
if node.inst is not None:
|
||||
self.write(', ')
|
||||
self.visit(node.inst)
|
||||
if node.tback is not None:
|
||||
self.write(', ')
|
||||
self.visit(node.tback)
|
||||
|
||||
# Expressions
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
self.visit(node.value)
|
||||
self.write('.' + node.attr)
|
||||
|
||||
def visit_Call(self, node):
|
||||
want_comma = []
|
||||
def write_comma():
|
||||
if want_comma:
|
||||
self.write(', ')
|
||||
else:
|
||||
want_comma.append(True)
|
||||
|
||||
self.visit(node.func)
|
||||
self.write('(')
|
||||
for arg in node.args:
|
||||
write_comma()
|
||||
self.visit(arg)
|
||||
for keyword in node.keywords:
|
||||
write_comma()
|
||||
self.write(keyword.arg + '=')
|
||||
self.visit(keyword.value)
|
||||
if node.starargs is not None:
|
||||
write_comma()
|
||||
self.write('*')
|
||||
self.visit(node.starargs)
|
||||
if node.kwargs is not None:
|
||||
write_comma()
|
||||
self.write('**')
|
||||
self.visit(node.kwargs)
|
||||
self.write(')')
|
||||
|
||||
def visit_Name(self, node):
|
||||
self.write(node.id)
|
||||
|
||||
def visit_Str(self, node):
|
||||
self.write(repr(node.s))
|
||||
|
||||
def visit_Bytes(self, node):
|
||||
self.write(repr(node.s))
|
||||
|
||||
def visit_Num(self, node):
|
||||
self.write(repr(node.n))
|
||||
|
||||
def visit_Tuple(self, node):
|
||||
self.write('(')
|
||||
idx = -1
|
||||
for idx, item in enumerate(node.elts):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(item)
|
||||
self.write(idx and ')' or ',)')
|
||||
|
||||
def sequence_visit(left, right):
|
||||
def visit(self, node):
|
||||
self.write(left)
|
||||
for idx, item in enumerate(node.elts):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(item)
|
||||
self.write(right)
|
||||
return visit
|
||||
|
||||
visit_List = sequence_visit('[', ']')
|
||||
visit_Set = sequence_visit('{', '}')
|
||||
del sequence_visit
|
||||
|
||||
def visit_Dict(self, node):
|
||||
self.write('{')
|
||||
for idx, (key, value) in enumerate(zip(node.keys, node.values)):
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(key)
|
||||
self.write(': ')
|
||||
self.visit(value)
|
||||
self.write('}')
|
||||
|
||||
def visit_BinOp(self, node):
|
||||
self.visit(node.left)
|
||||
self.write(' %s ' % BINOP_SYMBOLS[type(node.op)])
|
||||
self.visit(node.right)
|
||||
|
||||
def visit_BoolOp(self, node):
|
||||
self.write('(')
|
||||
for idx, value in enumerate(node.values):
|
||||
if idx:
|
||||
self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
|
||||
self.visit(value)
|
||||
self.write(')')
|
||||
|
||||
def visit_Compare(self, node):
|
||||
self.write('(')
|
||||
self.write(node.left)
|
||||
for op, right in zip(node.ops, node.comparators):
|
||||
self.write(' %s %%' % CMPOP_SYMBOLS[type(op)])
|
||||
self.visit(right)
|
||||
self.write(')')
|
||||
|
||||
def visit_UnaryOp(self, node):
|
||||
self.write('(')
|
||||
op = UNARYOP_SYMBOLS[type(node.op)]
|
||||
self.write(op)
|
||||
if op == 'not':
|
||||
self.write(' ')
|
||||
self.visit(node.operand)
|
||||
self.write(')')
|
||||
|
||||
def visit_Subscript(self, node):
|
||||
self.visit(node.value)
|
||||
self.write('[')
|
||||
self.visit(node.slice)
|
||||
self.write(']')
|
||||
|
||||
def visit_Slice(self, node):
|
||||
if node.lower is not None:
|
||||
self.visit(node.lower)
|
||||
self.write(':')
|
||||
if node.upper is not None:
|
||||
self.visit(node.upper)
|
||||
if node.step is not None:
|
||||
self.write(':')
|
||||
if not (isinstance(node.step, Name) and node.step.id == 'None'):
|
||||
self.visit(node.step)
|
||||
|
||||
def visit_ExtSlice(self, node):
|
||||
for idx, item in node.dims:
|
||||
if idx:
|
||||
self.write(', ')
|
||||
self.visit(item)
|
||||
|
||||
def visit_Yield(self, node):
|
||||
self.write('yield ')
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_Lambda(self, node):
|
||||
self.write('lambda ')
|
||||
self.signature(node.args)
|
||||
self.write(': ')
|
||||
self.visit(node.body)
|
||||
|
||||
def visit_Ellipsis(self, node):
|
||||
self.write('Ellipsis')
|
||||
|
||||
def generator_visit(left, right):
|
||||
def visit(self, node):
|
||||
self.write(left)
|
||||
self.visit(node.elt)
|
||||
for comprehension in node.generators:
|
||||
self.visit(comprehension)
|
||||
self.write(right)
|
||||
return visit
|
||||
|
||||
visit_ListComp = generator_visit('[', ']')
|
||||
visit_GeneratorExp = generator_visit('(', ')')
|
||||
visit_SetComp = generator_visit('{', '}')
|
||||
del generator_visit
|
||||
|
||||
def visit_DictComp(self, node):
|
||||
self.write('{')
|
||||
self.visit(node.key)
|
||||
self.write(': ')
|
||||
self.visit(node.value)
|
||||
for comprehension in node.generators:
|
||||
self.visit(comprehension)
|
||||
self.write('}')
|
||||
|
||||
def visit_IfExp(self, node):
|
||||
self.visit(node.body)
|
||||
self.write(' if ')
|
||||
self.visit(node.test)
|
||||
self.write(' else ')
|
||||
self.visit(node.orelse)
|
||||
|
||||
def visit_Starred(self, node):
|
||||
self.write('*')
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_Repr(self, node):
|
||||
# XXX: python 2.6 only
|
||||
self.write('`')
|
||||
self.visit(node.value)
|
||||
self.write('`')
|
||||
|
||||
# Helper Nodes
|
||||
|
||||
def visit_alias(self, node):
|
||||
self.write(node.name)
|
||||
if node.asname is not None:
|
||||
self.write(' as ' + node.asname)
|
||||
|
||||
def visit_comprehension(self, node):
|
||||
self.write(' for ')
|
||||
self.visit(node.target)
|
||||
self.write(' in ')
|
||||
self.visit(node.iter)
|
||||
if node.ifs:
|
||||
for if_ in node.ifs:
|
||||
self.write(' if ')
|
||||
self.visit(if_)
|
||||
|
||||
def visit_excepthandler(self, node):
|
||||
self.newline(node)
|
||||
self.write('except')
|
||||
if node.type is not None:
|
||||
self.write(' ')
|
||||
self.visit(node.type)
|
||||
if node.name is not None:
|
||||
self.write(' as ')
|
||||
self.visit(node.name)
|
||||
self.write(':')
|
||||
self.body(node.body)
|
||||
4
bitbake/lib/ply/__init__.py
Normal file
4
bitbake/lib/ply/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# PLY package
|
||||
# Author: David Beazley (dave@dabeaz.com)
|
||||
|
||||
__all__ = ['lex','yacc']
|
||||
1058
bitbake/lib/ply/lex.py
Normal file
1058
bitbake/lib/ply/lex.py
Normal file
File diff suppressed because it is too large
Load Diff
3276
bitbake/lib/ply/yacc.py
Normal file
3276
bitbake/lib/ply/yacc.py
Normal file
File diff suppressed because it is too large
Load Diff
0
bitbake/lib/pysh/__init__.py
Normal file
0
bitbake/lib/pysh/__init__.py
Normal file
710
bitbake/lib/pysh/builtin.py
Normal file
710
bitbake/lib/pysh/builtin.py
Normal file
@@ -0,0 +1,710 @@
|
||||
# builtin.py - builtins and utilities definitions for pysh.
|
||||
#
|
||||
# Copyright 2007 Patrick Mezard
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
"""Builtin and internal utilities implementations.
|
||||
|
||||
- Beware not to use python interpreter environment as if it were the shell
|
||||
environment. For instance, commands working directory must be explicitely handled
|
||||
through env['PWD'] instead of relying on python working directory.
|
||||
"""
|
||||
import errno
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
def has_subprocess_bug():
|
||||
return getattr(subprocess, 'list2cmdline') and \
|
||||
( subprocess.list2cmdline(['']) == '' or \
|
||||
subprocess.list2cmdline(['foo|bar']) == 'foo|bar')
|
||||
|
||||
# Detect python bug 1634343: "subprocess swallows empty arguments under win32"
|
||||
# <http://sourceforge.net/tracker/index.php?func=detail&aid=1634343&group_id=5470&atid=105470>
|
||||
# Also detect: "[ 1710802 ] subprocess must escape redirection characters under win32"
|
||||
# <http://sourceforge.net/tracker/index.php?func=detail&aid=1710802&group_id=5470&atid=105470>
|
||||
if has_subprocess_bug():
|
||||
import subprocess_fix
|
||||
subprocess.list2cmdline = subprocess_fix.list2cmdline
|
||||
|
||||
from sherrors import *
|
||||
|
||||
class NonExitingParser(optparse.OptionParser):
|
||||
"""OptionParser default behaviour upon error is to print the error message and
|
||||
exit. Raise a utility error instead.
|
||||
"""
|
||||
def error(self, msg):
|
||||
raise UtilityError(msg)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# set special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_SET = NonExitingParser(usage="set - set or unset options and positional parameters")
|
||||
OPT_SET.add_option( '-f', action='store_true', dest='has_f', default=False,
|
||||
help='The shell shall disable pathname expansion.')
|
||||
OPT_SET.add_option('-e', action='store_true', dest='has_e', default=False,
|
||||
help="""When this option is on, if a simple command fails for any of the \
|
||||
reasons listed in Consequences of Shell Errors or returns an exit status \
|
||||
value >0, and is not part of the compound list following a while, until, \
|
||||
or if keyword, and is not a part of an AND or OR list, and is not a \
|
||||
pipeline preceded by the ! reserved word, then the shell shall immediately \
|
||||
exit.""")
|
||||
OPT_SET.add_option('-x', action='store_true', dest='has_x', default=False,
|
||||
help="""The shell shall write to standard error a trace for each command \
|
||||
after it expands the command and before it executes it. It is unspecified \
|
||||
whether the command that turns tracing off is traced.""")
|
||||
|
||||
def builtin_set(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_SET.parse_args(args)
|
||||
env = interp.get_env()
|
||||
|
||||
if option.has_f:
|
||||
env.set_opt('-f')
|
||||
if option.has_e:
|
||||
env.set_opt('-e')
|
||||
if option.has_x:
|
||||
env.set_opt('-x')
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# shift special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
def builtin_shift(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
params = interp.get_env().get_positional_args()
|
||||
if args:
|
||||
try:
|
||||
n = int(args[0])
|
||||
if n > len(params):
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
return 1
|
||||
else:
|
||||
n = 1
|
||||
|
||||
params[:n] = []
|
||||
interp.get_env().set_positional_args(params)
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# export special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_EXPORT = NonExitingParser(usage="set - set or unset options and positional parameters")
|
||||
OPT_EXPORT.add_option('-p', action='store_true', dest='has_p', default=False)
|
||||
|
||||
def builtin_export(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_EXPORT.parse_args(args)
|
||||
if option.has_p:
|
||||
raise NotImplementedError()
|
||||
|
||||
for arg in args:
|
||||
try:
|
||||
name, value = arg.split('=', 1)
|
||||
except ValueError:
|
||||
name, value = arg, None
|
||||
env = interp.get_env().export(name, value)
|
||||
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# return special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
def builtin_return(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
res = 0
|
||||
if args:
|
||||
try:
|
||||
res = int(args[0])
|
||||
except ValueError:
|
||||
res = 0
|
||||
if not 0<=res<=255:
|
||||
res = 0
|
||||
|
||||
# BUG: should be last executed command exit code
|
||||
raise ReturnSignal(res)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# trap special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
def builtin_trap(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
if len(args) < 2:
|
||||
stderr.write('trap: usage: trap [[arg] signal_spec ...]\n')
|
||||
return 2
|
||||
|
||||
action = args[0]
|
||||
for sig in args[1:]:
|
||||
try:
|
||||
env.traps[sig] = action
|
||||
except Exception, e:
|
||||
stderr.write('trap: %s\n' % str(e))
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# unset special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_UNSET = NonExitingParser("unset - unset values and attributes of variables and functions")
|
||||
OPT_UNSET.add_option( '-f', action='store_true', dest='has_f', default=False)
|
||||
OPT_UNSET.add_option( '-v', action='store_true', dest='has_v', default=False)
|
||||
|
||||
def builtin_unset(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_UNSET.parse_args(args)
|
||||
|
||||
status = 0
|
||||
env = interp.get_env()
|
||||
for arg in args:
|
||||
try:
|
||||
if option.has_f:
|
||||
env.remove_function(arg)
|
||||
else:
|
||||
del env[arg]
|
||||
except KeyError:
|
||||
pass
|
||||
except VarAssignmentError:
|
||||
status = 1
|
||||
|
||||
return status
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# wait special builtin
|
||||
#-------------------------------------------------------------------------------
|
||||
def builtin_wait(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
return interp.wait([int(arg) for arg in args])
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# cat utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_cat(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
if not args:
|
||||
args = ['-']
|
||||
|
||||
status = 0
|
||||
for arg in args:
|
||||
if arg == '-':
|
||||
data = stdin.read()
|
||||
else:
|
||||
path = os.path.join(env['PWD'], arg)
|
||||
try:
|
||||
f = file(path, 'rb')
|
||||
try:
|
||||
data = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
except IOError, e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
status = 1
|
||||
continue
|
||||
stdout.write(data)
|
||||
stdout.flush()
|
||||
return status
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# cd utility
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_CD = NonExitingParser("cd - change the working directory")
|
||||
|
||||
def utility_cd(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_CD.parse_args(args)
|
||||
env = interp.get_env()
|
||||
|
||||
directory = None
|
||||
printdir = False
|
||||
if not args:
|
||||
home = env.get('HOME')
|
||||
if home:
|
||||
# Unspecified, do nothing
|
||||
return 0
|
||||
else:
|
||||
directory = home
|
||||
elif len(args)==1:
|
||||
directory = args[0]
|
||||
if directory=='-':
|
||||
if 'OLDPWD' not in env:
|
||||
raise UtilityError("OLDPWD not set")
|
||||
printdir = True
|
||||
directory = env['OLDPWD']
|
||||
else:
|
||||
raise UtilityError("too many arguments")
|
||||
|
||||
curpath = None
|
||||
# Absolute directories will be handled correctly by the os.path.join call.
|
||||
if not directory.startswith('.') and not directory.startswith('..'):
|
||||
cdpaths = env.get('CDPATH', '.').split(';')
|
||||
for cdpath in cdpaths:
|
||||
p = os.path.join(cdpath, directory)
|
||||
if os.path.isdir(p):
|
||||
curpath = p
|
||||
break
|
||||
|
||||
if curpath is None:
|
||||
curpath = directory
|
||||
curpath = os.path.join(env['PWD'], directory)
|
||||
|
||||
env['OLDPWD'] = env['PWD']
|
||||
env['PWD'] = curpath
|
||||
if printdir:
|
||||
stdout.write('%s\n' % curpath)
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# colon utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_colon(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# echo utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_echo(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
# Echo only takes arguments, no options. Use printf if you need fancy stuff.
|
||||
output = ' '.join(args) + '\n'
|
||||
stdout.write(output)
|
||||
stdout.flush()
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# egrep utility
|
||||
#-------------------------------------------------------------------------------
|
||||
# egrep is usually a shell script.
|
||||
# Unfortunately, pysh does not support shell scripts *with arguments* right now,
|
||||
# so the redirection is implemented here, assuming grep is available.
|
||||
def utility_egrep(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
return run_command('grep', ['-E'] + args, interp, env, stdin, stdout,
|
||||
stderr, debugflags)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# env utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_env(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
if args and args[0]=='-i':
|
||||
raise NotImplementedError('env: -i option is not implemented')
|
||||
|
||||
i = 0
|
||||
for arg in args:
|
||||
if '=' not in arg:
|
||||
break
|
||||
# Update the current environment
|
||||
name, value = arg.split('=', 1)
|
||||
env[name] = value
|
||||
i += 1
|
||||
|
||||
if args[i:]:
|
||||
# Find then execute the specified interpreter
|
||||
utility = env.find_in_path(args[i])
|
||||
if not utility:
|
||||
return 127
|
||||
args[i:i+1] = utility
|
||||
name = args[i]
|
||||
args = args[i+1:]
|
||||
try:
|
||||
return run_command(name, args, interp, env, stdin, stdout, stderr,
|
||||
debugflags)
|
||||
except UtilityError:
|
||||
stderr.write('env: failed to execute %s' % ' '.join([name]+args))
|
||||
return 126
|
||||
else:
|
||||
for pair in env.get_variables().iteritems():
|
||||
stdout.write('%s=%s\n' % pair)
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# exit utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_exit(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
res = None
|
||||
if args:
|
||||
try:
|
||||
res = int(args[0])
|
||||
except ValueError:
|
||||
res = None
|
||||
if not 0<=res<=255:
|
||||
res = None
|
||||
|
||||
if res is None:
|
||||
# BUG: should be last executed command exit code
|
||||
res = 0
|
||||
|
||||
raise ExitSignal(res)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# fgrep utility
|
||||
#-------------------------------------------------------------------------------
|
||||
# see egrep
|
||||
def utility_fgrep(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
return run_command('grep', ['-F'] + args, interp, env, stdin, stdout,
|
||||
stderr, debugflags)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# gunzip utility
|
||||
#-------------------------------------------------------------------------------
|
||||
# see egrep
|
||||
def utility_gunzip(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
return run_command('gzip', ['-d'] + args, interp, env, stdin, stdout,
|
||||
stderr, debugflags)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# kill utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_kill(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
for arg in args:
|
||||
pid = int(arg)
|
||||
status = subprocess.call(['pskill', '/T', str(pid)],
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
# pskill is asynchronous, hence the stupid polling loop
|
||||
while 1:
|
||||
p = subprocess.Popen(['pslist', str(pid)],
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
output = p.communicate()[0]
|
||||
if ('process %d was not' % pid) in output:
|
||||
break
|
||||
time.sleep(1)
|
||||
return status
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# mkdir utility
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_MKDIR = NonExitingParser("mkdir - make directories.")
|
||||
OPT_MKDIR.add_option('-p', action='store_true', dest='has_p', default=False)
|
||||
|
||||
def utility_mkdir(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
# TODO: implement umask
|
||||
# TODO: implement proper utility error report
|
||||
option, args = OPT_MKDIR.parse_args(args)
|
||||
for arg in args:
|
||||
path = os.path.join(env['PWD'], arg)
|
||||
if option.has_p:
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except IOError, e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
else:
|
||||
os.mkdir(path)
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# netstat utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_netstat(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
# Do you really expect me to implement netstat ?
|
||||
# This empty form is enough for Mercurial tests since it's
|
||||
# supposed to generate nothing upon success. Faking this test
|
||||
# is not a big deal either.
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# pwd utility
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_PWD = NonExitingParser("pwd - return working directory name")
|
||||
OPT_PWD.add_option('-L', action='store_true', dest='has_L', default=True,
|
||||
help="""If the PWD environment variable contains an absolute pathname of \
|
||||
the current directory that does not contain the filenames dot or dot-dot, \
|
||||
pwd shall write this pathname to standard output. Otherwise, the -L option \
|
||||
shall behave as the -P option.""")
|
||||
OPT_PWD.add_option('-P', action='store_true', dest='has_L', default=False,
|
||||
help="""The absolute pathname written shall not contain filenames that, in \
|
||||
the context of the pathname, refer to files of type symbolic link.""")
|
||||
|
||||
def utility_pwd(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_PWD.parse_args(args)
|
||||
stdout.write('%s\n' % env['PWD'])
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# printf utility
|
||||
#-------------------------------------------------------------------------------
|
||||
RE_UNESCAPE = re.compile(r'(\\x[a-zA-Z0-9]{2}|\\[0-7]{1,3}|\\.)')
|
||||
|
||||
def utility_printf(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
def replace(m):
|
||||
assert m.group()
|
||||
g = m.group()[1:]
|
||||
if g.startswith('x'):
|
||||
return chr(int(g[1:], 16))
|
||||
if len(g) <= 3 and len([c for c in g if c in '01234567']) == len(g):
|
||||
# Yay, an octal number
|
||||
return chr(int(g, 8))
|
||||
return {
|
||||
'a': '\a',
|
||||
'b': '\b',
|
||||
'f': '\f',
|
||||
'n': '\n',
|
||||
'r': '\r',
|
||||
't': '\t',
|
||||
'v': '\v',
|
||||
'\\': '\\',
|
||||
}.get(g)
|
||||
|
||||
# Convert escape sequences
|
||||
format = re.sub(RE_UNESCAPE, replace, args[0])
|
||||
stdout.write(format % tuple(args[1:]))
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# true utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_true(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# sed utility
|
||||
#-------------------------------------------------------------------------------
|
||||
RE_SED = re.compile(r'^s(.).*\1[a-zA-Z]*$')
|
||||
|
||||
# cygwin sed fails with some expressions when they do not end with a single space.
|
||||
# see unit tests for details. Interestingly, the same expressions works perfectly
|
||||
# in cygwin shell.
|
||||
def utility_sed(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
# Scan pattern arguments and append a space if necessary
|
||||
for i in xrange(len(args)):
|
||||
if not RE_SED.search(args[i]):
|
||||
continue
|
||||
args[i] = args[i] + ' '
|
||||
|
||||
return run_command(name, args, interp, env, stdin, stdout,
|
||||
stderr, debugflags)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# sleep utility
|
||||
#-------------------------------------------------------------------------------
|
||||
def utility_sleep(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
time.sleep(int(args[0]))
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# sort utility
|
||||
#-------------------------------------------------------------------------------
|
||||
OPT_SORT = NonExitingParser("sort - sort, merge, or sequence check text files")
|
||||
|
||||
def utility_sort(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
||||
|
||||
def sort(path):
|
||||
if path == '-':
|
||||
lines = stdin.readlines()
|
||||
else:
|
||||
try:
|
||||
f = file(path)
|
||||
try:
|
||||
lines = f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
except IOError, e:
|
||||
stderr.write(str(e) + '\n')
|
||||
return 1
|
||||
|
||||
if lines and lines[-1][-1]!='\n':
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
return lines
|
||||
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
option, args = OPT_SORT.parse_args(args)
|
||||
alllines = []
|
||||
|
||||
if len(args)<=0:
|
||||
args += ['-']
|
||||
|
||||
# Load all files lines
|
||||
curdir = os.getcwd()
|
||||
try:
|
||||
os.chdir(env['PWD'])
|
||||
for path in args:
|
||||
alllines += sort(path)
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
alllines.sort()
|
||||
for line in alllines:
|
||||
stdout.write(line)
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# hg utility
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
hgcommands = [
|
||||
'add',
|
||||
'addremove',
|
||||
'commit', 'ci',
|
||||
'debugrename',
|
||||
'debugwalk',
|
||||
'falabala', # Dummy command used in a mercurial test
|
||||
'incoming',
|
||||
'locate',
|
||||
'pull',
|
||||
'push',
|
||||
'qinit',
|
||||
'remove', 'rm',
|
||||
'rename', 'mv',
|
||||
'revert',
|
||||
'showconfig',
|
||||
'status', 'st',
|
||||
'strip',
|
||||
]
|
||||
|
||||
def rewriteslashes(name, args):
|
||||
# Several hg commands output file paths, rewrite the separators
|
||||
if len(args) > 1 and name.lower().endswith('python') \
|
||||
and args[0].endswith('hg'):
|
||||
for cmd in hgcommands:
|
||||
if cmd in args[1:]:
|
||||
return True
|
||||
|
||||
# svn output contains many paths with OS specific separators.
|
||||
# Normalize these to unix paths.
|
||||
base = os.path.basename(name)
|
||||
if base.startswith('svn'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def rewritehg(output):
|
||||
if not output:
|
||||
return output
|
||||
# Rewrite os specific messages
|
||||
output = output.replace(': The system cannot find the file specified',
|
||||
': No such file or directory')
|
||||
output = re.sub(': Access is denied.*$', ': Permission denied', output)
|
||||
output = output.replace(': No connection could be made because the target machine actively refused it',
|
||||
': Connection refused')
|
||||
return output
|
||||
|
||||
|
||||
def run_command(name, args, interp, env, stdin, stdout,
|
||||
stderr, debugflags):
|
||||
# Execute the command
|
||||
if 'debug-utility' in debugflags:
|
||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||
|
||||
hgbin = interp.options().hgbinary
|
||||
ishg = hgbin and ('hg' in name or args and 'hg' in args[0])
|
||||
unixoutput = 'cygwin' in name or ishg
|
||||
|
||||
exec_env = env.get_variables()
|
||||
try:
|
||||
# BUG: comparing file descriptor is clearly not a reliable way to tell
|
||||
# whether they point on the same underlying object. But in pysh limited
|
||||
# scope this is usually right, we do not expect complicated redirections
|
||||
# besides usual 2>&1.
|
||||
# Still there is one case we have but cannot deal with is when stdout
|
||||
# and stderr are redirected *by pysh caller*. This the reason for the
|
||||
# --redirect pysh() option.
|
||||
# Now, we want to know they are the same because we sometimes need to
|
||||
# transform the command output, mostly remove CR-LF to ensure that
|
||||
# command output is unix-like. Cygwin utilies are a special case because
|
||||
# they explicitely set their output streams to binary mode, so we have
|
||||
# nothing to do. For all others commands, we have to guess whether they
|
||||
# are sending text data, in which case the transformation must be done.
|
||||
# Again, the NUL character test is unreliable but should be enough for
|
||||
# hg tests.
|
||||
redirected = stdout.fileno()==stderr.fileno()
|
||||
if not redirected:
|
||||
p = subprocess.Popen([name] + args, cwd=env['PWD'], env=exec_env,
|
||||
stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
else:
|
||||
p = subprocess.Popen([name] + args, cwd=env['PWD'], env=exec_env,
|
||||
stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, err = p.communicate()
|
||||
except WindowsError, e:
|
||||
raise UtilityError(str(e))
|
||||
|
||||
if not unixoutput:
|
||||
def encode(s):
|
||||
if '\0' in s:
|
||||
return s
|
||||
return s.replace('\r\n', '\n')
|
||||
else:
|
||||
encode = lambda s: s
|
||||
|
||||
if rewriteslashes(name, args):
|
||||
encode1_ = encode
|
||||
def encode(s):
|
||||
s = encode1_(s)
|
||||
s = s.replace('\\\\', '\\')
|
||||
s = s.replace('\\', '/')
|
||||
return s
|
||||
|
||||
if ishg:
|
||||
encode2_ = encode
|
||||
def encode(s):
|
||||
return rewritehg(encode2_(s))
|
||||
|
||||
stdout.write(encode(out))
|
||||
if not redirected:
|
||||
stderr.write(encode(err))
|
||||
return p.returncode
|
||||
|
||||
1367
bitbake/lib/pysh/interp.py
Normal file
1367
bitbake/lib/pysh/interp.py
Normal file
File diff suppressed because it is too large
Load Diff
116
bitbake/lib/pysh/lsprof.py
Normal file
116
bitbake/lib/pysh/lsprof.py
Normal file
@@ -0,0 +1,116 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
from _lsprof import Profiler, profiler_entry
|
||||
|
||||
__all__ = ['profile', 'Stats']
|
||||
|
||||
def profile(f, *args, **kwds):
|
||||
"""XXX docstring"""
|
||||
p = Profiler()
|
||||
p.enable(subcalls=True, builtins=True)
|
||||
try:
|
||||
f(*args, **kwds)
|
||||
finally:
|
||||
p.disable()
|
||||
return Stats(p.getstats())
|
||||
|
||||
|
||||
class Stats(object):
|
||||
"""XXX docstring"""
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def sort(self, crit="inlinetime"):
|
||||
"""XXX docstring"""
|
||||
if crit not in profiler_entry.__dict__:
|
||||
raise ValueError("Can't sort by %s" % crit)
|
||||
self.data.sort(lambda b, a: cmp(getattr(a, crit),
|
||||
getattr(b, crit)))
|
||||
for e in self.data:
|
||||
if e.calls:
|
||||
e.calls.sort(lambda b, a: cmp(getattr(a, crit),
|
||||
getattr(b, crit)))
|
||||
|
||||
def pprint(self, top=None, file=None, limit=None, climit=None):
|
||||
"""XXX docstring"""
|
||||
if file is None:
|
||||
file = sys.stdout
|
||||
d = self.data
|
||||
if top is not None:
|
||||
d = d[:top]
|
||||
cols = "% 12s %12s %11.4f %11.4f %s\n"
|
||||
hcols = "% 12s %12s %12s %12s %s\n"
|
||||
cols2 = "+%12s %12s %11.4f %11.4f + %s\n"
|
||||
file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
|
||||
"Inline(ms)", "module:lineno(function)"))
|
||||
count = 0
|
||||
for e in d:
|
||||
file.write(cols % (e.callcount, e.reccallcount, e.totaltime,
|
||||
e.inlinetime, label(e.code)))
|
||||
count += 1
|
||||
if limit is not None and count == limit:
|
||||
return
|
||||
ccount = 0
|
||||
if e.calls:
|
||||
for se in e.calls:
|
||||
file.write(cols % ("+%s" % se.callcount, se.reccallcount,
|
||||
se.totaltime, se.inlinetime,
|
||||
"+%s" % label(se.code)))
|
||||
count += 1
|
||||
ccount += 1
|
||||
if limit is not None and count == limit:
|
||||
return
|
||||
if climit is not None and ccount == climit:
|
||||
break
|
||||
|
||||
def freeze(self):
|
||||
"""Replace all references to code objects with string
|
||||
descriptions; this makes it possible to pickle the instance."""
|
||||
|
||||
# this code is probably rather ickier than it needs to be!
|
||||
for i in range(len(self.data)):
|
||||
e = self.data[i]
|
||||
if not isinstance(e.code, str):
|
||||
self.data[i] = type(e)((label(e.code),) + e[1:])
|
||||
if e.calls:
|
||||
for j in range(len(e.calls)):
|
||||
se = e.calls[j]
|
||||
if not isinstance(se.code, str):
|
||||
e.calls[j] = type(se)((label(se.code),) + se[1:])
|
||||
|
||||
_fn2mod = {}
|
||||
|
||||
def label(code):
|
||||
if isinstance(code, str):
|
||||
return code
|
||||
try:
|
||||
mname = _fn2mod[code.co_filename]
|
||||
except KeyError:
|
||||
for k, v in sys.modules.items():
|
||||
if v is None:
|
||||
continue
|
||||
if not hasattr(v, '__file__'):
|
||||
continue
|
||||
if not isinstance(v.__file__, str):
|
||||
continue
|
||||
if v.__file__.startswith(code.co_filename):
|
||||
mname = _fn2mod[code.co_filename] = k
|
||||
break
|
||||
else:
|
||||
mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
|
||||
|
||||
return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
sys.argv = sys.argv[1:]
|
||||
if not sys.argv:
|
||||
print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
|
||||
sys.exit(2)
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
|
||||
stats = profile(execfile, sys.argv[0], globals(), locals())
|
||||
stats.sort()
|
||||
stats.pprint()
|
||||
167
bitbake/lib/pysh/pysh.py
Normal file
167
bitbake/lib/pysh/pysh.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# pysh.py - command processing for pysh.
|
||||
#
|
||||
# Copyright 2007 Patrick Mezard
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import interp
|
||||
|
||||
SH_OPT = optparse.OptionParser(prog='pysh', usage="%prog [OPTIONS]", version='0.1')
|
||||
SH_OPT.add_option('-c', action='store_true', dest='command_string', default=None,
|
||||
help='A string that shall be interpreted by the shell as one or more commands')
|
||||
SH_OPT.add_option('--redirect-to', dest='redirect_to', default=None,
|
||||
help='Redirect script commands stdout and stderr to the specified file')
|
||||
# See utility_command in builtin.py about the reason for this flag.
|
||||
SH_OPT.add_option('--redirected', dest='redirected', action='store_true', default=False,
|
||||
help='Tell the interpreter that stdout and stderr are actually the same objects, which is really stdout')
|
||||
SH_OPT.add_option('--debug-parsing', action='store_true', dest='debug_parsing', default=False,
|
||||
help='Trace PLY execution')
|
||||
SH_OPT.add_option('--debug-tree', action='store_true', dest='debug_tree', default=False,
|
||||
help='Display the generated syntax tree.')
|
||||
SH_OPT.add_option('--debug-cmd', action='store_true', dest='debug_cmd', default=False,
|
||||
help='Trace command execution before parameters expansion and exit status.')
|
||||
SH_OPT.add_option('--debug-utility', action='store_true', dest='debug_utility', default=False,
|
||||
help='Trace utility calls, after parameters expansions')
|
||||
SH_OPT.add_option('--ast', action='store_true', dest='ast', default=False,
|
||||
help='Encoded commands to execute in a subprocess')
|
||||
SH_OPT.add_option('--profile', action='store_true', default=False,
|
||||
help='Profile pysh run')
|
||||
|
||||
|
||||
def split_args(args):
|
||||
# Separate shell arguments from command ones
|
||||
# Just stop at the first argument not starting with a dash. I know, this is completely broken,
|
||||
# it ignores files starting with a dash or may take option values for command file. This is not
|
||||
# supposed to happen for now
|
||||
command_index = len(args)
|
||||
for i,arg in enumerate(args):
|
||||
if not arg.startswith('-'):
|
||||
command_index = i
|
||||
break
|
||||
|
||||
return args[:command_index], args[command_index:]
|
||||
|
||||
|
||||
def fixenv(env):
|
||||
path = env.get('PATH')
|
||||
if path is not None:
|
||||
parts = path.split(os.pathsep)
|
||||
# Remove Windows utilities from PATH, they are useless at best and
|
||||
# some of them (find) may be confused with other utilities.
|
||||
parts = [p for p in parts if 'system32' not in p.lower()]
|
||||
env['PATH'] = os.pathsep.join(parts)
|
||||
if env.get('HOME') is None:
|
||||
# Several utilities, including cvsps, cannot work without
|
||||
# a defined HOME directory.
|
||||
env['HOME'] = os.path.expanduser('~')
|
||||
return env
|
||||
|
||||
def _sh(cwd, shargs, cmdargs, options, debugflags=None, env=None):
|
||||
if os.environ.get('PYSH_TEXT') != '1':
|
||||
import msvcrt
|
||||
for fp in (sys.stdin, sys.stdout, sys.stderr):
|
||||
msvcrt.setmode(fp.fileno(), os.O_BINARY)
|
||||
|
||||
hgbin = os.environ.get('PYSH_HGTEXT') != '1'
|
||||
|
||||
if debugflags is None:
|
||||
debugflags = []
|
||||
if options.debug_parsing: debugflags.append('debug-parsing')
|
||||
if options.debug_utility: debugflags.append('debug-utility')
|
||||
if options.debug_cmd: debugflags.append('debug-cmd')
|
||||
if options.debug_tree: debugflags.append('debug-tree')
|
||||
|
||||
if env is None:
|
||||
env = fixenv(dict(os.environ))
|
||||
if cwd is None:
|
||||
cwd = os.getcwd()
|
||||
|
||||
if not cmdargs:
|
||||
# Nothing to do
|
||||
return 0
|
||||
|
||||
ast = None
|
||||
command_file = None
|
||||
if options.command_string:
|
||||
input = cmdargs[0]
|
||||
if not options.ast:
|
||||
input += '\n'
|
||||
else:
|
||||
args, input = interp.decodeargs(input), None
|
||||
env, ast = args
|
||||
cwd = env.get('PWD', cwd)
|
||||
else:
|
||||
command_file = cmdargs[0]
|
||||
arguments = cmdargs[1:]
|
||||
|
||||
prefix = interp.resolve_shebang(command_file, ignoreshell=True)
|
||||
if prefix:
|
||||
input = ' '.join(prefix + [command_file] + arguments)
|
||||
else:
|
||||
# Read commands from file
|
||||
f = file(command_file)
|
||||
try:
|
||||
# Trailing newline to help the parser
|
||||
input = f.read() + '\n'
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
redirect = None
|
||||
try:
|
||||
if options.redirected:
|
||||
stdout = sys.stdout
|
||||
stderr = stdout
|
||||
elif options.redirect_to:
|
||||
redirect = open(options.redirect_to, 'wb')
|
||||
stdout = redirect
|
||||
stderr = redirect
|
||||
else:
|
||||
stdout = sys.stdout
|
||||
stderr = sys.stderr
|
||||
|
||||
# TODO: set arguments to environment variables
|
||||
opts = interp.Options()
|
||||
opts.hgbinary = hgbin
|
||||
ip = interp.Interpreter(cwd, debugflags, stdout=stdout, stderr=stderr,
|
||||
opts=opts)
|
||||
try:
|
||||
# Export given environment in shell object
|
||||
for k,v in env.iteritems():
|
||||
ip.get_env().export(k,v)
|
||||
return ip.execute_script(input, ast, scriptpath=command_file)
|
||||
finally:
|
||||
ip.close()
|
||||
finally:
|
||||
if redirect is not None:
|
||||
redirect.close()
|
||||
|
||||
def sh(cwd=None, args=None, debugflags=None, env=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
shargs, cmdargs = split_args(args)
|
||||
options, shargs = SH_OPT.parse_args(shargs)
|
||||
|
||||
if options.profile:
|
||||
import lsprof
|
||||
p = lsprof.Profiler()
|
||||
p.enable(subcalls=True)
|
||||
try:
|
||||
return _sh(cwd, shargs, cmdargs, options, debugflags, env)
|
||||
finally:
|
||||
p.disable()
|
||||
stats = lsprof.Stats(p.getstats())
|
||||
stats.sort()
|
||||
stats.pprint(top=10, file=sys.stderr, climit=5)
|
||||
else:
|
||||
return _sh(cwd, shargs, cmdargs, options, debugflags, env)
|
||||
|
||||
def main():
|
||||
sys.exit(sh())
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
888
bitbake/lib/pysh/pyshlex.py
Normal file
888
bitbake/lib/pysh/pyshlex.py
Normal file
@@ -0,0 +1,888 @@
|
||||
# pyshlex.py - PLY compatible lexer for pysh.
|
||||
#
|
||||
# Copyright 2007 Patrick Mezard
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
# TODO:
|
||||
# - review all "char in 'abc'" snippets: the empty string can be matched
|
||||
# - test line continuations within quoted/expansion strings
|
||||
# - eof is buggy wrt sublexers
|
||||
# - the lexer cannot really work in pull mode as it would be required to run
|
||||
# PLY in pull mode. It was designed to work incrementally and it would not be
|
||||
# that hard to enable pull mode.
|
||||
import re
|
||||
try:
|
||||
s = set()
|
||||
del s
|
||||
except NameError:
|
||||
from Set import Set as set
|
||||
|
||||
from ply import lex
|
||||
from sherrors import *
|
||||
|
||||
class NeedMore(Exception):
|
||||
pass
|
||||
|
||||
def is_blank(c):
|
||||
return c in (' ', '\t')
|
||||
|
||||
_RE_DIGITS = re.compile(r'^\d+$')
|
||||
|
||||
def are_digits(s):
|
||||
return _RE_DIGITS.search(s) is not None
|
||||
|
||||
_OPERATORS = dict([
|
||||
('&&', 'AND_IF'),
|
||||
('||', 'OR_IF'),
|
||||
(';;', 'DSEMI'),
|
||||
('<<', 'DLESS'),
|
||||
('>>', 'DGREAT'),
|
||||
('<&', 'LESSAND'),
|
||||
('>&', 'GREATAND'),
|
||||
('<>', 'LESSGREAT'),
|
||||
('<<-', 'DLESSDASH'),
|
||||
('>|', 'CLOBBER'),
|
||||
('&', 'AMP'),
|
||||
(';', 'COMMA'),
|
||||
('<', 'LESS'),
|
||||
('>', 'GREATER'),
|
||||
('(', 'LPARENS'),
|
||||
(')', 'RPARENS'),
|
||||
])
|
||||
|
||||
#Make a function to silence pychecker "Local variable shadows global"
|
||||
def make_partial_ops():
|
||||
partials = {}
|
||||
for k in _OPERATORS:
|
||||
for i in range(1, len(k)+1):
|
||||
partials[k[:i]] = None
|
||||
return partials
|
||||
|
||||
_PARTIAL_OPERATORS = make_partial_ops()
|
||||
|
||||
def is_partial_op(s):
|
||||
"""Return True if s matches a non-empty subpart of an operator starting
|
||||
at its first character.
|
||||
"""
|
||||
return s in _PARTIAL_OPERATORS
|
||||
|
||||
def is_op(s):
|
||||
"""If s matches an operator, returns the operator identifier. Return None
|
||||
otherwise.
|
||||
"""
|
||||
return _OPERATORS.get(s)
|
||||
|
||||
_RESERVEDS = dict([
|
||||
('if', 'If'),
|
||||
('then', 'Then'),
|
||||
('else', 'Else'),
|
||||
('elif', 'Elif'),
|
||||
('fi', 'Fi'),
|
||||
('do', 'Do'),
|
||||
('done', 'Done'),
|
||||
('case', 'Case'),
|
||||
('esac', 'Esac'),
|
||||
('while', 'While'),
|
||||
('until', 'Until'),
|
||||
('for', 'For'),
|
||||
('{', 'Lbrace'),
|
||||
('}', 'Rbrace'),
|
||||
('!', 'Bang'),
|
||||
('in', 'In'),
|
||||
('|', 'PIPE'),
|
||||
])
|
||||
|
||||
def get_reserved(s):
|
||||
return _RESERVEDS.get(s)
|
||||
|
||||
_RE_NAME = re.compile(r'^[0-9a-zA-Z_]+$')
|
||||
|
||||
def is_name(s):
|
||||
return _RE_NAME.search(s) is not None
|
||||
|
||||
def find_chars(seq, chars):
|
||||
for i,v in enumerate(seq):
|
||||
if v in chars:
|
||||
return i,v
|
||||
return -1, None
|
||||
|
||||
class WordLexer:
|
||||
"""WordLexer parse quoted or expansion expressions and return an expression
|
||||
tree. The input string can be any well formed sequence beginning with quoting
|
||||
or expansion character. Embedded expressions are handled recursively. The
|
||||
resulting tree is made of lists and strings. Lists represent quoted or
|
||||
expansion expressions. Each list first element is the opening separator,
|
||||
the last one the closing separator. In-between can be any number of strings
|
||||
or lists for sub-expressions. Non quoted/expansion expression can written as
|
||||
strings or as lists with empty strings as starting and ending delimiters.
|
||||
"""
|
||||
|
||||
NAME_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
|
||||
NAME_CHARSET = dict(zip(NAME_CHARSET, NAME_CHARSET))
|
||||
|
||||
SPECIAL_CHARSET = '@*#?-$!0'
|
||||
|
||||
#Characters which can be escaped depends on the current delimiters
|
||||
ESCAPABLE = {
|
||||
'`': set(['$', '\\', '`']),
|
||||
'"': set(['$', '\\', '`', '"']),
|
||||
"'": set(),
|
||||
}
|
||||
|
||||
def __init__(self, heredoc = False):
|
||||
# _buffer is the unprocessed input characters buffer
|
||||
self._buffer = []
|
||||
# _stack is empty or contains a quoted list being processed
|
||||
# (this is the DFS path to the quoted expression being evaluated).
|
||||
self._stack = []
|
||||
self._escapable = None
|
||||
# True when parsing unquoted here documents
|
||||
self._heredoc = heredoc
|
||||
|
||||
def add(self, data, eof=False):
|
||||
"""Feed the lexer with more data. If the quoted expression can be
|
||||
delimited, return a tuple (expr, remaining) containing the expression
|
||||
tree and the unconsumed data.
|
||||
Otherwise, raise NeedMore.
|
||||
"""
|
||||
self._buffer += list(data)
|
||||
self._parse(eof)
|
||||
|
||||
result = self._stack[0]
|
||||
remaining = ''.join(self._buffer)
|
||||
self._stack = []
|
||||
self._buffer = []
|
||||
return result, remaining
|
||||
|
||||
def _is_escapable(self, c, delim=None):
|
||||
if delim is None:
|
||||
if self._heredoc:
|
||||
# Backslashes works as if they were double quoted in unquoted
|
||||
# here-documents
|
||||
delim = '"'
|
||||
else:
|
||||
if len(self._stack)<=1:
|
||||
return True
|
||||
delim = self._stack[-2][0]
|
||||
|
||||
escapables = self.ESCAPABLE.get(delim, None)
|
||||
return escapables is None or c in escapables
|
||||
|
||||
def _parse_squote(self, buf, result, eof):
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
try:
|
||||
pos = buf.index("'")
|
||||
except ValueError:
|
||||
raise NeedMore()
|
||||
result[-1] += ''.join(buf[:pos])
|
||||
result += ["'"]
|
||||
return pos+1, True
|
||||
|
||||
def _parse_bquote(self, buf, result, eof):
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
|
||||
if buf[0]=='\n':
|
||||
#Remove line continuations
|
||||
result[:] = ['', '', '']
|
||||
elif self._is_escapable(buf[0]):
|
||||
result[-1] += buf[0]
|
||||
result += ['']
|
||||
else:
|
||||
#Keep as such
|
||||
result[:] = ['', '\\'+buf[0], '']
|
||||
|
||||
return 1, True
|
||||
|
||||
def _parse_dquote(self, buf, result, eof):
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
pos, sep = find_chars(buf, '$\\`"')
|
||||
if pos==-1:
|
||||
raise NeedMore()
|
||||
|
||||
result[-1] += ''.join(buf[:pos])
|
||||
if sep=='"':
|
||||
result += ['"']
|
||||
return pos+1, True
|
||||
else:
|
||||
#Keep everything until the separator and defer processing
|
||||
return pos, False
|
||||
|
||||
def _parse_command(self, buf, result, eof):
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
|
||||
chars = '$\\`"\''
|
||||
if result[0] == '$(':
|
||||
chars += ')'
|
||||
pos, sep = find_chars(buf, chars)
|
||||
if pos == -1:
|
||||
raise NeedMore()
|
||||
|
||||
result[-1] += ''.join(buf[:pos])
|
||||
if (result[0]=='$(' and sep==')') or (result[0]=='`' and sep=='`'):
|
||||
result += [sep]
|
||||
return pos+1, True
|
||||
else:
|
||||
return pos, False
|
||||
|
||||
def _parse_parameter(self, buf, result, eof):
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
|
||||
pos, sep = find_chars(buf, '$\\`"\'}')
|
||||
if pos==-1:
|
||||
raise NeedMore()
|
||||
|
||||
result[-1] += ''.join(buf[:pos])
|
||||
if sep=='}':
|
||||
result += [sep]
|
||||
return pos+1, True
|
||||
else:
|
||||
return pos, False
|
||||
|
||||
def _parse_dollar(self, buf, result, eof):
|
||||
sep = result[0]
|
||||
if sep=='$':
|
||||
if not buf:
|
||||
#TODO: handle empty $
|
||||
raise NeedMore()
|
||||
if buf[0]=='(':
|
||||
if len(buf)==1:
|
||||
raise NeedMore()
|
||||
|
||||
if buf[1]=='(':
|
||||
result[0] = '$(('
|
||||
buf[:2] = []
|
||||
else:
|
||||
result[0] = '$('
|
||||
buf[:1] = []
|
||||
|
||||
elif buf[0]=='{':
|
||||
result[0] = '${'
|
||||
buf[:1] = []
|
||||
else:
|
||||
if buf[0] in self.SPECIAL_CHARSET:
|
||||
result[-1] = buf[0]
|
||||
read = 1
|
||||
else:
|
||||
for read,c in enumerate(buf):
|
||||
if c not in self.NAME_CHARSET:
|
||||
break
|
||||
else:
|
||||
if not eof:
|
||||
raise NeedMore()
|
||||
read += 1
|
||||
|
||||
result[-1] += ''.join(buf[0:read])
|
||||
|
||||
if not result[-1]:
|
||||
result[:] = ['', result[0], '']
|
||||
else:
|
||||
result += ['']
|
||||
return read,True
|
||||
|
||||
sep = result[0]
|
||||
if sep=='$(':
|
||||
parsefunc = self._parse_command
|
||||
elif sep=='${':
|
||||
parsefunc = self._parse_parameter
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
pos, closed = parsefunc(buf, result, eof)
|
||||
return pos, closed
|
||||
|
||||
def _parse(self, eof):
|
||||
buf = self._buffer
|
||||
stack = self._stack
|
||||
recurse = False
|
||||
|
||||
while 1:
|
||||
if not stack or recurse:
|
||||
if not buf:
|
||||
raise NeedMore()
|
||||
if buf[0] not in ('"\\`$\''):
|
||||
raise ShellSyntaxError('Invalid quoted string sequence')
|
||||
stack.append([buf[0], ''])
|
||||
buf[:1] = []
|
||||
recurse = False
|
||||
|
||||
result = stack[-1]
|
||||
if result[0]=="'":
|
||||
parsefunc = self._parse_squote
|
||||
elif result[0]=='\\':
|
||||
parsefunc = self._parse_bquote
|
||||
elif result[0]=='"':
|
||||
parsefunc = self._parse_dquote
|
||||
elif result[0]=='`':
|
||||
parsefunc = self._parse_command
|
||||
elif result[0][0]=='$':
|
||||
parsefunc = self._parse_dollar
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
read, closed = parsefunc(buf, result, eof)
|
||||
|
||||
buf[:read] = []
|
||||
if closed:
|
||||
if len(stack)>1:
|
||||
#Merge in parent expression
|
||||
parsed = stack.pop()
|
||||
stack[-1] += [parsed]
|
||||
stack[-1] += ['']
|
||||
else:
|
||||
break
|
||||
else:
|
||||
recurse = True
|
||||
|
||||
def normalize_wordtree(wtree):
|
||||
"""Fold back every literal sequence (delimited with empty strings) into
|
||||
parent sequence.
|
||||
"""
|
||||
def normalize(wtree):
|
||||
result = []
|
||||
for part in wtree[1:-1]:
|
||||
if isinstance(part, list):
|
||||
part = normalize(part)
|
||||
if part[0]=='':
|
||||
#Move the part content back at current level
|
||||
result += part[1:-1]
|
||||
continue
|
||||
elif not part:
|
||||
#Remove empty strings
|
||||
continue
|
||||
result.append(part)
|
||||
if not result:
|
||||
result = ['']
|
||||
return [wtree[0]] + result + [wtree[-1]]
|
||||
|
||||
return normalize(wtree)
|
||||
|
||||
|
||||
def make_wordtree(token, here_document=False):
|
||||
"""Parse a delimited token and return a tree similar to the ones returned by
|
||||
WordLexer. token may contain any combinations of expansion/quoted fields and
|
||||
non-ones.
|
||||
"""
|
||||
tree = ['']
|
||||
remaining = token
|
||||
delimiters = '\\$`'
|
||||
if not here_document:
|
||||
delimiters += '\'"'
|
||||
|
||||
while 1:
|
||||
pos, sep = find_chars(remaining, delimiters)
|
||||
if pos==-1:
|
||||
tree += [remaining, '']
|
||||
return normalize_wordtree(tree)
|
||||
tree.append(remaining[:pos])
|
||||
remaining = remaining[pos:]
|
||||
|
||||
try:
|
||||
result, remaining = WordLexer(heredoc = here_document).add(remaining, True)
|
||||
except NeedMore:
|
||||
raise ShellSyntaxError('Invalid token "%s"')
|
||||
tree.append(result)
|
||||
|
||||
|
||||
def wordtree_as_string(wtree):
|
||||
"""Rewrite an expression tree generated by make_wordtree as string."""
|
||||
def visit(node, output):
|
||||
for child in node:
|
||||
if isinstance(child, list):
|
||||
visit(child, output)
|
||||
else:
|
||||
output.append(child)
|
||||
|
||||
output = []
|
||||
visit(wtree, output)
|
||||
return ''.join(output)
|
||||
|
||||
|
||||
def unquote_wordtree(wtree):
|
||||
"""Fold the word tree while removing quotes everywhere. Other expansion
|
||||
sequences are joined as such.
|
||||
"""
|
||||
def unquote(wtree):
|
||||
unquoted = []
|
||||
if wtree[0] in ('', "'", '"', '\\'):
|
||||
wtree = wtree[1:-1]
|
||||
|
||||
for part in wtree:
|
||||
if isinstance(part, list):
|
||||
part = unquote(part)
|
||||
unquoted.append(part)
|
||||
return ''.join(unquoted)
|
||||
|
||||
return unquote(wtree)
|
||||
|
||||
|
||||
class HereDocLexer:
|
||||
"""HereDocLexer delimits whatever comes from the here-document starting newline
|
||||
not included to the closing delimiter line included.
|
||||
"""
|
||||
def __init__(self, op, delim):
|
||||
assert op in ('<<', '<<-')
|
||||
if not delim:
|
||||
raise ShellSyntaxError('invalid here document delimiter %s' % str(delim))
|
||||
|
||||
self._op = op
|
||||
self._delim = delim
|
||||
self._buffer = []
|
||||
self._token = []
|
||||
|
||||
def add(self, data, eof):
|
||||
"""If the here-document was delimited, return a tuple (content, remaining).
|
||||
Raise NeedMore() otherwise.
|
||||
"""
|
||||
self._buffer += list(data)
|
||||
self._parse(eof)
|
||||
token = ''.join(self._token)
|
||||
remaining = ''.join(self._buffer)
|
||||
self._token, self._remaining = [], []
|
||||
return token, remaining
|
||||
|
||||
def _parse(self, eof):
|
||||
while 1:
|
||||
#Look for first unescaped newline. Quotes may be ignored
|
||||
escaped = False
|
||||
for i,c in enumerate(self._buffer):
|
||||
if escaped:
|
||||
escaped = False
|
||||
elif c=='\\':
|
||||
escaped = True
|
||||
elif c=='\n':
|
||||
break
|
||||
else:
|
||||
i = -1
|
||||
|
||||
if i==-1 or self._buffer[i]!='\n':
|
||||
if not eof:
|
||||
raise NeedMore()
|
||||
#No more data, maybe the last line is closing delimiter
|
||||
line = ''.join(self._buffer)
|
||||
eol = ''
|
||||
self._buffer[:] = []
|
||||
else:
|
||||
line = ''.join(self._buffer[:i])
|
||||
eol = self._buffer[i]
|
||||
self._buffer[:i+1] = []
|
||||
|
||||
if self._op=='<<-':
|
||||
line = line.lstrip('\t')
|
||||
|
||||
if line==self._delim:
|
||||
break
|
||||
|
||||
self._token += [line, eol]
|
||||
if i==-1:
|
||||
break
|
||||
|
||||
class Token:
|
||||
#TODO: check this is still in use
|
||||
OPERATOR = 'OPERATOR'
|
||||
WORD = 'WORD'
|
||||
|
||||
def __init__(self):
|
||||
self.value = ''
|
||||
self.type = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
#Behave like a two elements tuple
|
||||
if key==0:
|
||||
return self.type
|
||||
if key==1:
|
||||
return self.value
|
||||
raise IndexError(key)
|
||||
|
||||
|
||||
class HereDoc:
|
||||
def __init__(self, op, name=None):
|
||||
self.op = op
|
||||
self.name = name
|
||||
self.pendings = []
|
||||
|
||||
TK_COMMA = 'COMMA'
|
||||
TK_AMPERSAND = 'AMP'
|
||||
TK_OP = 'OP'
|
||||
TK_TOKEN = 'TOKEN'
|
||||
TK_COMMENT = 'COMMENT'
|
||||
TK_NEWLINE = 'NEWLINE'
|
||||
TK_IONUMBER = 'IO_NUMBER'
|
||||
TK_ASSIGNMENT = 'ASSIGNMENT_WORD'
|
||||
TK_HERENAME = 'HERENAME'
|
||||
|
||||
class Lexer:
|
||||
"""Main lexer.
|
||||
|
||||
Call add() until the script AST is returned.
|
||||
"""
|
||||
# Here-document handling makes the whole thing more complex because they basically
|
||||
# force tokens to be reordered: here-content must come right after the operator
|
||||
# and the here-document name, while some other tokens might be following the
|
||||
# here-document expression on the same line.
|
||||
#
|
||||
# So, here-doc states are basically:
|
||||
# *self._state==ST_NORMAL
|
||||
# - self._heredoc.op is None: no here-document
|
||||
# - self._heredoc.op is not None but name is: here-document operator matched,
|
||||
# waiting for the document name/delimiter
|
||||
# - self._heredoc.op and name are not None: here-document is ready, following
|
||||
# tokens are being stored and will be pushed again when the document is
|
||||
# completely parsed.
|
||||
# *self._state==ST_HEREDOC
|
||||
# - The here-document is being delimited by self._herelexer. Once it is done
|
||||
# the content is pushed in front of the pending token list then all these
|
||||
# tokens are pushed once again.
|
||||
ST_NORMAL = 'ST_NORMAL'
|
||||
ST_OP = 'ST_OP'
|
||||
ST_BACKSLASH = 'ST_BACKSLASH'
|
||||
ST_QUOTED = 'ST_QUOTED'
|
||||
ST_COMMENT = 'ST_COMMENT'
|
||||
ST_HEREDOC = 'ST_HEREDOC'
|
||||
|
||||
#Match end of backquote strings
|
||||
RE_BACKQUOTE_END = re.compile(r'(?<!\\)(`)')
|
||||
|
||||
def __init__(self, parent_state = None):
|
||||
self._input = []
|
||||
self._pos = 0
|
||||
|
||||
self._token = ''
|
||||
self._type = TK_TOKEN
|
||||
|
||||
self._state = self.ST_NORMAL
|
||||
self._parent_state = parent_state
|
||||
self._wordlexer = None
|
||||
|
||||
self._heredoc = HereDoc(None)
|
||||
self._herelexer = None
|
||||
|
||||
### Following attributes are not used for delimiting token and can safely
|
||||
### be changed after here-document detection (see _push_toke)
|
||||
|
||||
# Count the number of tokens following a 'For' reserved word. Needed to
|
||||
# return an 'In' reserved word if it comes in third place.
|
||||
self._for_count = None
|
||||
|
||||
def add(self, data, eof=False):
|
||||
"""Feed the lexer with data.
|
||||
|
||||
When eof is set to True, returns unconsumed data or raise if the lexer
|
||||
is in the middle of a delimiting operation.
|
||||
Raise NeedMore otherwise.
|
||||
"""
|
||||
self._input += list(data)
|
||||
self._parse(eof)
|
||||
self._input[:self._pos] = []
|
||||
return ''.join(self._input)
|
||||
|
||||
def _parse(self, eof):
|
||||
while self._state:
|
||||
if self._pos>=len(self._input):
|
||||
if not eof:
|
||||
raise NeedMore()
|
||||
elif self._state not in (self.ST_OP, self.ST_QUOTED, self.ST_HEREDOC):
|
||||
#Delimit the current token and leave cleanly
|
||||
self._push_token('')
|
||||
break
|
||||
else:
|
||||
#Let the sublexer handle the eof themselves
|
||||
pass
|
||||
|
||||
if self._state==self.ST_NORMAL:
|
||||
self._parse_normal()
|
||||
elif self._state==self.ST_COMMENT:
|
||||
self._parse_comment()
|
||||
elif self._state==self.ST_OP:
|
||||
self._parse_op(eof)
|
||||
elif self._state==self.ST_QUOTED:
|
||||
self._parse_quoted(eof)
|
||||
elif self._state==self.ST_HEREDOC:
|
||||
self._parse_heredoc(eof)
|
||||
else:
|
||||
assert False, "Unknown state " + str(self._state)
|
||||
|
||||
if self._heredoc.op is not None:
|
||||
raise ShellSyntaxError('missing here-document delimiter')
|
||||
|
||||
def _parse_normal(self):
|
||||
c = self._input[self._pos]
|
||||
if c=='\n':
|
||||
self._push_token(c)
|
||||
self._token = c
|
||||
self._type = TK_NEWLINE
|
||||
self._push_token('')
|
||||
self._pos += 1
|
||||
elif c in ('\\', '\'', '"', '`', '$'):
|
||||
self._state = self.ST_QUOTED
|
||||
elif is_partial_op(c):
|
||||
self._push_token(c)
|
||||
|
||||
self._type = TK_OP
|
||||
self._token += c
|
||||
self._pos += 1
|
||||
self._state = self.ST_OP
|
||||
elif is_blank(c):
|
||||
self._push_token(c)
|
||||
|
||||
#Discard blanks
|
||||
self._pos += 1
|
||||
elif self._token:
|
||||
self._token += c
|
||||
self._pos += 1
|
||||
elif c=='#':
|
||||
self._state = self.ST_COMMENT
|
||||
self._type = TK_COMMENT
|
||||
self._pos += 1
|
||||
else:
|
||||
self._pos += 1
|
||||
self._token += c
|
||||
|
||||
def _parse_op(self, eof):
|
||||
assert self._token
|
||||
|
||||
while 1:
|
||||
if self._pos>=len(self._input):
|
||||
if not eof:
|
||||
raise NeedMore()
|
||||
c = ''
|
||||
else:
|
||||
c = self._input[self._pos]
|
||||
|
||||
op = self._token + c
|
||||
if c and is_partial_op(op):
|
||||
#Still parsing an operator
|
||||
self._token = op
|
||||
self._pos += 1
|
||||
else:
|
||||
#End of operator
|
||||
self._push_token(c)
|
||||
self._state = self.ST_NORMAL
|
||||
break
|
||||
|
||||
def _parse_comment(self):
|
||||
while 1:
|
||||
if self._pos>=len(self._input):
|
||||
raise NeedMore()
|
||||
|
||||
c = self._input[self._pos]
|
||||
if c=='\n':
|
||||
#End of comment, do not consume the end of line
|
||||
self._state = self.ST_NORMAL
|
||||
break
|
||||
else:
|
||||
self._token += c
|
||||
self._pos += 1
|
||||
|
||||
def _parse_quoted(self, eof):
|
||||
"""Precondition: the starting backquote/dollar is still in the input queue."""
|
||||
if not self._wordlexer:
|
||||
self._wordlexer = WordLexer()
|
||||
|
||||
if self._pos<len(self._input):
|
||||
#Transfer input queue character into the subparser
|
||||
input = self._input[self._pos:]
|
||||
self._pos += len(input)
|
||||
|
||||
wtree, remaining = self._wordlexer.add(input, eof)
|
||||
self._wordlexer = None
|
||||
self._token += wordtree_as_string(wtree)
|
||||
|
||||
#Put unparsed character back in the input queue
|
||||
if remaining:
|
||||
self._input[self._pos:self._pos] = list(remaining)
|
||||
self._state = self.ST_NORMAL
|
||||
|
||||
def _parse_heredoc(self, eof):
|
||||
assert not self._token
|
||||
|
||||
if self._herelexer is None:
|
||||
self._herelexer = HereDocLexer(self._heredoc.op, self._heredoc.name)
|
||||
|
||||
if self._pos<len(self._input):
|
||||
#Transfer input queue character into the subparser
|
||||
input = self._input[self._pos:]
|
||||
self._pos += len(input)
|
||||
|
||||
self._token, remaining = self._herelexer.add(input, eof)
|
||||
|
||||
#Reset here-document state
|
||||
self._herelexer = None
|
||||
heredoc, self._heredoc = self._heredoc, HereDoc(None)
|
||||
if remaining:
|
||||
self._input[self._pos:self._pos] = list(remaining)
|
||||
self._state = self.ST_NORMAL
|
||||
|
||||
#Push pending tokens
|
||||
heredoc.pendings[:0] = [(self._token, self._type, heredoc.name)]
|
||||
for token, type, delim in heredoc.pendings:
|
||||
self._token = token
|
||||
self._type = type
|
||||
self._push_token(delim)
|
||||
|
||||
def _push_token(self, delim):
|
||||
if not self._token:
|
||||
return 0
|
||||
|
||||
if self._heredoc.op is not None:
|
||||
if self._heredoc.name is None:
|
||||
#Here-document name
|
||||
if self._type!=TK_TOKEN:
|
||||
raise ShellSyntaxError("expecting here-document name, got '%s'" % self._token)
|
||||
self._heredoc.name = unquote_wordtree(make_wordtree(self._token))
|
||||
self._type = TK_HERENAME
|
||||
else:
|
||||
#Capture all tokens until the newline starting the here-document
|
||||
if self._type==TK_NEWLINE:
|
||||
assert self._state==self.ST_NORMAL
|
||||
self._state = self.ST_HEREDOC
|
||||
|
||||
self._heredoc.pendings.append((self._token, self._type, delim))
|
||||
self._token = ''
|
||||
self._type = TK_TOKEN
|
||||
return 1
|
||||
|
||||
# BEWARE: do not change parser state from here to the end of the function:
|
||||
# when parsing between an here-document operator to the end of the line
|
||||
# tokens are stored in self._heredoc.pendings. Therefore, they will not
|
||||
# reach the section below.
|
||||
|
||||
#Check operators
|
||||
if self._type==TK_OP:
|
||||
#False positive because of partial op matching
|
||||
op = is_op(self._token)
|
||||
if not op:
|
||||
self._type = TK_TOKEN
|
||||
else:
|
||||
#Map to the specific operator
|
||||
self._type = op
|
||||
if self._token in ('<<', '<<-'):
|
||||
#Done here rather than in _parse_op because there is no need
|
||||
#to change the parser state since we are still waiting for
|
||||
#the here-document name
|
||||
if self._heredoc.op is not None:
|
||||
raise ShellSyntaxError("syntax error near token '%s'" % self._token)
|
||||
assert self._heredoc.op is None
|
||||
self._heredoc.op = self._token
|
||||
|
||||
if self._type==TK_TOKEN:
|
||||
if '=' in self._token and not delim:
|
||||
if self._token.startswith('='):
|
||||
#Token is a WORD... a TOKEN that is.
|
||||
pass
|
||||
else:
|
||||
prev = self._token[:self._token.find('=')]
|
||||
if is_name(prev):
|
||||
self._type = TK_ASSIGNMENT
|
||||
else:
|
||||
#Just a token (unspecified)
|
||||
pass
|
||||
else:
|
||||
reserved = get_reserved(self._token)
|
||||
if reserved is not None:
|
||||
if reserved=='In' and self._for_count!=2:
|
||||
#Sorry, not a reserved word after all
|
||||
pass
|
||||
else:
|
||||
self._type = reserved
|
||||
if reserved in ('For', 'Case'):
|
||||
self._for_count = 0
|
||||
elif are_digits(self._token) and delim in ('<', '>'):
|
||||
#Detect IO_NUMBER
|
||||
self._type = TK_IONUMBER
|
||||
elif self._token==';':
|
||||
self._type = TK_COMMA
|
||||
elif self._token=='&':
|
||||
self._type = TK_AMPERSAND
|
||||
elif self._type==TK_COMMENT:
|
||||
#Comments are not part of sh grammar, ignore them
|
||||
self._token = ''
|
||||
self._type = TK_TOKEN
|
||||
return 0
|
||||
|
||||
if self._for_count is not None:
|
||||
#Track token count in 'For' expression to detect 'In' reserved words.
|
||||
#Can only be in third position, no need to go beyond
|
||||
self._for_count += 1
|
||||
if self._for_count==3:
|
||||
self._for_count = None
|
||||
|
||||
self.on_token((self._token, self._type))
|
||||
self._token = ''
|
||||
self._type = TK_TOKEN
|
||||
return 1
|
||||
|
||||
def on_token(self, token):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
tokens = [
|
||||
TK_TOKEN,
|
||||
# To silence yacc unused token warnings
|
||||
# TK_COMMENT,
|
||||
TK_NEWLINE,
|
||||
TK_IONUMBER,
|
||||
TK_ASSIGNMENT,
|
||||
TK_HERENAME,
|
||||
]
|
||||
|
||||
#Add specific operators
|
||||
tokens += _OPERATORS.values()
|
||||
#Add reserved words
|
||||
tokens += _RESERVEDS.values()
|
||||
|
||||
class PLYLexer(Lexer):
|
||||
"""Bridge Lexer and PLY lexer interface."""
|
||||
def __init__(self):
|
||||
Lexer.__init__(self)
|
||||
self._tokens = []
|
||||
self._current = 0
|
||||
self.lineno = 0
|
||||
|
||||
def on_token(self, token):
|
||||
value, type = token
|
||||
|
||||
self.lineno = 0
|
||||
t = lex.LexToken()
|
||||
t.value = value
|
||||
t.type = type
|
||||
t.lexer = self
|
||||
t.lexpos = 0
|
||||
t.lineno = 0
|
||||
|
||||
self._tokens.append(t)
|
||||
|
||||
def is_empty(self):
|
||||
return not bool(self._tokens)
|
||||
|
||||
#PLY compliant interface
|
||||
def token(self):
|
||||
if self._current>=len(self._tokens):
|
||||
return None
|
||||
t = self._tokens[self._current]
|
||||
self._current += 1
|
||||
return t
|
||||
|
||||
|
||||
def get_tokens(s):
|
||||
"""Parse the input string and return a tuple (tokens, unprocessed) where
|
||||
tokens is a list of parsed tokens and unprocessed is the part of the input
|
||||
string left untouched by the lexer.
|
||||
"""
|
||||
lexer = PLYLexer()
|
||||
untouched = lexer.add(s, True)
|
||||
tokens = []
|
||||
while 1:
|
||||
token = lexer.token()
|
||||
if token is None:
|
||||
break
|
||||
tokens.append(token)
|
||||
|
||||
tokens = [(t.value, t.type) for t in tokens]
|
||||
return tokens, untouched
|
||||
772
bitbake/lib/pysh/pyshyacc.py
Normal file
772
bitbake/lib/pysh/pyshyacc.py
Normal file
@@ -0,0 +1,772 @@
|
||||
# pyshyacc.py - PLY grammar definition for pysh
|
||||
#
|
||||
# Copyright 2007 Patrick Mezard
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
"""PLY grammar file.
|
||||
"""
|
||||
import sys
|
||||
|
||||
import pyshlex
|
||||
tokens = pyshlex.tokens
|
||||
|
||||
from ply import yacc
|
||||
import sherrors
|
||||
|
||||
class IORedirect:
|
||||
def __init__(self, op, filename, io_number=None):
|
||||
self.op = op
|
||||
self.filename = filename
|
||||
self.io_number = io_number
|
||||
|
||||
class HereDocument:
|
||||
def __init__(self, op, name, content, io_number=None):
|
||||
self.op = op
|
||||
self.name = name
|
||||
self.content = content
|
||||
self.io_number = io_number
|
||||
|
||||
def make_io_redirect(p):
|
||||
"""Make an IORedirect instance from the input 'io_redirect' production."""
|
||||
name, io_number, io_target = p
|
||||
assert name=='io_redirect'
|
||||
|
||||
if io_target[0]=='io_file':
|
||||
io_type, io_op, io_file = io_target
|
||||
return IORedirect(io_op, io_file, io_number)
|
||||
elif io_target[0]=='io_here':
|
||||
io_type, io_op, io_name, io_content = io_target
|
||||
return HereDocument(io_op, io_name, io_content, io_number)
|
||||
else:
|
||||
assert False, "Invalid IO redirection token %s" % repr(io_type)
|
||||
|
||||
class SimpleCommand:
|
||||
"""
|
||||
assigns contains (name, value) pairs.
|
||||
"""
|
||||
def __init__(self, words, redirs, assigns):
|
||||
self.words = list(words)
|
||||
self.redirs = list(redirs)
|
||||
self.assigns = list(assigns)
|
||||
|
||||
class Pipeline:
|
||||
def __init__(self, commands, reverse_status=False):
|
||||
self.commands = list(commands)
|
||||
assert self.commands #Grammar forbids this
|
||||
self.reverse_status = reverse_status
|
||||
|
||||
class AndOr:
|
||||
def __init__(self, op, left, right):
|
||||
self.op = str(op)
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
class ForLoop:
|
||||
def __init__(self, name, items, cmds):
|
||||
self.name = str(name)
|
||||
self.items = list(items)
|
||||
self.cmds = list(cmds)
|
||||
|
||||
class WhileLoop:
|
||||
def __init__(self, condition, cmds):
|
||||
self.condition = list(condition)
|
||||
self.cmds = list(cmds)
|
||||
|
||||
class UntilLoop:
|
||||
def __init__(self, condition, cmds):
|
||||
self.condition = list(condition)
|
||||
self.cmds = list(cmds)
|
||||
|
||||
class FunDef:
|
||||
def __init__(self, name, body):
|
||||
self.name = str(name)
|
||||
self.body = body
|
||||
|
||||
class BraceGroup:
|
||||
def __init__(self, cmds):
|
||||
self.cmds = list(cmds)
|
||||
|
||||
class IfCond:
|
||||
def __init__(self, cond, if_cmds, else_cmds):
|
||||
self.cond = list(cond)
|
||||
self.if_cmds = if_cmds
|
||||
self.else_cmds = else_cmds
|
||||
|
||||
class Case:
|
||||
def __init__(self, name, items):
|
||||
self.name = name
|
||||
self.items = items
|
||||
|
||||
class SubShell:
|
||||
def __init__(self, cmds):
|
||||
self.cmds = cmds
|
||||
|
||||
class RedirectList:
|
||||
def __init__(self, cmd, redirs):
|
||||
self.cmd = cmd
|
||||
self.redirs = list(redirs)
|
||||
|
||||
def get_production(productions, ptype):
|
||||
"""productions must be a list of production tuples like (name, obj) where
|
||||
name is the production string identifier.
|
||||
Return the first production named 'ptype'. Raise KeyError if None can be
|
||||
found.
|
||||
"""
|
||||
for production in productions:
|
||||
if production is not None and production[0]==ptype:
|
||||
return production
|
||||
raise KeyError(ptype)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# PLY grammar definition
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def p_multiple_commands(p):
|
||||
"""multiple_commands : newline_sequence
|
||||
| complete_command
|
||||
| multiple_commands complete_command"""
|
||||
if len(p)==2:
|
||||
if p[1] is not None:
|
||||
p[0] = [p[1]]
|
||||
else:
|
||||
p[0] = []
|
||||
else:
|
||||
p[0] = p[1] + [p[2]]
|
||||
|
||||
def p_complete_command(p):
|
||||
"""complete_command : list separator
|
||||
| list"""
|
||||
if len(p)==3 and p[2] and p[2][1] == '&':
|
||||
p[0] = ('async', p[1])
|
||||
else:
|
||||
p[0] = p[1]
|
||||
|
||||
def p_list(p):
|
||||
"""list : list separator_op and_or
|
||||
| and_or"""
|
||||
if len(p)==2:
|
||||
p[0] = [p[1]]
|
||||
else:
|
||||
#if p[2]!=';':
|
||||
# raise NotImplementedError('AND-OR list asynchronous execution is not implemented')
|
||||
p[0] = p[1] + [p[3]]
|
||||
|
||||
def p_and_or(p):
|
||||
"""and_or : pipeline
|
||||
| and_or AND_IF linebreak pipeline
|
||||
| and_or OR_IF linebreak pipeline"""
|
||||
if len(p)==2:
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[0] = ('and_or', AndOr(p[2], p[1], p[4]))
|
||||
|
||||
def p_maybe_bang_word(p):
|
||||
"""maybe_bang_word : Bang"""
|
||||
p[0] = ('maybe_bang_word', p[1])
|
||||
|
||||
def p_pipeline(p):
|
||||
"""pipeline : pipe_sequence
|
||||
| bang_word pipe_sequence"""
|
||||
if len(p)==3:
|
||||
p[0] = ('pipeline', Pipeline(p[2][1:], True))
|
||||
else:
|
||||
p[0] = ('pipeline', Pipeline(p[1][1:]))
|
||||
|
||||
def p_pipe_sequence(p):
|
||||
"""pipe_sequence : command
|
||||
| pipe_sequence PIPE linebreak command"""
|
||||
if len(p)==2:
|
||||
p[0] = ['pipe_sequence', p[1]]
|
||||
else:
|
||||
p[0] = p[1] + [p[4]]
|
||||
|
||||
def p_command(p):
|
||||
"""command : simple_command
|
||||
| compound_command
|
||||
| compound_command redirect_list
|
||||
| function_definition"""
|
||||
|
||||
if p[1][0] in ( 'simple_command',
|
||||
'for_clause',
|
||||
'while_clause',
|
||||
'until_clause',
|
||||
'case_clause',
|
||||
'if_clause',
|
||||
'function_definition',
|
||||
'subshell',
|
||||
'brace_group',):
|
||||
if len(p) == 2:
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[0] = ('redirect_list', RedirectList(p[1], p[2][1:]))
|
||||
else:
|
||||
raise NotImplementedError('%s command is not implemented' % repr(p[1][0]))
|
||||
|
||||
def p_compound_command(p):
|
||||
"""compound_command : brace_group
|
||||
| subshell
|
||||
| for_clause
|
||||
| case_clause
|
||||
| if_clause
|
||||
| while_clause
|
||||
| until_clause"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_subshell(p):
|
||||
"""subshell : LPARENS compound_list RPARENS"""
|
||||
p[0] = ('subshell', SubShell(p[2][1:]))
|
||||
|
||||
def p_compound_list(p):
|
||||
"""compound_list : term
|
||||
| newline_list term
|
||||
| term separator
|
||||
| newline_list term separator"""
|
||||
productions = p[1:]
|
||||
try:
|
||||
sep = get_production(productions, 'separator')
|
||||
if sep[1]!=';':
|
||||
raise NotImplementedError()
|
||||
except KeyError:
|
||||
pass
|
||||
term = get_production(productions, 'term')
|
||||
p[0] = ['compound_list'] + term[1:]
|
||||
|
||||
def p_term(p):
|
||||
"""term : term separator and_or
|
||||
| and_or"""
|
||||
if len(p)==2:
|
||||
p[0] = ['term', p[1]]
|
||||
else:
|
||||
if p[2] is not None and p[2][1] == '&':
|
||||
p[0] = ['term', ('async', p[1][1:])] + [p[3]]
|
||||
else:
|
||||
p[0] = p[1] + [p[3]]
|
||||
|
||||
def p_maybe_for_word(p):
|
||||
# Rearrange 'For' priority wrt TOKEN. See p_for_word
|
||||
"""maybe_for_word : For"""
|
||||
p[0] = ('maybe_for_word', p[1])
|
||||
|
||||
def p_for_clause(p):
|
||||
"""for_clause : for_word name linebreak do_group
|
||||
| for_word name linebreak in sequential_sep do_group
|
||||
| for_word name linebreak in wordlist sequential_sep do_group"""
|
||||
productions = p[1:]
|
||||
do_group = get_production(productions, 'do_group')
|
||||
try:
|
||||
items = get_production(productions, 'in')[1:]
|
||||
except KeyError:
|
||||
raise NotImplementedError('"in" omission is not implemented')
|
||||
|
||||
try:
|
||||
items = get_production(productions, 'wordlist')[1:]
|
||||
except KeyError:
|
||||
items = []
|
||||
|
||||
name = p[2]
|
||||
p[0] = ('for_clause', ForLoop(name, items, do_group[1:]))
|
||||
|
||||
def p_name(p):
|
||||
"""name : token""" #Was NAME instead of token
|
||||
p[0] = p[1]
|
||||
|
||||
def p_in(p):
|
||||
"""in : In"""
|
||||
p[0] = ('in', p[1])
|
||||
|
||||
def p_wordlist(p):
|
||||
"""wordlist : wordlist token
|
||||
| token"""
|
||||
if len(p)==2:
|
||||
p[0] = ['wordlist', ('TOKEN', p[1])]
|
||||
else:
|
||||
p[0] = p[1] + [('TOKEN', p[2])]
|
||||
|
||||
def p_case_clause(p):
|
||||
"""case_clause : Case token linebreak in linebreak case_list Esac
|
||||
| Case token linebreak in linebreak case_list_ns Esac
|
||||
| Case token linebreak in linebreak Esac"""
|
||||
if len(p) < 8:
|
||||
items = []
|
||||
else:
|
||||
items = p[6][1:]
|
||||
name = p[2]
|
||||
p[0] = ('case_clause', Case(name, [c[1] for c in items]))
|
||||
|
||||
def p_case_list_ns(p):
|
||||
"""case_list_ns : case_list case_item_ns
|
||||
| case_item_ns"""
|
||||
p_case_list(p)
|
||||
|
||||
def p_case_list(p):
|
||||
"""case_list : case_list case_item
|
||||
| case_item"""
|
||||
if len(p)==2:
|
||||
p[0] = ['case_list', p[1]]
|
||||
else:
|
||||
p[0] = p[1] + [p[2]]
|
||||
|
||||
def p_case_item_ns(p):
|
||||
"""case_item_ns : pattern RPARENS linebreak
|
||||
| pattern RPARENS compound_list linebreak
|
||||
| LPARENS pattern RPARENS linebreak
|
||||
| LPARENS pattern RPARENS compound_list linebreak"""
|
||||
p_case_item(p)
|
||||
|
||||
def p_case_item(p):
|
||||
"""case_item : pattern RPARENS linebreak DSEMI linebreak
|
||||
| pattern RPARENS compound_list DSEMI linebreak
|
||||
| LPARENS pattern RPARENS linebreak DSEMI linebreak
|
||||
| LPARENS pattern RPARENS compound_list DSEMI linebreak"""
|
||||
if len(p) < 7:
|
||||
name = p[1][1:]
|
||||
else:
|
||||
name = p[2][1:]
|
||||
|
||||
try:
|
||||
cmds = get_production(p[1:], "compound_list")[1:]
|
||||
except KeyError:
|
||||
cmds = []
|
||||
|
||||
p[0] = ('case_item', (name, cmds))
|
||||
|
||||
def p_pattern(p):
|
||||
"""pattern : token
|
||||
| pattern PIPE token"""
|
||||
if len(p)==2:
|
||||
p[0] = ['pattern', ('TOKEN', p[1])]
|
||||
else:
|
||||
p[0] = p[1] + [('TOKEN', p[2])]
|
||||
|
||||
def p_maybe_if_word(p):
|
||||
# Rearrange 'If' priority wrt TOKEN. See p_if_word
|
||||
"""maybe_if_word : If"""
|
||||
p[0] = ('maybe_if_word', p[1])
|
||||
|
||||
def p_maybe_then_word(p):
|
||||
# Rearrange 'Then' priority wrt TOKEN. See p_then_word
|
||||
"""maybe_then_word : Then"""
|
||||
p[0] = ('maybe_then_word', p[1])
|
||||
|
||||
def p_if_clause(p):
|
||||
"""if_clause : if_word compound_list then_word compound_list else_part Fi
|
||||
| if_word compound_list then_word compound_list Fi"""
|
||||
else_part = []
|
||||
if len(p)==7:
|
||||
else_part = p[5]
|
||||
p[0] = ('if_clause', IfCond(p[2][1:], p[4][1:], else_part))
|
||||
|
||||
def p_else_part(p):
|
||||
"""else_part : Elif compound_list then_word compound_list else_part
|
||||
| Elif compound_list then_word compound_list
|
||||
| Else compound_list"""
|
||||
if len(p)==3:
|
||||
p[0] = p[2][1:]
|
||||
else:
|
||||
else_part = []
|
||||
if len(p)==6:
|
||||
else_part = p[5]
|
||||
p[0] = ('elif', IfCond(p[2][1:], p[4][1:], else_part))
|
||||
|
||||
def p_while_clause(p):
|
||||
"""while_clause : While compound_list do_group"""
|
||||
p[0] = ('while_clause', WhileLoop(p[2][1:], p[3][1:]))
|
||||
|
||||
def p_maybe_until_word(p):
|
||||
# Rearrange 'Until' priority wrt TOKEN. See p_until_word
|
||||
"""maybe_until_word : Until"""
|
||||
p[0] = ('maybe_until_word', p[1])
|
||||
|
||||
def p_until_clause(p):
|
||||
"""until_clause : until_word compound_list do_group"""
|
||||
p[0] = ('until_clause', UntilLoop(p[2][1:], p[3][1:]))
|
||||
|
||||
def p_function_definition(p):
|
||||
"""function_definition : fname LPARENS RPARENS linebreak function_body"""
|
||||
p[0] = ('function_definition', FunDef(p[1], p[5]))
|
||||
|
||||
def p_function_body(p):
|
||||
"""function_body : compound_command
|
||||
| compound_command redirect_list"""
|
||||
if len(p)!=2:
|
||||
raise NotImplementedError('functions redirections lists are not implemented')
|
||||
p[0] = p[1]
|
||||
|
||||
def p_fname(p):
|
||||
"""fname : TOKEN""" #Was NAME instead of token
|
||||
p[0] = p[1]
|
||||
|
||||
def p_brace_group(p):
|
||||
"""brace_group : Lbrace compound_list Rbrace"""
|
||||
p[0] = ('brace_group', BraceGroup(p[2][1:]))
|
||||
|
||||
def p_maybe_done_word(p):
|
||||
#See p_assignment_word for details.
|
||||
"""maybe_done_word : Done"""
|
||||
p[0] = ('maybe_done_word', p[1])
|
||||
|
||||
def p_maybe_do_word(p):
|
||||
"""maybe_do_word : Do"""
|
||||
p[0] = ('maybe_do_word', p[1])
|
||||
|
||||
def p_do_group(p):
|
||||
"""do_group : do_word compound_list done_word"""
|
||||
#Do group contains a list of AndOr
|
||||
p[0] = ['do_group'] + p[2][1:]
|
||||
|
||||
def p_simple_command(p):
|
||||
"""simple_command : cmd_prefix cmd_word cmd_suffix
|
||||
| cmd_prefix cmd_word
|
||||
| cmd_prefix
|
||||
| cmd_name cmd_suffix
|
||||
| cmd_name"""
|
||||
words, redirs, assigns = [], [], []
|
||||
for e in p[1:]:
|
||||
name = e[0]
|
||||
if name in ('cmd_prefix', 'cmd_suffix'):
|
||||
for sube in e[1:]:
|
||||
subname = sube[0]
|
||||
if subname=='io_redirect':
|
||||
redirs.append(make_io_redirect(sube))
|
||||
elif subname=='ASSIGNMENT_WORD':
|
||||
assigns.append(sube)
|
||||
else:
|
||||
words.append(sube)
|
||||
elif name in ('cmd_word', 'cmd_name'):
|
||||
words.append(e)
|
||||
|
||||
cmd = SimpleCommand(words, redirs, assigns)
|
||||
p[0] = ('simple_command', cmd)
|
||||
|
||||
def p_cmd_name(p):
|
||||
"""cmd_name : TOKEN"""
|
||||
p[0] = ('cmd_name', p[1])
|
||||
|
||||
def p_cmd_word(p):
|
||||
"""cmd_word : token"""
|
||||
p[0] = ('cmd_word', p[1])
|
||||
|
||||
def p_maybe_assignment_word(p):
|
||||
#See p_assignment_word for details.
|
||||
"""maybe_assignment_word : ASSIGNMENT_WORD"""
|
||||
p[0] = ('maybe_assignment_word', p[1])
|
||||
|
||||
def p_cmd_prefix(p):
|
||||
"""cmd_prefix : io_redirect
|
||||
| cmd_prefix io_redirect
|
||||
| assignment_word
|
||||
| cmd_prefix assignment_word"""
|
||||
try:
|
||||
prefix = get_production(p[1:], 'cmd_prefix')
|
||||
except KeyError:
|
||||
prefix = ['cmd_prefix']
|
||||
|
||||
try:
|
||||
value = get_production(p[1:], 'assignment_word')[1]
|
||||
value = ('ASSIGNMENT_WORD', value.split('=', 1))
|
||||
except KeyError:
|
||||
value = get_production(p[1:], 'io_redirect')
|
||||
p[0] = prefix + [value]
|
||||
|
||||
def p_cmd_suffix(p):
|
||||
"""cmd_suffix : io_redirect
|
||||
| cmd_suffix io_redirect
|
||||
| token
|
||||
| cmd_suffix token
|
||||
| maybe_for_word
|
||||
| cmd_suffix maybe_for_word
|
||||
| maybe_done_word
|
||||
| cmd_suffix maybe_done_word
|
||||
| maybe_do_word
|
||||
| cmd_suffix maybe_do_word
|
||||
| maybe_until_word
|
||||
| cmd_suffix maybe_until_word
|
||||
| maybe_assignment_word
|
||||
| cmd_suffix maybe_assignment_word
|
||||
| maybe_if_word
|
||||
| cmd_suffix maybe_if_word
|
||||
| maybe_then_word
|
||||
| cmd_suffix maybe_then_word
|
||||
| maybe_bang_word
|
||||
| cmd_suffix maybe_bang_word"""
|
||||
try:
|
||||
suffix = get_production(p[1:], 'cmd_suffix')
|
||||
token = p[2]
|
||||
except KeyError:
|
||||
suffix = ['cmd_suffix']
|
||||
token = p[1]
|
||||
|
||||
if isinstance(token, tuple):
|
||||
if token[0]=='io_redirect':
|
||||
p[0] = suffix + [token]
|
||||
else:
|
||||
#Convert maybe_* to TOKEN if necessary
|
||||
p[0] = suffix + [('TOKEN', token[1])]
|
||||
else:
|
||||
p[0] = suffix + [('TOKEN', token)]
|
||||
|
||||
def p_redirect_list(p):
|
||||
"""redirect_list : io_redirect
|
||||
| redirect_list io_redirect"""
|
||||
if len(p) == 2:
|
||||
p[0] = ['redirect_list', make_io_redirect(p[1])]
|
||||
else:
|
||||
p[0] = p[1] + [make_io_redirect(p[2])]
|
||||
|
||||
def p_io_redirect(p):
|
||||
"""io_redirect : io_file
|
||||
| IO_NUMBER io_file
|
||||
| io_here
|
||||
| IO_NUMBER io_here"""
|
||||
if len(p)==3:
|
||||
p[0] = ('io_redirect', p[1], p[2])
|
||||
else:
|
||||
p[0] = ('io_redirect', None, p[1])
|
||||
|
||||
def p_io_file(p):
|
||||
#Return the tuple (operator, filename)
|
||||
"""io_file : LESS filename
|
||||
| LESSAND filename
|
||||
| GREATER filename
|
||||
| GREATAND filename
|
||||
| DGREAT filename
|
||||
| LESSGREAT filename
|
||||
| CLOBBER filename"""
|
||||
#Extract the filename from the file
|
||||
p[0] = ('io_file', p[1], p[2][1])
|
||||
|
||||
def p_filename(p):
|
||||
#Return the filename
|
||||
"""filename : TOKEN"""
|
||||
p[0] = ('filename', p[1])
|
||||
|
||||
def p_io_here(p):
|
||||
"""io_here : DLESS here_end
|
||||
| DLESSDASH here_end"""
|
||||
p[0] = ('io_here', p[1], p[2][1], p[2][2])
|
||||
|
||||
def p_here_end(p):
|
||||
"""here_end : HERENAME TOKEN"""
|
||||
p[0] = ('here_document', p[1], p[2])
|
||||
|
||||
def p_newline_sequence(p):
|
||||
# Nothing in the grammar can handle leading NEWLINE productions, so add
|
||||
# this one with the lowest possible priority relatively to newline_list.
|
||||
"""newline_sequence : newline_list"""
|
||||
p[0] = None
|
||||
|
||||
def p_newline_list(p):
|
||||
"""newline_list : NEWLINE
|
||||
| newline_list NEWLINE"""
|
||||
p[0] = None
|
||||
|
||||
def p_linebreak(p):
|
||||
"""linebreak : newline_list
|
||||
| empty"""
|
||||
p[0] = None
|
||||
|
||||
def p_separator_op(p):
|
||||
"""separator_op : COMMA
|
||||
| AMP"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_separator(p):
|
||||
"""separator : separator_op linebreak
|
||||
| newline_list"""
|
||||
if len(p)==2:
|
||||
#Ignore newlines
|
||||
p[0] = None
|
||||
else:
|
||||
#Keep the separator operator
|
||||
p[0] = ('separator', p[1])
|
||||
|
||||
def p_sequential_sep(p):
|
||||
"""sequential_sep : COMMA linebreak
|
||||
| newline_list"""
|
||||
p[0] = None
|
||||
|
||||
# Low priority TOKEN => for_word conversion.
|
||||
# Let maybe_for_word be used as a token when necessary in higher priority
|
||||
# rules.
|
||||
def p_for_word(p):
|
||||
"""for_word : maybe_for_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_if_word(p):
|
||||
"""if_word : maybe_if_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_then_word(p):
|
||||
"""then_word : maybe_then_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_done_word(p):
|
||||
"""done_word : maybe_done_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_do_word(p):
|
||||
"""do_word : maybe_do_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_until_word(p):
|
||||
"""until_word : maybe_until_word"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_assignment_word(p):
|
||||
"""assignment_word : maybe_assignment_word"""
|
||||
p[0] = ('assignment_word', p[1][1])
|
||||
|
||||
def p_bang_word(p):
|
||||
"""bang_word : maybe_bang_word"""
|
||||
p[0] = ('bang_word', p[1][1])
|
||||
|
||||
def p_token(p):
|
||||
"""token : TOKEN
|
||||
| Fi"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_empty(p):
|
||||
'empty :'
|
||||
p[0] = None
|
||||
|
||||
# Error rule for syntax errors
|
||||
def p_error(p):
|
||||
msg = []
|
||||
w = msg.append
|
||||
w('%r\n' % p)
|
||||
w('followed by:\n')
|
||||
for i in range(5):
|
||||
n = yacc.token()
|
||||
if not n:
|
||||
break
|
||||
w(' %r\n' % n)
|
||||
raise sherrors.ShellSyntaxError(''.join(msg))
|
||||
|
||||
# Build the parser
|
||||
try:
|
||||
import pyshtables
|
||||
except ImportError:
|
||||
yacc.yacc(tabmodule = 'pyshtables')
|
||||
else:
|
||||
yacc.yacc(tabmodule = 'pysh.pyshtables', write_tables = 0, debug = 0)
|
||||
|
||||
|
||||
def parse(input, eof=False, debug=False):
|
||||
"""Parse a whole script at once and return the generated AST and unconsumed
|
||||
data in a tuple.
|
||||
|
||||
NOTE: eof is probably meaningless for now, the parser being unable to work
|
||||
in pull mode. It should be set to True.
|
||||
"""
|
||||
lexer = pyshlex.PLYLexer()
|
||||
remaining = lexer.add(input, eof)
|
||||
if lexer.is_empty():
|
||||
return [], remaining
|
||||
if debug:
|
||||
debug = 2
|
||||
return yacc.parse(lexer=lexer, debug=debug), remaining
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# AST rendering helpers
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def format_commands(v):
|
||||
"""Return a tree made of strings and lists. Make command trees easier to
|
||||
display.
|
||||
"""
|
||||
if isinstance(v, list):
|
||||
return [format_commands(c) for c in v]
|
||||
if isinstance(v, tuple):
|
||||
if len(v)==2 and isinstance(v[0], str) and not isinstance(v[1], str):
|
||||
if v[0] == 'async':
|
||||
return ['AsyncList', map(format_commands, v[1])]
|
||||
else:
|
||||
#Avoid decomposing tuples like ('pipeline', Pipeline(...))
|
||||
return format_commands(v[1])
|
||||
return format_commands(list(v))
|
||||
elif isinstance(v, IfCond):
|
||||
name = ['IfCond']
|
||||
name += ['if', map(format_commands, v.cond)]
|
||||
name += ['then', map(format_commands, v.if_cmds)]
|
||||
name += ['else', map(format_commands, v.else_cmds)]
|
||||
return name
|
||||
elif isinstance(v, ForLoop):
|
||||
name = ['ForLoop']
|
||||
name += [repr(v.name)+' in ', map(str, v.items)]
|
||||
name += ['commands', map(format_commands, v.cmds)]
|
||||
return name
|
||||
elif isinstance(v, AndOr):
|
||||
return [v.op, format_commands(v.left), format_commands(v.right)]
|
||||
elif isinstance(v, Pipeline):
|
||||
name = 'Pipeline'
|
||||
if v.reverse_status:
|
||||
name = '!' + name
|
||||
return [name, format_commands(v.commands)]
|
||||
elif isinstance(v, SimpleCommand):
|
||||
name = ['SimpleCommand']
|
||||
if v.words:
|
||||
name += ['words', map(str, v.words)]
|
||||
if v.assigns:
|
||||
assigns = [tuple(a[1]) for a in v.assigns]
|
||||
name += ['assigns', map(str, assigns)]
|
||||
if v.redirs:
|
||||
name += ['redirs', map(format_commands, v.redirs)]
|
||||
return name
|
||||
elif isinstance(v, RedirectList):
|
||||
name = ['RedirectList']
|
||||
if v.redirs:
|
||||
name += ['redirs', map(format_commands, v.redirs)]
|
||||
name += ['command', format_commands(v.cmd)]
|
||||
return name
|
||||
elif isinstance(v, IORedirect):
|
||||
return ' '.join(map(str, (v.io_number, v.op, v.filename)))
|
||||
elif isinstance(v, HereDocument):
|
||||
return ' '.join(map(str, (v.io_number, v.op, repr(v.name), repr(v.content))))
|
||||
elif isinstance(v, SubShell):
|
||||
return ['SubShell', map(format_commands, v.cmds)]
|
||||
else:
|
||||
return repr(v)
|
||||
|
||||
def print_commands(cmds, output=sys.stdout):
|
||||
"""Pretty print a command tree."""
|
||||
def print_tree(cmd, spaces, output):
|
||||
if isinstance(cmd, list):
|
||||
for c in cmd:
|
||||
print_tree(c, spaces + 3, output)
|
||||
else:
|
||||
print >>output, ' '*spaces + str(cmd)
|
||||
|
||||
formatted = format_commands(cmds)
|
||||
print_tree(formatted, 0, output)
|
||||
|
||||
|
||||
def stringify_commands(cmds):
|
||||
"""Serialize a command tree as a string.
|
||||
|
||||
Returned string is not pretty and is currently used for unit tests only.
|
||||
"""
|
||||
def stringify(value):
|
||||
output = []
|
||||
if isinstance(value, list):
|
||||
formatted = []
|
||||
for v in value:
|
||||
formatted.append(stringify(v))
|
||||
formatted = ' '.join(formatted)
|
||||
output.append(''.join(['<', formatted, '>']))
|
||||
else:
|
||||
output.append(value)
|
||||
return ' '.join(output)
|
||||
|
||||
return stringify(format_commands(cmds))
|
||||
|
||||
|
||||
def visit_commands(cmds, callable):
|
||||
"""Visit the command tree and execute callable on every Pipeline and
|
||||
SimpleCommand instances.
|
||||
"""
|
||||
if isinstance(cmds, (tuple, list)):
|
||||
map(lambda c: visit_commands(c,callable), cmds)
|
||||
elif isinstance(cmds, (Pipeline, SimpleCommand)):
|
||||
callable(cmds)
|
||||
41
bitbake/lib/pysh/sherrors.py
Normal file
41
bitbake/lib/pysh/sherrors.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# sherrors.py - shell errors and signals
|
||||
#
|
||||
# Copyright 2007 Patrick Mezard
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
"""Define shell exceptions and error codes.
|
||||
"""
|
||||
|
||||
class ShellError(Exception):
|
||||
pass
|
||||
|
||||
class ShellSyntaxError(ShellError):
|
||||
pass
|
||||
|
||||
class UtilityError(ShellError):
|
||||
"""Raised upon utility syntax error (option or operand error)."""
|
||||
pass
|
||||
|
||||
class ExpansionError(ShellError):
|
||||
pass
|
||||
|
||||
class CommandNotFound(ShellError):
|
||||
"""Specified command was not found."""
|
||||
pass
|
||||
|
||||
class RedirectionError(ShellError):
|
||||
pass
|
||||
|
||||
class VarAssignmentError(ShellError):
|
||||
"""Variable assignment error."""
|
||||
pass
|
||||
|
||||
class ExitSignal(ShellError):
|
||||
"""Exit signal."""
|
||||
pass
|
||||
|
||||
class ReturnSignal(ShellError):
|
||||
"""Exit signal."""
|
||||
pass
|
||||
77
bitbake/lib/pysh/subprocess_fix.py
Normal file
77
bitbake/lib/pysh/subprocess_fix.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# subprocess - Subprocesses with accessible I/O streams
|
||||
#
|
||||
# For more information about this module, see PEP 324.
|
||||
#
|
||||
# This module should remain compatible with Python 2.2, see PEP 291.
|
||||
#
|
||||
# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
|
||||
#
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# See http://www.python.org/2.4/license for licensing details.
|
||||
|
||||
def list2cmdline(seq):
|
||||
"""
|
||||
Translate a sequence of arguments into a command line
|
||||
string, using the same rules as the MS C runtime:
|
||||
|
||||
1) Arguments are delimited by white space, which is either a
|
||||
space or a tab.
|
||||
|
||||
2) A string surrounded by double quotation marks is
|
||||
interpreted as a single argument, regardless of white space
|
||||
contained within. A quoted string can be embedded in an
|
||||
argument.
|
||||
|
||||
3) A double quotation mark preceded by a backslash is
|
||||
interpreted as a literal double quotation mark.
|
||||
|
||||
4) Backslashes are interpreted literally, unless they
|
||||
immediately precede a double quotation mark.
|
||||
|
||||
5) If backslashes immediately precede a double quotation mark,
|
||||
every pair of backslashes is interpreted as a literal
|
||||
backslash. If the number of backslashes is odd, the last
|
||||
backslash escapes the next double quotation mark as
|
||||
described in rule 3.
|
||||
"""
|
||||
|
||||
# See
|
||||
# http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
|
||||
result = []
|
||||
needquote = False
|
||||
for arg in seq:
|
||||
bs_buf = []
|
||||
|
||||
# Add a space to separate this argument from the others
|
||||
if result:
|
||||
result.append(' ')
|
||||
|
||||
needquote = (" " in arg) or ("\t" in arg) or ("|" in arg) or arg == ""
|
||||
if needquote:
|
||||
result.append('"')
|
||||
|
||||
for c in arg:
|
||||
if c == '\\':
|
||||
# Don't know if we need to double yet.
|
||||
bs_buf.append(c)
|
||||
elif c == '"':
|
||||
# Double backspaces.
|
||||
result.append('\\' * len(bs_buf)*2)
|
||||
bs_buf = []
|
||||
result.append('\\"')
|
||||
else:
|
||||
# Normal char
|
||||
if bs_buf:
|
||||
result.extend(bs_buf)
|
||||
bs_buf = []
|
||||
result.append(c)
|
||||
|
||||
# Add remaining backspaces, if any.
|
||||
if bs_buf:
|
||||
result.extend(bs_buf)
|
||||
|
||||
if needquote:
|
||||
result.extend(bs_buf)
|
||||
result.append('"')
|
||||
|
||||
return ''.join(result)
|
||||
38
documentation/poky-ref-manual/Makefile
Normal file
38
documentation/poky-ref-manual/Makefile
Normal file
@@ -0,0 +1,38 @@
|
||||
all: html pdf tarball
|
||||
|
||||
pdf:
|
||||
../tools/poky-docbook-to-pdf poky-ref-manual.xml ../template
|
||||
../tools/poky-docbook-to-pdf bsp-guide.xml ../template
|
||||
|
||||
XSLTOPTS = --stringparam html.stylesheet style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam appendix.autolabel A \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
|
||||
##
|
||||
# These URI should be rewritten by your distribution's xml catalog to
|
||||
# match your localy installed XSL stylesheets.
|
||||
XSL_BASE_URI = http://docbook.sourceforge.net/release/xsl/current
|
||||
XSL_XHTML_URI = $(XSL_BASE_URI)/xhtml/docbook.xsl
|
||||
|
||||
html:
|
||||
# See http://www.sagehill.net/docbookxsl/HtmlOutput.html
|
||||
xsltproc $(XSLTOPTS) -o poky-ref-manual.html $(XSL_XHTML_URI) poky-ref-manual.xml
|
||||
xsltproc $(XSLTOPTS) -o bsp-guide.html $(XSL_XHTML_URI) bsp-guide.xml
|
||||
|
||||
tarball: html
|
||||
tar -cvzf poky-ref-manual.tgz poky-ref-manual.html style.css figures/yocto-project-transp.png figures/poky-ref-manual.png
|
||||
|
||||
validate:
|
||||
xmllint --postvalid --xinclude --noout poky-ref-manual.xml
|
||||
|
||||
OUTPUTS = poky-ref-manual.tgz poky-ref-manual.html poky-ref-manual.pdf bsp-guide.pdf bsp-guide.html
|
||||
SOURCES = *.png *.xml *.css *.svg
|
||||
|
||||
publish:
|
||||
scp -r $(OUTPUTS) $(SOURCES) o-hand.com:/srv/www/pokylinux.org/doc/
|
||||
|
||||
clean:
|
||||
rm -f $(OUTPUTS)
|
||||
11
documentation/poky-ref-manual/TODO
Normal file
11
documentation/poky-ref-manual/TODO
Normal file
@@ -0,0 +1,11 @@
|
||||
Handbook Todo List:
|
||||
|
||||
* Document adding a new IMAGE_FEATURE to the customising images section
|
||||
* Add instructions about using zaurus/openmoko emulation
|
||||
* Add component overview/block diagrams
|
||||
* Software Deevelopment intro should mention its software development for
|
||||
intended target and could be a different arch etc and thus special case.
|
||||
* Expand insane.bbclass documentation to cover tests
|
||||
* Document remaining classes (see list in ref-classes)
|
||||
* Document formfactor
|
||||
|
||||
61
documentation/poky-ref-manual/bsp-guide.xml
Normal file
61
documentation/poky-ref-manual/bsp-guide.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<book id='poky-handbook' lang='en'
|
||||
xmlns:xi="http://www.w3.org/2003/XInclude"
|
||||
xmlns="http://docbook.org/ns/docbook"
|
||||
>
|
||||
<bookinfo>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref='poky-ref-manual.png'
|
||||
format='SVG'
|
||||
align='center' scalefit='1' width='100%'/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<title>Board Support Package (BSP) Developers Guide</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Richard</firstname> <surname>Purdie</surname>
|
||||
<affiliation>
|
||||
<orgname>Intel Corporation</orgname>
|
||||
</affiliation>
|
||||
<email>richard@linux.intel.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>0.4</revnumber>
|
||||
<date>26 May 2010</date>
|
||||
<revremark>Alpha Draft</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
<year>2010</year>
|
||||
<holder>Linux Foundation</holder>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
Permission is granted to copy, distribute and/or modify this document under
|
||||
the terms of the <ulink type="http" url="http://creativecommons.org/licenses/by-nc-sa/2.0/uk/">Creative Commons Attribution-Non-Commercial-Share Alike 2.0 UK: England & Wales</ulink> as published by Creative Commons.
|
||||
</para>
|
||||
</legalnotice>
|
||||
|
||||
</bookinfo>
|
||||
|
||||
<xi:include href="bsp.xml"/>
|
||||
|
||||
<index id='index'>
|
||||
<title>Index</title>
|
||||
</index>
|
||||
|
||||
</book>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
451
documentation/poky-ref-manual/bsp.xml
Normal file
451
documentation/poky-ref-manual/bsp.xml
Normal file
@@ -0,0 +1,451 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id='bsp'>
|
||||
|
||||
<title>Board Support Packages (BSP) - Developers Guide</title>
|
||||
|
||||
<para>
|
||||
A Board Support Package (BSP) is a collection of information which together
|
||||
defines how to support a particular hardware device, set of devices, or
|
||||
hardware platform. It will include information about the hardware features
|
||||
present on the device and kernel configuration information along with any
|
||||
additional hardware drivers required. It will also list any additional software
|
||||
components required in addition to a generic Linux software stack for both
|
||||
essential and optional platform features.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The intent of this document is to define a structure for these components
|
||||
so that BSPs follow a commonly understood layout, allowing them to be
|
||||
provided in a common form that everyone understands. It also allows end-users
|
||||
to become familiar with one common format and encourages standardisation
|
||||
of software support of hardware.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The proposed format does have elements that are specific to the Poky and
|
||||
OpenEmbedded build systems. It is intended that this information can be
|
||||
used by other systems besides Poky/OpenEmbedded and that it will be simple
|
||||
to extract information and convert to other formats if required. The format
|
||||
described can be directly accepted as a layer by Poky using its standard
|
||||
layers mechanism, but it is important to recognise that the BSP captures all
|
||||
the hardware specific details in one place in a standard format, which is
|
||||
useful for any person wishing to use the hardware platform regardless of
|
||||
the build system in use.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The BSP specification does not include a build system or other tools -
|
||||
it is concerned with the hardware specific components only. At the end
|
||||
distribution point the BSP may be shipped combined with a build system
|
||||
and other tools, but it is important to maintain the distinction that these
|
||||
are separate components which may just be combined in certain end products.
|
||||
</para>
|
||||
|
||||
<section id='bsp-filelayout'>
|
||||
<title>Example Filesystem Layout</title>
|
||||
|
||||
<para>
|
||||
The BSP consists of a file structure inside a base directory, meta-bsp in this example, where "bsp" is a placeholder for the machine or platform name. Examples of some files that it could contain are:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/
|
||||
meta-bsp/binary/zImage
|
||||
meta-bsp/binary/poky-image-minimal.directdisk
|
||||
meta-bsp/conf/layer.conf
|
||||
meta-bsp/conf/machine/*.conf
|
||||
meta-bsp/conf/machine/include/tune-*.inc
|
||||
meta-bsp/packages/bootloader/bootloader_0.1.bb
|
||||
meta-bsp/packages/linux/linux-bsp-2.6.50/*.patch
|
||||
meta-bsp/packages/linux/linux-bsp-2.6.50/defconfig-bsp
|
||||
meta-bsp/packages/linux/linux-bsp_2.6.50.bb
|
||||
meta-bsp/packages/modem/modem-driver_0.1.bb
|
||||
meta-bsp/packages/modem/modem-daemon_0.1.bb
|
||||
meta-bsp/packages/image-creator/image-creator-native_0.1.bb
|
||||
meta-bsp/prebuilds/
|
||||
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following sections detail what these files and directories could contain.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-binary'>
|
||||
<title>Prebuilt User Binaries (meta-bsp/binary/*)</title>
|
||||
|
||||
<para>
|
||||
This optional area contains useful prebuilt kernels and userspace filesystem
|
||||
images appropriate to the target system. Users could use these to get a system
|
||||
running and quickly get started on development tasks. The exact types of binaries
|
||||
present will be highly hardware-dependent but a README file should be present
|
||||
explaining how to use them with the target hardware. If prebuilt binaries are
|
||||
present, source code to meet licensing requirements must also be provided in
|
||||
some form.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-layer'>
|
||||
<title>Layer Configuration (meta-bsp/conf/layer.conf)</title>
|
||||
|
||||
<para>
|
||||
This file identifies the structure as a Poky layer. This file identifies the
|
||||
contents of the layer and contains information about how Poky should use
|
||||
it. In general it will most likely be a standard boilerplate file consisting of:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
# We have a conf directory, add to BBPATH
|
||||
BBPATH := "${BBPATH}${LAYERDIR}"
|
||||
|
||||
# We have a recipes directory containing .bb and .bbappend files, add to BBFILES
|
||||
BBFILES := "${BBFILES} ${LAYERDIR}/recipes/*/*.bb ${LAYERDIR}/recipes/*/*.bbappend"
|
||||
|
||||
BBFILE_COLLECTIONS += "bsp"
|
||||
BBFILE_PATTERN_bsp := "^${LAYERDIR}/"
|
||||
BBFILE_PRIORITY_bsp = "5"
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
which simply makes bitbake aware of the recipes and conf directories.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This file is required for recognition of the BSP by Poky.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-machine'>
|
||||
<title>Hardware Configuration Options (meta-bsp/conf/machine/*.conf)</title>
|
||||
|
||||
<para>
|
||||
The machine files bind together all the information contained elsewhere
|
||||
in the BSP into a format that Poky/OpenEmbedded can understand. If
|
||||
the BSP supports multiple machines, multiple machine configuration files
|
||||
can be present. These filenames correspond to the values users set the
|
||||
MACHINE variable to.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
These files would define things like which kernel package to use
|
||||
(PREFERRED_PROVIDER of virtual/kernel), which hardware drivers to
|
||||
include in different types of images, any special software components
|
||||
that are needed, any bootloader information, and also any special image
|
||||
format requirements.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
At least one machine file is required for a Poky BSP layer but more than one may be present.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-tune'>
|
||||
<title>Hardware Optimisation Options (meta-bsp/conf/machine/include/tune-*.inc)</title>
|
||||
|
||||
<para>
|
||||
These are shared hardware "tuning" definitions and are commonly used to
|
||||
pass specific optimisation flags to the compiler. An example is
|
||||
tune-atom.inc:
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
BASE_PACKAGE_ARCH = "core2"
|
||||
TARGET_CC_ARCH = "-m32 -march=core2 -msse3 -mtune=generic -mfpmath=sse"
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
which defines a new package architecture called "core2" and uses the
|
||||
optimization flags specified, which are carefully chosen to give best
|
||||
performance on atom cpus.
|
||||
</para>
|
||||
<para>
|
||||
The tune file would be included by the machine definition and can be
|
||||
contained in the BSP or reference one from the standard core set of
|
||||
files included with Poky itself.
|
||||
</para>
|
||||
<para>
|
||||
These files are optional for a Poky BSP layer.
|
||||
</para>
|
||||
</section>
|
||||
<section id='bsp-filelayout-kernel'>
|
||||
<title>Linux Kernel Configuration (meta-bsp/packages/linux/*)</title>
|
||||
|
||||
<para>
|
||||
These files make up the definition of a kernel to use with this
|
||||
hardware. In this case it is a complete self-contained kernel with its own
|
||||
configuration and patches but kernels can be shared between many
|
||||
machines as well. Taking some specific example files:
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/packages/linux/linux-bsp_2.6.50.bb
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
which is the core kernel recipe which firstly details where to get the kernel
|
||||
source from. All standard source code locations are supported so this could
|
||||
be a release tarball, some git repository, or source included in
|
||||
the directory within the BSP itself. It then contains information about which
|
||||
patches to apply and how to configure and build it. It can reuse the main
|
||||
Poky kernel build class, so the definitions here can remain very simple.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
linux-bsp-2.6.50/*.patch
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
which are patches which may be applied against the base kernel, wherever
|
||||
they may have been obtained from.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/packages/linux/linux-bsp-2.6.50/defconfig-bsp
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
which is the configuration information to use to configure the kernel.
|
||||
</para>
|
||||
<para>
|
||||
Examples of kernel recipes are available in Poky itself. These files are
|
||||
optional since a kernel from Poky itself could be selected, although it
|
||||
would be unusual not to have a kernel configuration.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-packages'>
|
||||
<title>Other Software (meta-bsp/packages/*)</title>
|
||||
|
||||
<para>
|
||||
This area includes other pieces of software which the hardware may need for best
|
||||
operation. These are just examples of the kind of things that may be
|
||||
encountered. These are standard .bb file recipes in the usual Poky format,
|
||||
so for examples, see standard Poky recipes. The source can be included directly,
|
||||
referred to in source control systems or release tarballs of external software projects.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/packages/bootloader/bootloader_0.1.bb
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Some kind of bootloader recipe which may be used to generate a new
|
||||
bootloader binary. Sometimes these are included in the final image
|
||||
format and needed to reflash hardware.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/packages/modem/modem-driver_0.1.bb
|
||||
meta-bsp/packages/modem/modem-daemon_0.1.bb
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
These are examples of a hardware driver and also a hardware daemon which
|
||||
may need to be included in images to make the hardware useful. "modem"
|
||||
is one example but there may be other components needed like firmware.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
meta-bsp/packages/image-creator/image-creator-native_0.1.bb
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Sometimes the device will need an image in a very specific format for
|
||||
its update mechanism to accept and reflash with it. Recipes to build the
|
||||
tools needed to do this can be included with the BSP.
|
||||
</para>
|
||||
<para>
|
||||
These files only need be provided if the platform requires them.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='bs-filelayout-bbappend'>
|
||||
<title>Append BSP specific information to existing recipes</title>
|
||||
|
||||
<para>
|
||||
Say you have a recipe like pointercal which has machine-specific information in it,
|
||||
and then you have your new BSP code in a layer. Before the .bbappend extension was
|
||||
introduced, you'd have to copy the whole pointercal recipe and files into your layer,
|
||||
and then add the single file for your machine, which is ugly.
|
||||
|
||||
.bbappend makes the above work much easier, to allow BSP-specific information to be merged
|
||||
with the original recipe easily. When bitbake finds any X.bbappend files, they will be
|
||||
included after bitbake loads X.bb but before finalise or anonymous methods run.
|
||||
This allows the BSP layer to poke around and do whatever it might want to customise
|
||||
the original recipe.
|
||||
|
||||
If your recipe needs to reference extra files it can use the FILESEXTRAPATH variable
|
||||
to specify their location. The example below shows extra files contained in a folder
|
||||
called ${PN} (the package name).
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
FILESEXTRAPATHS := "${THISDIR}/${PN}"
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Then the BSP could add machine-specific config files in layer directory, which will be
|
||||
added by bitbake. You can look at meta-emenlow/packages/formfactor as an example.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-prebuilds'>
|
||||
<title>Prebuild Data (meta-bsp/prebuilds/*)</title>
|
||||
|
||||
<para>
|
||||
The location can contain a precompiled representation of the source code
|
||||
contained elsewhere in the BSP layer. It can be processed and used by
|
||||
Poky to provide much faster build times, assuming a compatible configuration is used.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
These files are optional.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='bsp-click-through-licensing'>
|
||||
<title>BSP 'Click-through' Licensing Procedure</title>
|
||||
|
||||
<note><para> This section is here as a description of how
|
||||
click-through licensing is expected to work, and is
|
||||
not yet not impemented.
|
||||
</para></note>
|
||||
|
||||
<para>
|
||||
In some cases, a BSP may contain separately licensed IP
|
||||
(Intellectual Property) for a component, which imposes
|
||||
upon the user a requirement to accept the terms of a
|
||||
'click-through' license. Once the license is accepted
|
||||
(in whatever form that may be, see details below) the
|
||||
Poky build system can then build and include the
|
||||
corresponding component in the final BSP image. Some
|
||||
affected components may be essential to the normal
|
||||
functioning of the system and have no 'free' replacement
|
||||
i.e. the resulting system would be non-functional
|
||||
without them. Other components may be simply
|
||||
'good-to-have' or purely elective, or if essential
|
||||
nonetheless have a 'free' (possibly less-capable)
|
||||
version which may substituted for in the BSP recipe.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For the latter cases, where it is possible to do so from
|
||||
a functionality perspective, the Poky website will make
|
||||
available a 'de-featured' BSP completely free of
|
||||
encumbered IP, which can be used directly and without
|
||||
any further licensing requirements. If present, this
|
||||
fully 'de-featured' BSP will be named meta-bsp (i.e. the
|
||||
normal default naming convention). This is the simplest
|
||||
and therefore preferred option if available, assuming
|
||||
the resulting functionality meets requirements.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If however, a non-encumbered version is unavailable or
|
||||
the 'free' version would provide unsuitable
|
||||
functionality or quality, an encumbered version can be
|
||||
used. Encumbered versions of a BSP are given names of
|
||||
the form meta-bsp-nonfree. There are several ways
|
||||
within the Poky build system to satisfy the licensing
|
||||
requirements for an encumbered BSP, in roughly the
|
||||
following order of preference:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
|
||||
<para>
|
||||
Get a license key (or keys) for the encumbered BSP
|
||||
by
|
||||
visiting <ulink url='https://pokylinux.org/bsp-keys.html'>https://pokylinux.org/bsp-keys.html</ulink>
|
||||
and give the web form there the name of the BSP
|
||||
and your e-mail address.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
[screenshot of dialog box]
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
After agreeing to any applicable license terms, the
|
||||
BSP key(s) will be immediately sent to the address
|
||||
given and can be used by specifying BSPKEY_<keydomain>
|
||||
environment variables when building the image:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
$ BSPKEY_<keydomain>=<key> bitbake poky-image-sato
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
This will allow the encumbered image to be built
|
||||
with no change at all to the normal build process.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Equivalently and probably more conveniently, a line
|
||||
for each key can instead be put into the user's
|
||||
local.conf file.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <keydomain> component of the
|
||||
BSPKEY_<keydomain> is required because there
|
||||
may be multiple licenses in effect for a give BSP; a
|
||||
given <keydomain> in such cases corresponds to
|
||||
a particular license. In order for an encumbered
|
||||
BSP encompassing multiple key domains to be built
|
||||
successfully, a <keydomain> entry for each
|
||||
applicable license must be present in local.conf or
|
||||
supplied on the command-line.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Do nothing - build as you normally would, and follow
|
||||
any license prompts that originate from the
|
||||
encumbered BSP (the build will cleanly stop at this
|
||||
point). These usually take the form of instructions
|
||||
needed to manually fetch the encumbered package(s)
|
||||
and md5 sums into e.g. the poky/build/downloads
|
||||
directory. Once the manual package fetch has been
|
||||
completed, restarting the build will continue where
|
||||
it left off, this time without the prompt since the
|
||||
license requirements will have been satisfied.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Get a full-featured BSP recipe rather than a key, by
|
||||
visiting
|
||||
<ulink url='https://pokylinux.org/bsps.html'>https://pokylinux.org/bsps.html</ulink>.
|
||||
Accepting the license agreement(s) presented will
|
||||
subsequently allow you to download a tarball
|
||||
containing a full-featured BSP legally cleared for
|
||||
your use by the just-given license agreement(s).
|
||||
This method will also allow the encumbered image to
|
||||
be built with no change at all to the normal build
|
||||
process.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Note that method 3 is also the only option available
|
||||
when downloading pre-compiled images generated from
|
||||
non-free BSPs. Those images are likewise available at
|
||||
<ulink url='https://pokylinux.org/bsps.html'>https://pokylinux.org/bsps.html</ulink>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
1004
documentation/poky-ref-manual/development.xml
Normal file
1004
documentation/poky-ref-manual/development.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
DESCRIPTION = "GNU Helloworld application"
|
||||
SECTION = "examples"
|
||||
LICENSE = "GPLv3"
|
||||
|
||||
SRC_URI = "${GNU_MIRROR}/hello/hello-${PV}.tar.bz2"
|
||||
|
||||
inherit autotools
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Hello world!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
16
documentation/poky-ref-manual/examples/hello-single/hello.bb
Normal file
16
documentation/poky-ref-manual/examples/hello-single/hello.bb
Normal file
@@ -0,0 +1,16 @@
|
||||
DESCRIPTION = "Simple helloworld application"
|
||||
SECTION = "examples"
|
||||
LICENSE = "MIT"
|
||||
|
||||
SRC_URI = "file://helloworld.c"
|
||||
|
||||
S = "${WORKDIR}"
|
||||
|
||||
do_compile() {
|
||||
${CC} helloworld.c -o helloworld
|
||||
}
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${bindir}
|
||||
install -m 0755 helloworld ${D}${bindir}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
require xorg-lib-common.inc
|
||||
|
||||
DESCRIPTION = "X11 Pixmap library"
|
||||
LICENSE = "X-BSD"
|
||||
DEPENDS += "libxext"
|
||||
PR = "r2"
|
||||
PE = "1"
|
||||
|
||||
XORG_PN = "libXpm"
|
||||
|
||||
PACKAGES =+ "sxpm cxpm"
|
||||
FILES_cxpm = "${bindir}/cxpm"
|
||||
FILES_sxpm = "${bindir}/sxpm"
|
||||
@@ -0,0 +1,13 @@
|
||||
DESCRIPTION = "Tools for managing memory technology devices."
|
||||
SECTION = "base"
|
||||
DEPENDS = "zlib"
|
||||
HOMEPAGE = "http://www.linux-mtd.infradead.org/"
|
||||
LICENSE = "GPLv2"
|
||||
|
||||
SRC_URI = "ftp://ftp.infradead.org/pub/mtd-utils/mtd-utils-${PV}.tar.gz"
|
||||
|
||||
CFLAGS_prepend = "-I ${S}/include "
|
||||
|
||||
do_install() {
|
||||
oe_runmake install DESTDIR=${D}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user