Compare commits

..

21 Commits

Author SHA1 Message Date
Alexandru DAMIAN
1182665de0 bitbake: cooker, toaster: variable definition tracking
In order to track the file where a configuration
variable was defined, this patch bring these changes:

* a new feature is defined in CookerFeatures, named
BASEDATASTORE_TRACKING. When a UI requests BASEDATASTORE_TRACKING,
the base variable definition are tracked when configuration
is parsed.

* getAllKeysWithFlags now includes variable history in the
data dump

* toaster_ui.py will record the operation, file path
and line number where the variable was changes

* toaster Simple UI will display the file path
and line number for Configuration page

There is a change in the models to accomodate the recording
of variable change history.

[YOCTO #5227]

(Bitbake rev: 78e58fed82f2a71f052485de0052d7b9cca53ffd)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:18:03 +00:00
Cristiana Voicu
6302d1baf5 bitbake: toaster: task with outcome 2 (sstate), should have sstate_result!=0
0 (not applicable) is not a valid sstate_result for tasks with
outcome 2 (sstate), which should return 3 (restored), 2
(failed) or 1 (missed).
Sstate_result for tasks with outcome 2 is equal to the outcome
of _setscene corespondent task.

[YOCTO #5220]
(Bitbake rev: 8ff8d75318ea88ba80c744b471e486901ef6749a)

Signed-off-by: Cristiana Voicu <cristiana.voicu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:17:50 +00:00
Alexandru DAMIAN
3390674247 bitbake: toaster: fix path to buildstats file
The buildstats file path changes based on the
optional PE variable that may be defined for a
recipe.

The toasterui simply ignored the PE value, and
as such it didn't correctly reach buildstats files
for some of the tasks.

This patch fixes the issue.

    [YOCTO #5073]

(Bitbake rev: 97b8ab88edc7c8dfb26b4cf305701ec96e52cc4f)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:17:29 +00:00
Alexandru DAMIAN
0e6bbf8181 bitbake: toasterui: mark failed sceneQueue tasks as failed
This patch addresses an issue where a failed sceneQueue task
entry was not updated on the Fail event. As a result, it
always showed the task as not-available.

    [YOCTO #5216]

(Bitbake rev: 9b99a417f58381bac4bda412bcfd11de50403318)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:17:19 +00:00
Alexandru DAMIAN
3b113312fd bitbake: toaster: remove author field
The AUTHOR field in most recipes is not defined,
or it's not really consistently set in the metadata,
Also does it seem particularly useful.

This patch removes the AUTHOR variable from the
toaster system

    [YOCTO #5449]

(Bitbake rev: da3ac049300be84defab7b32b0b99ab07c7d0a27)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:17:09 +00:00
Alexandru DAMIAN
6f9c4d9778 bitbake: toaster: fix tasks showing as NoExec
Tasks without script type information showed by default
as NoExec; this happens for all Prebuild or Covered
tasks, as script type information comes only on TaskStarted
event. Such a default value may drive confusion, as NoExec value
should be reserved for the NoExec-flagged tasks.

This patch adds a new default value named Unknown that will be
used for all tasks that don't have script type information
available.

    [YOCTO #5327]

(Bitbake rev: ec6cac74290f0d4f5b60222019c23416b4b8e1ef)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:16:58 +00:00
Cristiana Voicu
f20de8ac90 bitbake: toaster: convert build_package size to bytes to keep consistence
[YOCTO #5503]
(Bitbake rev: 19eb6e01b675c439ff0a817be6fa5e34ad42ba37)

Signed-off-by: Cristiana Voicu <cristiana.voicu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-15 12:16:38 +00:00
Alexandru DAMIAN
e5cf3e598e bitbake: toaster: fix target package information
Toaster needs to record information about packages
installed on a built target image, and dependencies
between these packages.

This patch fixes a bug where the variable from the
server wasn't read correctly leading which caused
the buildhistory to not be processed correctly.

Additionally, two display issues in the package table
were fixed, issues that lead to package information
being displayed incorrectly.

    [YOCTO #5197]

(Bitbake rev: ab4bc18409d80de6d069e3dd76c3c54964fe5764)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-05 12:50:58 +00:00
Cristiana Voicu
46814c99ee bitbake: toaster: add variable description for prefixed/suffixed variables
In the Configuration table, we need to link prefixed / suffixed
variables to the corresponding variable descriptions in documentation.conf.

[YOCTO #5198]

(Bitbake rev: 641d9c4fda5fe978154fdfab978c3c09e3906eab)

Signed-off-by: Cristiana Voicu <cristiana.voicu@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:56:04 +00:00
Alexandru DAMIAN
b0c24033bb bitbake: toaster: enable required classes in the toaster startup script
In order to use toaster, now you have to set INHERIT+="toaster buildhistory"
To keep it simple, I've done some changes in order to automate it. When toaster
is started, this line is added to a new file called toaster.conf.

This file is passed to the bitbake server with the --postread parameter.

Based on a patch by Cristiana Voicu <cristiana.voicu@intel.com>

(Bitbake rev: 029e868044989eda370340f8bf4200cfd2670fca)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:55:54 +00:00
Alexandru DAMIAN
25f50e24c7 bitbake: toaster: server shutdown on terminal exit
If the terminal where the server was started is closed,
the bitbake server should shutdown. Currently the system
is left in hanging state.

This patch uses "trap" command to make sure the servers
are closed on terminal exit.

[YOCTO #5376]

(Bitbake rev: 5f8b97010f7b465753b6ff6275d18426006ee14b)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:53:50 +00:00
Alexandru DAMIAN
a6b357a9af bitbake: toaster: fix timezone settings
This patch fixes an issue where, if not defined,
the timezone defaults to 'America/Chicago'.

The solution is to set the timezone to current computer's
timezone.

[YOCTO #5186]

(Bitbake rev: a4102b549f04a9b52cdcd318bf511a18ab48067d)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:53:43 +00:00
Alexandru DAMIAN
57beaf994f bitbake: cooker: do not recreate recipecache in buildfile mode
When building a single file, the cooker will recreate
the recipecache from scratch.

I suspect this is a remnant of past code, since:
* the current recipecache works fine
* the new recipecache will not have all the fields as
requested by HOB_EXTRA_CACHES setting

This patch disables recreating the recipecache, leading
to shorter times when building single build files
(-b option) and better compatibility with Toaster.

(Bitbake rev: 618d69b00075981b8553513130d7deb1aed61578)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:53:35 +00:00
Alexandru DAMIAN
e1aebfe018 bitbake: build, toaster: record proper task type
Bitbake tasks may be of type 'python' or 'shell',
or they may not be executed at all, which is record
as task type 'noexec'.

In order to record proper task type, this patch:

* creates no exec task type as the default value in
the toaster model definition

* adds full task flags to the bb.build.TaskStarted event
in build.py

* if the task actually starts, the toaster ui will
record the type of the task as either 'python' or 'shell'
based on the task flags.

[YOCTO #5073]
[YOCTO #5075]
[YOCTO #5327]

(Bitbake rev: 6648c57e6d369fc009ea3a9fe939def5d2c67bf5)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:53:24 +00:00
Alexandru DAMIAN
f6847b0cd2 bitbake: cooker: add data to the dependency tree dump
Toaster needes to record extra data that needs to
be moved at the time of the dependency tree dump.

This data includes:
* layer priorities for recording in the layer section
* the inherit list for each PN which allows to determine
the type of the PN (regular package, image, etc).

This patch adds this data to the dependency tree dump.

(Bitbake rev: 7636aba37320aaf9b044d3832ddc21af51ccd69c)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:53:12 +00:00
Alexandru DAMIAN
1e6e27d98d bitbake: toaster: fixes for null values from events
Some of the data values may come of as None through the event system,
and the UI would encounter a problem saving the Configuration.
It would be trying to save these values as NULL in the
database, which is not allowed.

This patch adds more verification for data coming through
the event system.

Other minor updates:
* update for the event model from toaster.bbclass
* minor code flow fix in the event system

(Bitbake rev: 03fafd086381723c6486522873671515824e49f2)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:52:59 +00:00
Alexandru DAMIAN
73293c6481 toaster: add class to dump toaster-tracked data
Adding a new bbclass that will collect and send relevant
data from the task context to the Toaster UI.

This bbclass consists of postfuncs that get executed
right after the main task func, and in the same context.
This allows data gathering in a synchronous manner during
the build, guaranteeing data integrity. This approach also
preserves the task signatures.

The data is moved to the UI through the event system.

There is no performance impact if the class is disabled.

License is MIT.

(From OE-Core rev: 1d2d37d579492b63d20ff8aa890a43b9a1576cf0)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:52:45 +00:00
Belen Barros Pena
d7b8f82a64 documentation.conf: update contents
This patch updates descriptions and brings more
info for the variable configurations as documented
in the OE-Core manual.

This file is used by Toaster to display help for
the configuration variables.

(From OE-Core rev: 98405beddb93490c8a2e9903adc2a510969ed6a9)

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-11-04 10:52:39 +00:00
Alexandru DAMIAN
4b64eb444a bitbake: add Toaster UI interface
Adding a new bitbake UI interface named 'toasterui'.

'toasterui' listens for events and data coming from a
bitbake server during a run, and records it
in a data store using the Toaster object model.

Adds a helper class named BuildInfoHelper that
reconstructs the state of the bitbake server and
saves relevant data to the data store.

Code portions contributed by Calin Dragomir <calindragomir@gmail.com>.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
2013-10-16 15:27:03 +01:00
Alexandru DAMIAN
db2a7845a9 bitbake: toaster: adding frameworks for the Simple UI
This commit adds the 3rd party frameworks used for the web UI.

jQuery is licensed under MIT.

Bootstrap is licensed under APACHE-2.0

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
2013-10-16 15:26:52 +01:00
Alexandru DAMIAN
cb1338dedc bitbake: add toaster code to bitbake
This patch adds the Toaster component to Bitbake.

Toaster is a module designed to record the progress of a
Bitbake build, and data about the resultant artifacts.

It contains a web-based interface and a REST API allowing
post-facto inspection of the build process and artifacts.

Features present in this build:
* toaster start script
* relational data model
* Django boilerplate code
* the REST API
* the Simple UI web interface

This patch has all the development history squashed together.

Code portions contributed by Calin Dragomir <calindragomir@gmail.com>.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
2013-10-16 15:26:37 +01:00
4008 changed files with 138189 additions and 386812 deletions

8
.gitignore vendored
View File

@@ -16,8 +16,8 @@ hob-image-*.bb
*~
!meta-yocto
!meta-yocto-bsp
!meta-yocto-imported
documentation/user-manual/user-manual.html
documentation/user-manual/user-manual.pdf
documentation/user-manual/user-manual.tgz
bitbake/doc/manual/html/
bitbake/doc/manual/pdf/
bitbake/doc/manual/txt/
bitbake/doc/manual/xhtml/
pull-*/

View File

@@ -1,2 +0,0 @@
# Template settings
TEMPLATECONF=${TEMPLATECONF:-meta-yocto/conf}

16
LICENSE
View File

@@ -1,14 +1,14 @@
Different components of OpenEmbedded are under different licenses (a mix
of MIT and GPLv2). Please see:
Different components of Poky are under different licenses (a mix of
MIT and GPLv2). Please see:
meta/COPYING.GPLv2 (GPLv2)
bitbake/COPYING (GPLv2)
meta/COPYING.MIT (MIT)
meta-selftest/COPYING.MIT (MIT)
meta-skeleton/COPYING.MIT (MIT)
meta-extras/COPYING.MIT (MIT)
All metadata is MIT licensed unless otherwise stated. Source code
included in tree for individual recipes is under the LICENSE stated in
the associated recipe (.bb file) unless otherwise stated.
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.

2
README
View File

@@ -22,7 +22,7 @@ reference manual which can be found at:
OpenEmbedded-Core is a layer containing the core metadata for current versions
of OpenEmbedded. It is distro-less (can build a functional image with
DISTRO = "nodistro") and contains only emulated machine support.
DISTRO = "") and contains only emulated machine support.
For information about OpenEmbedded, see the OpenEmbedded website:
http://www.openembedded.org/

View File

@@ -46,19 +46,13 @@ Hardware Reference Boards
The following boards are supported by the meta-yocto-bsp layer:
* Texas Instruments Beaglebone (beaglebone)
* Texas Instruments Beagleboard (beagleboard)
* Freescale MPC8315E-RDB (mpc8315e-rdb)
* Ubiquiti Networks RouterStation Pro (routerstationpro)
For more information see the board's section below. The appropriate MACHINE
variable value corresponding to the board is given in brackets.
Reference Board Maintenance
===========================
Send pull requests, patches, comments or questions about meta-yocto-bsps to poky@yoctoproject.org
Maintainers: Kevin Hao <kexin.hao@windriver.com>
Bruce Ashfield <bruce.ashfield@windriver.com>
Consumer Devices
================
@@ -66,7 +60,6 @@ Consumer Devices
The following consumer devices are supported by the meta-yocto-bsp layer:
* Intel x86 based PCs and devices (genericx86)
* Ubiquiti Networks EdgeRouter Lite (edgerouter)
For more information see the device's section below. The appropriate MACHINE
variable value corresponding to the device is given in brackets.
@@ -187,28 +180,30 @@ USB Device:
http://git.kernel.org/?p=boot/syslinux/syslinux.git;a=blob_plain;f=doc/usbkey.txt;hb=HEAD
Texas Instruments Beaglebone (beaglebone)
=========================================
Texas Instruments Beagleboard (beagleboard)
===========================================
The Beaglebone is an ARM Cortex-A8 development board with USB, Ethernet, 2D/3D
accelerated graphics, audio, serial, JTAG, and SD/MMC. The Black adds a faster
CPU, more RAM, eMMC flash and a micro HDMI port. The beaglebone MACHINE is
tested on the following platforms:
The Beagleboard is an ARM Cortex-A8 development board with USB, DVI-D, S-Video,
2D/3D accelerated graphics, audio, serial, JTAG, and SD/MMC. The xM adds a
faster CPU, more RAM, an ethernet port, more USB ports, microSD, and removes
the NAND flash. The beagleboard MACHINE is tested on the following platforms:
o Beaglebone Black A6
o Beaglebone A6 (the original "White" model)
o Beagleboard C4
o Beagleboard xM rev A & B
The Beaglebone Black has eMMC, while the White does not. Pressing the USER/BOOT
button when powering on will temporarily change the boot order. But for the sake
of simplicity, these instructions assume you have erased the eMMC on the Black,
so its boot behavior matches that of the White and boots off of SD card. To do
this, issue the following commands from the u-boot prompt:
The Beagleboard C4 has NAND, while the xM does not. For the sake of simplicity,
these instructions assume you have erased the NAND on the C4 so its boot
behavior matches that of the xM. To do this, issue the following commands from
the u-boot prompt (note that the unlock may be unecessary depending on the
version of u-boot installed on your board and only one of the erase commands
will succeed):
# mmc dev 1
# mmc erase 0 512
# nand unlock
# nand erase
# nand erase.chip
To further tailor these instructions for your board, please refer to the
documentation at http://www.beagleboard.org/bone and http://www.beagleboard.org/black
documentation at http://www.beagleboard.org.
From a Linux system with access to the image files perform the following steps
as root, replacing mmcblk0* with the SD card device on your machine (such as sdc
@@ -216,11 +211,11 @@ if used via a usb card reader):
1. Partition and format an SD card:
# fdisk -lu /dev/mmcblk0
Disk /dev/mmcblk0: 3951 MB, 3951034368 bytes
255 heads, 63 sectors/track, 480 cylinders, total 7716864 sectors
Units = sectors of 1 * 512 = 512 bytes
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 * 63 144584 72261 c Win95 FAT32 (LBA)
/dev/mmcblk0p2 144585 465884 160650 83 Linux
@@ -228,40 +223,51 @@ if used via a usb card reader):
# mkfs.vfat -F 16 -n "boot" /dev/mmcblk0p1
# mke2fs -j -L "root" /dev/mmcblk0p2
The following assumes the SD card partitions 1 and 2 are mounted at
The following assumes the SD card partition 1 and 2 are mounted at
/media/boot and /media/root respectively. Removing the card and reinserting
it will do just that on most modern Linux desktop environments.
The files referenced below are made available after the build in
build/tmp/deploy/images.
2. Install the boot loaders
# cp MLO-beaglebone /media/boot/MLO
# cp u-boot-beaglebone.img /media/boot/u-boot.img
# cp MLO-beagleboard /media/boot/MLO
# cp u-boot-beagleboard.bin /media/boot/u-boot.bin
3. Install the root filesystem
# tar x -C /media/root -f core-image-$IMAGE_TYPE-beaglebone.tar.bz2
# tar x -C /media/root -f core-image-$IMAGE_TYPE-beagleboard.tar.bz2
# tar x -C /media/root -f modules-$KERNEL_VERSION-beagleboard.tgz
4. If using core-image-base or core-image-sato images, the SD card is ready
and rootfs already contains the kernel, modules and device tree (DTB)
files necessary to be booted with U-boot's default configuration, so
skip directly to step 8.
For core-image-minimal, proceed through next steps.
4. Install the kernel uImage
# cp uImage-beagleboard.bin /media/boot/uImage
5. If using core-image-minimal rootfs, install the modules
# tar x -C /media/root -f modules-beaglebone.tgz
5. Prepare a u-boot script to simplify the boot process
The Beagleboard can be made to boot at this point from the u-boot command
shell. To automate this process, generate a user.scr script as follows.
6. If using core-image-minimal rootfs, install the kernel uImage into /boot
directory of rootfs
# cp uImage-beaglebone.bin /media/root/boot/uImage
Install uboot-mkimage (from uboot-mkimage on Ubuntu or uboot-tools on Fedora).
7. If using core-image-minimal rootfs, also install device tree (DTB) files
into /boot directory of rootfs
# cp uImage-am335x-bone.dtb /media/root/boot/am335x-bone.dtb
# cp uImage-am335x-boneblack.dtb /media/root/boot/am335x-boneblack.dtb
Prepare a script config:
8. Unmount the SD partitions, insert the SD card into the Beaglebone, and
boot the Beaglebone
# (cat << EOF
setenv bootcmd 'mmc init; fatload mmc 0:1 0x80300000 uImage; bootm 0x80300000'
setenv bootargs 'console=tty0 console=ttyO2,115200n8 root=/dev/mmcblk0p2 rootwait rootfstype=ext3 ro'
boot
EOF
) > serial-boot.cmd
# mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "Core Minimal" -d ./serial-boot.cmd ./boot.scr
# cp boot.scr /media/boot
6. Unmount the SD partitions, insert the SD card into the Beagleboard, and
boot the Beagleboard
Note: As of the 2.6.37 linux-yocto kernel recipe, the Beagleboard uses the
OMAP_SERIAL device (ttyO2). If you are using an older kernel, such as the
2.6.34 linux-yocto-stable, be sure to replace ttyO2 with ttyS2 above. You
should also override the machine SERIAL_CONSOLE in your local.conf in
order to setup the getty on the serial line:
SERIAL_CONSOLE_beagleboard = "115200 ttyS2"
Freescale MPC8315E-RDB (mpc8315e-rdb)
@@ -324,176 +330,165 @@ Load the kernel and dtb (device tree blob), and boot the system as follows:
=> tftp 2000000 uImage-mpc8315e-rdb.dtb
=> bootm 1000000 - 2000000
--- Booting from JFFS2 root ---
1. First boot the board with NFS root.
Ubiquiti Networks RouterStation Pro (routerstationpro)
======================================================
2. Erase the MTD partition which will be used as root:
$ flash_eraseall /dev/mtd3
3. Copy the JFFS2 image to the MTD partition:
$ flashcp core-image-minimal-mpc8315e-rdb.jffs2 /dev/mtd3
4. Then reboot the board and set up the environment in U-Boot:
=> setenv bootargs root=/dev/mtdblock3 rootfstype=jffs2 console=ttyS0,115200
Ubiquiti Networks EdgeRouter Lite (edgerouter)
==============================================
The EdgeRouter Lite is part of the EdgeMax series. It is a MIPS64 router
(based on the Cavium Octeon processor) with 512MB of RAM, which uses an
internal USB pendrive for storage.
The RouterStation Pro is an Atheros AR7161 MIPS-based board. Geared towards
networking applications, it has all of the usual features as well as three
type IIIA mini-PCI slots and an on-board 3-port 10/100/1000 Ethernet switch,
in addition to the 10/100/1000 Ethernet WAN port which supports
Power-over-Ethernet.
Setup instructions
------------------
You will need the following:
* NFS root setup on your workstation
* TFTP server installed on your workstation
* RJ45 -> serial ("rollover") cable connected from your PC to the CONSOLE
port on the board
* Ethernet connected to the first ethernet port on the board
* A serial cable - female to female (or female to male + gender changer)
NOTE: cable must be straight through, *not* a null modem cable.
* USB flash drive or hard disk that is able to be powered from the
board's USB port.
* tftp server installed on your workstation
NOTE: in the following instructions it is assumed that /dev/sdb corresponds
to the USB disk when it is plugged into your workstation. If this is not the
case in your setup then please be careful to substitute the correct device
name in all commands where appropriate.
--- Preparation ---
Build an image (e.g. core-image-minimal) using "edgerouter" as the MACHINE.
In the following instruction it is based on core-image-minimal. Another target
may be similiar with it.
1) Build an image (e.g. core-image-minimal) using "routerstationpro" as the
MACHINE
--- Booting from NFS root ---
2) Partition the USB drive so that primary partition 1 is type Linux (83).
Minimum size depends on your root image size - core-image-minimal probably
only needs 8-16MB, other images will need more.
Load the kernel, and boot the system as follows:
# fdisk /dev/sdb
Command (m for help): p
1. Get the kernel (vmlinux) file from the tmp/deploy/images/edgerouter
directory, and make them available on your TFTP server.
Disk /dev/sdb: 4011 MB, 4011491328 bytes
124 heads, 62 sectors/track, 1019 cylinders, total 7834944 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0009e87d
2. Connect the board's first serial port to your workstation and then start up
your favourite serial terminal so that you will be able to interact with
the serial console. If you don't have a favourite, picocom is suggested:
Device Boot Start End Blocks Id System
/dev/sdb1 62 1952751 976345 83 Linux
$ picocom /dev/ttyS0 -b 115200
3) Format partition 1 on the USB as ext3
3. Power up or reset the board and press a key on the terminal when prompted
to get to the U-Boot command line
# mke2fs -j /dev/sdb1
4. Set up the environment in U-Boot:
4) Mount partition 1 and then extract the contents of
tmp/deploy/images/core-image-XXXX.tar.bz2 into it (preserving permissions).
=> setenv ipaddr <board ip>
=> setenv serverip <tftp server ip>
# mount /dev/sdb1 /media/sdb1
# cd /media/sdb1
# tar -xvjpf tmp/deploy/images/core-image-XXXX.tar.bz2
5. Download the kernel and boot:
5) Unmount the USB drive and then plug it into the board's USB port
=> tftp tftp $loadaddr vmlinux
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/nfs rw nfsroot=<nfsroot ip>:<rootfs path> ip=<board ip>:<server ip>:<gateway ip>:<netmask>:edgerouter:eth0:off mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)
6) Connect the board's serial port to your workstation and then start up
your favourite serial terminal so that you will be able to interact with
the serial console. If you don't have a favourite, picocom is suggested:
--- Booting from USB root ---
$ picocom /dev/ttyUSB0 -b 115200
To boot from the USB disk, you either need to remove it from the edgerouter
box and populate it from another computer, or use a previously booted NFS
image and populate from the edgerouter itself.
7) Connect the network into eth0 (the one that is NOT the 3 port switch). If
you are using power-over-ethernet then the board will power up at this point.
Type 1: Mounted USB disk
------------------------
8) Start up the board, watch the serial console. Hit Ctrl+C to abort the
autostart if the board is configured that way (it is by default). The
bootloader's fconfig command can be used to disable autostart and configure
the IP settings if you need to change them (default IP is 192.168.1.20).
To boot from the USB disk there are two available partitions on the factory
USB storage. The rest of this guide assumes that these partitions are left
intact. If you change the partition scheme, you must update your boot method
appropriately.
9) Make the kernel (tmp/deploy/images/vmlinux-routerstationpro.bin) available
on the tftp server.
The standard partitions are:
10) If you are going to write the kernel to flash (optional - see "Booting a
kernel directly" below for the alternative), remove the current kernel and
rootfs flash partitions. You can list the partitions using the following
bootloader command:
- 1: vfat partition containing factory kernels
- 2: ext3 partition for the root filesystem.
RedBoot> fis list
You can place the kernel on either partition 1, or partition 2, but the roofs
must go on partition 2 (due to its size).
You can delete the existing kernel and rootfs with these commands:
Note: If you place the kernel on the ext3 partition, you must re-create the
ext3 filesystem, since the factory u-boot can only handle 128 byte inodes and
cannot read the partition otherwise.
RedBoot> fis delete kernel
RedBoot> fis delete rootfs
Steps:
--- Booting a kernel directly ---
1. Remove the USB disk from the edgerouter and insert it into a computer
that has access to your build artifacts.
1) Load the kernel using the following bootloader command:
2. Copy the kernel image to the USB storage (assuming discovered as 'sdb' on
the development machine):
RedBoot> load -m tftp -h <ip of tftp server> vmlinux-routerstationpro.bin
2a) if booting from vfat
# mount /dev/sdb1 /mnt
# cp tmp/deploy/images/edgerouter/vmlinux /mnt
# umount /mnt
You should see a message on it being successfully loaded.
2b) if booting from ext3
2) Execute the kernel:
# mkfs.ext3 -I 128 /dev/sdb2
# mount /dev/sdb2 /mnt
# mkdir /mnt/boot
# cp tmp/deploy/images/edgerouter/vmlinux /mnt/boot
# umount /mnt
RedBoot> exec -c "console=ttyS0,115200 root=/dev/sda1 rw rootdelay=2 board=UBNT-RSPRO"
3. Extract the rootfs to the USB storage ext3 partition
Note that specifying the command line with -c is important as linux-yocto does
not provide a default command line.
# mount /dev/sdb2 /mnt
# tar -xvjpf core-image-minimal-XXX.tar.bz2 -C /mnt
# umount /mnt
--- Writing a kernel to flash ---
4. Reboot the board and press a key on the terminal when prompted to get to the U-Boot
command line:
1) Go to your tftp server and gzip the kernel you want in flash. It should
halve the size.
5. Load the kernel and boot:
2) Load the kernel using the following bootloader command:
5a) vfat boot
RedBoot> load -r -b 0x80600000 -m tftp -h <ip of tftp server> vmlinux-routerstationpro.bin.gz
=> fatload usb 0:1 $loadaddr vmlinux
This should output something similar to the following:
5b) ext3 boot
Raw file loaded 0x80600000-0x8087c537, assumed entry at 0x80600000
=> ext2load usb 0:2 $loadaddr boot/vmlinux
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/sda2 rw rootwait mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)
Calculate the length by subtracting the first number from the second number
and then rounding the result up to the nearest 0x1000.
Type 2: NFS
-----------
3) Using the length calculated above, create a flash partition for the kernel:
Note: If you place the kernel on the ext3 partition, you must re-create the
ext3 filesystem, since the factory u-boot can only handle 128 byte inodes and
cannot read the partition otherwise.
RedBoot> fis create -b 0x80600000 -l 0x240000 kernel
These boot instructions assume that you have recreated the ext3 filesystem with
128 byte inodes, you have an updated uboot or you are running and image capable
of making the filesystem on the board itself.
(change 0x240000 to your rounded length -- change "kernel" to whatever
you want to name your kernel)
--- Booting a kernel from flash ---
1. Boot from NFS root
To boot the flashed kernel perform the following steps.
2. Mount the USB disk partition 2 and then extract the contents of
tmp/deploy/core-image-XXXX.tar.bz2 into it.
1) At the bootloader prompt, load the kernel:
Before starting, copy core-image-minimal-xxx.tar.bz2 and vmlinux into
rootfs path on your workstation.
RedBoot> fis load -d -e kernel
and then,
# mount /dev/sda2 /media/sda2
# tar -xvjpf core-image-minimal-XXX.tar.bz2 -C /media/sda2
# cp vmlinux /media/sda2/boot/vmlinux
# umount /media/sda2
# reboot
(Change the name "kernel" above if you chose something different earlier)
3. Reboot the board and press a key on the terminal when prompted to get to the U-Boot
command line:
(-e means 'elf', -d 'decompress')
# reboot
2) Execute the kernel using the exec command as above.
4. Load the kernel and boot:
--- Automating the boot process ---
After writing the kernel to flash and testing the load and exec commands
manually, you can automate the boot process with a boot script.
1) RedBoot> fconfig
(Answer the questions not specified here as they pertain to your environment)
2) Run script at boot: true
Boot script:
.. fis load -d -e kernel
.. exec
Enter script, terminate with empty line
>> fis load -d -e kernel
>> exec -c "console=ttyS0,115200 root=/dev/sda1 rw rootdelay=2 board=UBNT-RSPRO"
>>
3) Answer the remaining questions and write the changes to flash:
Update RedBoot non-volatile configuration - continue (y/n)? y
... Erase from 0xbfff0000-0xc0000000: .
... Program from 0x87ff0000-0x88000000 at 0xbfff0000: .
4) Power cycle the board.
=> ext2load usb 0:2 $loadaddr boot/vmlinux
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/sda2 rw rootwait mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)

View File

@@ -41,7 +41,7 @@ from bb import ui
from bb import server
from bb import cookerdata
__version__ = "1.24.0"
__version__ = "1.20.0"
logger = logging.getLogger("BitBake")
# Python multiprocessing requires /dev/shm
@@ -139,8 +139,8 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
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 = "Dump out the signature construction information, with no task execution. The SIGNATURE_HANDLER parameter is passed to the handler. Two common values are none and printdiff but the handler may define more/less. none means only dump the signature, printdiff means compare the dumped signature with the cached one.",
action = "append", dest = "dump_signatures", default = [], metavar="SIGNATURE_HANDLER")
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 recipes.",
action = "store_true", dest = "parse_only", default = False)
@@ -148,7 +148,7 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
parser.add_option("-s", "--show-versions", help = "Show current and preferred versions of all recipes.",
action = "store_true", dest = "show_versions", default = False)
parser.add_option("-e", "--environment", help = "Show the global or per-recipe environment complete with information about where variables were set/changed.",
parser.add_option("-e", "--environment", help = "Show the global or per-package environment complete with information about where variables were set/changed.",
action = "store_true", dest = "show_environment", default = False)
parser.add_option("-g", "--graphviz", help = "Save dependency tree information for the specified targets in the dot syntax.",
@@ -169,9 +169,6 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
parser.add_option("-t", "--servertype", help = "Choose which server to use, process or xmlrpc.",
action = "store", dest = "servertype")
parser.add_option("", "--token", help = "Specify the connection token to be used when connecting to a remote server.",
action = "store", dest = "xmlrpctoken")
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)
@@ -193,9 +190,6 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
action = "store_true", dest = "observe_only", default = False)
parser.add_option("", "--status-only", help = "Check the status of the remote bitbake server.",
action = "store_true", dest = "status_only", default = False)
options, targets = parser.parse_args(sys.argv)
# some environmental variables set also configuration options
@@ -203,54 +197,27 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
options.servertype = "xmlrpc"
options.remote_server = os.environ["BBSERVER"]
if "BBTOKEN" in os.environ:
options.xmlrpctoken = os.environ["BBTOKEN"]
# if BBSERVER says to autodetect, let's do that
if options.remote_server:
[host, port] = options.remote_server.split(":", 2)
port = int(port)
# use automatic port if port set to -1, means read it from
# the bitbake.lock file; this is a bit tricky, but we always expect
# to be in the base of the build directory if we need to have a
# chance to start the server later, anyway
if port == -1:
lock_location = "./bitbake.lock"
# we try to read the address at all times; if the server is not started,
# we'll try to start it after the first connect fails, below
try:
lf = open(lock_location, 'r')
remotedef = lf.readline()
[host, port] = remotedef.split(":")
port = int(port)
lf.close()
options.remote_server = remotedef
except Exception as e:
sys.exit("Failed to read bitbake.lock (%s), invalid port" % str(e))
return options, targets[1:]
def start_server(servermodule, configParams, configuration, features):
def start_server(servermodule, configParams, configuration):
server = servermodule.BitBakeServer()
if configParams.bind:
(host, port) = configParams.bind.split(':')
server.initServer((host, int(port)))
configuration.interface = [ server.serverImpl.host, server.serverImpl.port ]
else:
server.initServer()
configuration.interface = []
try:
configuration.setServerRegIdleCallback(server.getServerIdleCB())
cooker = bb.cooker.BBCooker(configuration, features)
cooker = bb.cooker.BBCooker(configuration)
server.addcooker(cooker)
server.saveConnectionDetails()
except Exception as e:
exc_info = sys.exc_info()
while hasattr(server, "event_queue"):
while True:
try:
import queue
except ImportError:
@@ -263,7 +230,6 @@ def start_server(servermodule, configParams, configuration, features):
logger.handle(event)
raise exc_info[1], None, exc_info[2]
server.detach()
cooker.lock.close()
return server
@@ -288,7 +254,7 @@ def main():
servermodule = getattr(module, server_type)
except AttributeError:
sys.exit("FATAL: Invalid server type '%s' specified.\n"
"Valid interfaces: xmlrpc, process [default]." % server_type)
"Valid interfaces: xmlrpc, process [default]." % servertype)
if configParams.server_only:
if configParams.servertype != "xmlrpc":
@@ -308,9 +274,6 @@ def main():
if configParams.observe_only and (not configParams.remote_server or configParams.bind):
sys.exit("FATAL: '--observe-only' can only be used by UI clients connecting to a server.\n")
if configParams.kill_server and not configParams.remote_server:
sys.exit("FATAL: '--kill-server' can only be used to terminate a remote server")
if "BBDEBUG" in os.environ:
level = int(os.environ["BBDEBUG"])
if level > configuration.debug:
@@ -321,35 +284,26 @@ def main():
# Ensure logging messages get sent to the UI as events
handler = bb.event.LogHandler()
if not configParams.status_only:
# In status only mode there are no logs and no UI
logger.addHandler(handler)
logger.addHandler(handler)
# Clear away any spurious environment variables while we stoke up the cooker
cleanedvars = bb.utils.clean_environment()
featureset = []
if not configParams.remote_server:
# we start a server with a given configuration
server = start_server(servermodule, configParams, configuration)
bb.event.ui_queue = []
else:
# we start a stub server that is actually a XMLRPClient that connects to a real server
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only)
server.saveConnectionDetails(configParams.remote_server)
if not configParams.server_only:
# Collect the feature set for the UI
featureset = getattr(ui_module, "featureSet", [])
if not configParams.remote_server:
# we start a server with a given configuration
server = start_server(servermodule, configParams, configuration, featureset)
bb.event.ui_queue = []
else:
# we start a stub server that is actually a XMLRPClient that connects to a real server
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only, configParams.xmlrpctoken)
server.saveConnectionDetails(configParams.remote_server)
if not configParams.server_only:
try:
server_connection = server.establishConnection(featureset)
except Exception as e:
if configParams.kill_server:
sys.exit(0)
bb.fatal("Could not connect to server %s: %s" % (configParams.remote_server, str(e)))
# Setup a connection to the server (cooker)
server_connection = server.establishConnection(featureset)
# Restore the environment in case the UI needs it
for k in cleanedvars:
@@ -357,16 +311,6 @@ def main():
logger.removeHandler(handler)
if configParams.status_only:
server_connection.terminate()
sys.exit(0)
if configParams.kill_server:
server_connection.connection.terminateServer()
bb.event.ui_queue = []
sys.exit(0)
try:
return ui_module.main(server_connection.connection, server_connection.events, configParams)
finally:
@@ -374,7 +318,6 @@ def main():
server_connection.terminate()
else:
print("server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
return 0
return 1

View File

@@ -118,5 +118,5 @@ else:
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
sys.exit(1)
if output:
print '\n'.join(output)
if output:
print '\n'.join(output)

View File

@@ -55,16 +55,10 @@ def main(args):
class Commands(cmd.Cmd):
def __init__(self):
self.bbhandler = None
self.returncode = 0
self.bblayers = []
cmd.Cmd.__init__(self)
def init_bbhandler(self, config_only = False):
if not self.bbhandler:
self.bbhandler = bb.tinfoil.Tinfoil()
self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
self.bbhandler.prepare(config_only)
self.bbhandler = bb.tinfoil.Tinfoil()
self.returncode = 0
self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
def default(self, line):
"""Handle unrecognised commands"""
@@ -89,7 +83,7 @@ class Commands(cmd.Cmd):
def do_show_layers(self, args):
"""show current configured layers"""
self.init_bbhandler(config_only = True)
self.bbhandler.prepare(config_only = True)
logger.plain("%s %s %s" % ("layer".ljust(20), "path".ljust(40), "priority"))
logger.plain('=' * 74)
for layerdir in self.bblayers:
@@ -126,7 +120,7 @@ Options:
recipes with the ones they overlay indented underneath
-s only list overlayed recipes where the version is the same
"""
self.init_bbhandler()
self.bbhandler.prepare()
show_filenames = False
show_same_ver_only = False
@@ -209,7 +203,7 @@ Options:
-m only list where multiple recipes (in the same layer or different
layers) exist for the same recipe name
"""
self.init_bbhandler()
self.bbhandler.prepare()
show_filenames = False
show_multi_provider_only = False
@@ -347,7 +341,7 @@ build results (as the layer priority order has effectively changed).
logger.error('Directory %s exists and is non-empty, please clear it out first' % outputdir)
return
self.init_bbhandler()
self.bbhandler.prepare()
layers = self.bblayers
if len(arglist) > 2:
layernames = arglist[:-1]
@@ -503,7 +497,7 @@ usage: show-appends
Recipes are listed with the bbappends that apply to them as subitems.
"""
self.init_bbhandler()
self.bbhandler.prepare()
if not self.bbhandler.cooker.collection.appendlist:
logger.plain('No append files found')
return
@@ -556,7 +550,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
continue
basename = os.path.basename(filename)
appends = self.bbhandler.cooker.collection.get_file_appends(basename)
appends = self.bbhandler.cooker.collection.appendlist.get(basename)
if appends:
appended.append((basename, list(appends)))
else:
@@ -566,29 +560,26 @@ Recipes are listed with the bbappends that apply to them as subitems.
def do_show_cross_depends(self, args):
"""figure out the dependency between recipes that crosses a layer boundary.
usage: show-cross-depends [-f] [-i layer1[,layer2[,layer3...]]]
usage: show-cross-depends [-f]
Figure out the dependency between recipes that crosses a layer boundary.
Options:
-f show full file path
-i ignore dependencies on items in the specified layer(s)
NOTE:
The .bbappend file can impact the dependency.
"""
import optparse
self.bbhandler.prepare()
parser = optparse.OptionParser(usage="show-cross-depends [-f] [-i layer1[,layer2[,layer3...]]]")
parser.add_option("-f", "",
action="store_true", dest="show_filenames")
parser.add_option("-i", "",
action="store", dest="ignore_layers", default="")
options, args = parser.parse_args(sys.argv)
ignore_layers = options.ignore_layers.split(',')
self.init_bbhandler()
show_filenames = False
for arg in args.split():
if arg == '-f':
show_filenames = True
else:
sys.stderr.write("show-cross-depends: invalid option %s\n" % arg)
self.do_help('')
return
pkg_fn = self.bbhandler.cooker_data.pkg_fn
bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
@@ -596,8 +587,6 @@ The .bbappend file can impact the dependency.
self.include_re = re.compile(r"include\s+(.+)")
self.inherit_re = re.compile(r"inherit\s+(.+)")
global_inherit = (self.bbhandler.config_data.getVar('INHERIT', True) or "").split()
# The bb's DEPENDS and RDEPENDS
for f in pkg_fn:
f = bb.cache.Cache.virtualfn2realfn(f)[0]
@@ -612,7 +601,7 @@ The .bbappend file can impact the dependency.
self.bbhandler.config_data,
self.bbhandler.cooker_data,
self.bbhandler.cooker_data.pkg_pn)
self.check_cross_depends("DEPENDS", layername, f, best[3], options.show_filenames, ignore_layers)
self.check_cross_depends("DEPENDS", layername, f, best[3], show_filenames)
# The RDPENDS
all_rdeps = self.bbhandler.cooker_data.rundeps[f].values()
@@ -626,33 +615,10 @@ The .bbappend file can impact the dependency.
for rdep in all_rdeps:
all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep)
if all_p:
if f in all_p:
# The recipe provides this one itself, ignore
continue
best = bb.providers.filterProvidersRunTime(all_p, rdep,
self.bbhandler.config_data,
self.bbhandler.cooker_data)[0][0]
self.check_cross_depends("RDEPENDS", layername, f, best, options.show_filenames, ignore_layers)
# The RRECOMMENDS
all_rrecs = self.bbhandler.cooker_data.runrecs[f].values()
# Remove the duplicated or null one.
sorted_rrecs = {}
# The all_rrecs is the list in list, so we need two for loops
for k1 in all_rrecs:
for k2 in k1:
sorted_rrecs[k2] = 1
all_rrecs = sorted_rrecs.keys()
for rrec in all_rrecs:
all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rrec)
if all_p:
if f in all_p:
# The recipe provides this one itself, ignore
continue
best = bb.providers.filterProvidersRunTime(all_p, rrec,
self.bbhandler.config_data,
self.bbhandler.cooker_data)[0][0]
self.check_cross_depends("RRECOMMENDS", layername, f, best, options.show_filenames, ignore_layers)
self.check_cross_depends("RDEPENDS", layername, f, best, show_filenames)
# The inherit class
cls_re = re.compile('classes/')
@@ -662,12 +628,9 @@ The .bbappend file can impact the dependency.
# The inherits' format is [classes/cls, /path/to/classes/cls]
# ignore the classes/cls.
if not cls_re.match(cls):
classname = os.path.splitext(os.path.basename(cls))[0]
if classname in global_inherit:
continue
inherit_layername = self.get_file_layer(cls)
if inherit_layername != layername and not inherit_layername in ignore_layers:
if not options.show_filenames:
if inherit_layername != layername:
if not show_filenames:
f_short = self.remove_layer_prefix(f)
cls = self.remove_layer_prefix(cls)
else:
@@ -687,7 +650,7 @@ The .bbappend file can impact the dependency.
if pv_re.search(needed_file) and f in self.bbhandler.cooker_data.pkg_pepvpr:
pv = self.bbhandler.cooker_data.pkg_pepvpr[f][1]
needed_file = re.sub(r"\${PV}", pv, needed_file)
self.print_cross_files(bbpath, keyword, layername, f, needed_file, options.show_filenames, ignore_layers)
self.print_cross_files(bbpath, keyword, layername, f, needed_file, show_filenames)
line = fnfile.readline()
fnfile.close()
@@ -714,22 +677,21 @@ The .bbappend file can impact the dependency.
bbclass=".bbclass"
# Find a 'require/include xxxx'
if m:
self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, options.show_filenames, ignore_layers)
self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, show_filenames)
line = ffile.readline()
ffile.close()
def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames, ignore_layers):
def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames):
"""Print the depends that crosses a layer boundary"""
needed_file = bb.utils.which(bbpath, needed_filename)
if needed_file:
# Which layer is this file from
needed_layername = self.get_file_layer(needed_file)
if needed_layername != layername and not needed_layername in ignore_layers:
if needed_layername != layername:
if not show_filenames:
f = self.remove_layer_prefix(f)
needed_file = self.remove_layer_prefix(needed_file)
logger.plain("%s %s %s" %(f, keyword, needed_file))
def match_inherit(self, line):
"""Match the inherit xxx line"""
return (self.inherit_re.match(line), "inherits")
@@ -743,11 +705,11 @@ The .bbappend file can impact the dependency.
keyword = "includes"
return (m, keyword)
def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames, ignore_layers):
def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames):
"""Print the DEPENDS/RDEPENDS file that crosses a layer boundary"""
best_realfn = bb.cache.Cache.virtualfn2realfn(needed_file)[0]
needed_layername = self.get_file_layer(best_realfn)
if needed_layername != layername and not needed_layername in ignore_layers:
if needed_layername != layername:
if not show_filenames:
f = self.remove_layer_prefix(f)
best_realfn = self.remove_layer_prefix(best_realfn)

View File

@@ -25,24 +25,13 @@ try:
except RuntimeError as exc:
sys.exit(str(exc))
def usage():
print('usage: %s [testname1 [testname2]...]' % os.path.basename(sys.argv[0]))
if len(sys.argv) > 1:
if '--help' in sys.argv[1:]:
usage()
sys.exit(0)
tests = sys.argv[1:]
else:
tests = ["bb.tests.codeparser",
"bb.tests.cow",
"bb.tests.data",
"bb.tests.fetch",
"bb.tests.utils"]
tests = ["bb.tests.codeparser",
"bb.tests.cow",
"bb.tests.data",
"bb.tests.fetch",
"bb.tests.utils"]
for t in tests:
t = '.'.join(t.split('.')[:3])
__import__(t)
unittest.main(argv=["bitbake-selftest"] + tests)

View File

@@ -12,18 +12,10 @@ import errno
import signal
# Users shouldn't be running this code directly
if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
if len(sys.argv) != 2 or sys.argv[1] != "decafbad":
print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
sys.exit(1)
profiling = False
if sys.argv[1] == "decafbadbad":
profiling = True
try:
import cProfile as profile
except:
import profile
logger = logging.getLogger("BitBake")
try:
@@ -89,12 +81,7 @@ def workerlog_write(msg):
lf.write(msg)
lf.flush()
def sigterm_handler(signum, frame):
signal.signal(signal.SIGTERM, signal.SIG_DFL)
os.killpg(0, signal.SIGTERM)
sys.exit()
def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdata, quieterrors=False):
def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, quieterrors=False):
# We need to setup the environment BEFORE the fork, since
# a fork() or exec*() activates PSEUDO...
@@ -110,8 +97,7 @@ def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdat
except TypeError:
umask = taskdep['umask'][taskname]
# We can't use the fakeroot environment in a dry run as it possibly hasn't been built
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not cfg.dry_run:
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
envvars = (workerdata["fakerootenv"][fn] or "").split()
for key, value in (var.split('=') for var in envvars):
envbackup[key] = os.environ.get(key)
@@ -142,25 +128,17 @@ def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdat
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
if pid == 0:
def child():
global worker_pipe
pipein.close()
signal.signal(signal.SIGTERM, sigterm_handler)
# Let SIGHUP exit as SIGTERM
signal.signal(signal.SIGHUP, sigterm_handler)
# Save out the PID so that the event can include it the
# events
bb.event.worker_pid = os.getpid()
bb.event.worker_fire = worker_child_fire
worker_pipe = pipeout
# Make the child the process group leader and ensure no
# child process will be controlled by the current terminal
# This ensures signals sent to the controlling terminal like Ctrl+C
# don't stop the child processes.
os.setsid()
# Make the child the process group leader
os.setpgid(0, 0)
# No stdin
newsi = os.open(os.devnull, os.O_RDWR)
os.dup2(newsi, sys.stdin.fileno())
@@ -169,15 +147,18 @@ def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdat
os.umask(umask)
data.setVar("BB_WORKERCONTEXT", "1")
data.setVar("BB_TASKDEPDATA", taskdepdata)
data.setVar("BUILDNAME", workerdata["buildname"])
data.setVar("DATE", workerdata["date"])
data.setVar("TIME", workerdata["time"])
bb.parse.siggen.set_taskdata(workerdata["sigdata"])
bb.parse.siggen.set_taskdata(workerdata["hashes"], workerdata["hash_deps"], workerdata["sigchecksums"])
ret = 0
try:
the_data = bb.cache.Cache.loadDataFull(fn, appends, data)
the_data.setVar('BB_TASKHASH', workerdata["runq_hash"][task])
for h in workerdata["hashes"]:
the_data.setVar("BBHASH_%s" % h, workerdata["hashes"][h])
for h in workerdata["hash_deps"]:
the_data.setVar("BBHASHDEPS_%s" % h, workerdata["hash_deps"][h])
# exported_vars() returns a generator which *cannot* be passed to os.environ.update()
# successfully. We also need to unset anything from the environment which shouldn't be there
@@ -198,22 +179,11 @@ def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdat
logger.critical(str(exc))
os._exit(1)
try:
if cfg.dry_run:
return 0
return bb.build.exec_task(fn, taskname, the_data, cfg.profile)
if not cfg.dry_run:
ret = bb.build.exec_task(fn, taskname, the_data, cfg.profile)
os._exit(ret)
except:
os._exit(1)
if not profiling:
os._exit(child())
else:
profname = "profile-%s.log" % (fn.replace("/", "-") + "-" + taskname)
prof = profile.Profile()
try:
ret = profile.Profile.runcall(prof, child)
finally:
prof.dump_stats(profname)
bb.utils.process_profilelog(profname)
os._exit(ret)
else:
for key, value in envbackup.iteritems():
if value is None:
@@ -270,19 +240,6 @@ class BitbakeWorker(object):
self.build_pids = {}
self.build_pipes = {}
signal.signal(signal.SIGTERM, self.sigterm_exception)
# Let SIGHUP exit as SIGTERM
signal.signal(signal.SIGHUP, self.sigterm_exception)
def sigterm_exception(self, signum, stackframe):
if signum == signal.SIGTERM:
bb.warn("Worker recieved SIGTERM, shutting down...")
elif signum == signal.SIGHUP:
bb.warn("Worker recieved SIGHUP, shutting down...")
self.handle_finishnow(None)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
os.kill(os.getpid(), signal.SIGTERM)
def serve(self):
while True:
(ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
@@ -342,10 +299,10 @@ class BitbakeWorker(object):
sys.exit(0)
def handle_runtask(self, data):
fn, task, taskname, quieterrors, appends, taskdepdata = pickle.loads(data)
fn, task, taskname, quieterrors, appends = pickle.loads(data)
workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.workerdata, fn, task, taskname, appends, taskdepdata, quieterrors)
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.workerdata, fn, task, taskname, appends, quieterrors)
self.build_pids[pid] = task
self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
@@ -393,16 +350,7 @@ class BitbakeWorker(object):
try:
worker = BitbakeWorker(sys.stdin)
if not profiling:
worker.serve()
else:
profname = "profile-worker.log"
prof = profile.Profile()
try:
profile.Profile.runcall(prof, worker.serve)
finally:
prof.dump_stats(profname)
bb.utils.process_profilelog(profname)
worker.serve()
except BaseException as e:
if not normalexit:
import traceback

View File

@@ -16,15 +16,9 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# This script can be run in two modes.
# When used with "source", from a build directory,
# it enables toaster event logging and starts the bitbake resident server.
# use as: source toaster [start|stop] [noweb] [noui]
# When it is called as a stand-alone script, it starts just the
# web server, and the building shall be done through the web interface.
# As script, it will not return to the command prompt. Stop with Ctrl-C.
# This script enables toaster event logging and
# starts bitbake resident server
# use as: source toaster [start|stop]
# Helper function to kill a background toaster development server
@@ -36,8 +30,6 @@ function webserverKillAll()
while kill -0 $(< ${pidfile}) 2>/dev/null; do
kill -SIGTERM -$(< ${pidfile}) 2>/dev/null
sleep 1;
# Kill processes if they are still running - may happen in interactive shells
ps fux | grep "python.*manage.py" | awk '{print $2}' | xargs kill
done;
rm ${pidfile}
fi
@@ -46,35 +38,12 @@ function webserverKillAll()
function webserverStartAll()
{
# do not start if toastermain points to a valid process
if ! cat "${BUILDDIR}/.toastermain.pid" 2>/dev/null | xargs -I{} kill -0 {} ; then
retval=1
rm "${BUILDDIR}/.toastermain.pid"
fi
retval=0
python $BBBASEDIR/lib/toaster/manage.py syncdb || retval=1
python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=2
if [ $retval -eq 1 ]; then
echo "Failed db sync, stopping system start" 1>&2
elif [ $retval -eq 2 ]; then
echo -e "\nError on migration, trying to recover... \n"
python $BBBASEDIR/lib/toaster/manage.py migrate orm 0001_initial --fake
retval=0
python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1
fi
if [ "x$TOASTER_MANAGED" == "x1" ]; then
python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
python $BBBASEDIR/lib/toaster/manage.py checksettings --traceback || retval=1
fi
if [ $retval -eq 0 ]; then
echo "Starting webserver"
else
python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
sleep 1
if ! cat "${BUILDDIR}/.toastermain.pid" | xargs -I{} kill -0 {} ; then
retval=1
rm "${BUILDDIR}/.toastermain.pid"
fi
fi
return $retval
}
@@ -87,100 +56,42 @@ function addtoConfiguration()
echo $1 >> ${BUILDDIR}/conf/$2
}
INSTOPSYSTEM=0
# define the stop command
function stop_system()
{
# prevent reentry
if [ $INSTOPSYSTEM == 1 ]; then return; fi
INSTOPSYSTEM=1
if [ -f ${BUILDDIR}/.toasterui.pid ]; then
kill $(< ${BUILDDIR}/.toasterui.pid ) 2>/dev/null
kill $(< ${BUILDDIR}/.toasterui.pid )
rm ${BUILDDIR}/.toasterui.pid
fi
BBSERVER=0.0.0.0:8200 bitbake -m
BBSERVER=localhost:8200 bitbake -m
unset BBSERVER
webserverKillAll
# force stop any misbehaving bitbake server
lsof bitbake.lock | awk '{print $2}' | grep "[0-9]\+" | xargs -n1 -r kill
trap - SIGHUP
#trap - SIGCHLD
INSTOPSYSTEM=0
}
function check_pidbyfile() {
[ -e $1 ] && kill -0 $(< $1) 2>/dev/null
}
function notify_chldexit() {
if [ $NOTOASTERUI == 0 ]; then
check_pidbyfile ${BUILDDIR}/.toasterui.pid && return
stop_system
fi
}
BBBASEDIR=`dirname ${BASH_SOURCE}`/..
RUNNING=0
if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; then
# We are called as standalone. We refuse to run in a build environment - we need the interactive mode for that.
# Start just the web server, point the web browser to the interface, and start any Django services.
if [ -n "$BUILDDIR" ]; then
echo -e "Error: build/ directory detected. Toaster will not start in managed mode if a build environment is detected.\nUse a clean terminal to start Toaster." 1>&2;
exit 1;
fi
# Define a fake builddir where only the pid files are actually created. No real builds will take place here.
BUILDDIR=/tmp
RUNNING=1
function trap_ctrlc() {
echo "** Stopping system"
webserverKillAll
RUNNING=0
}
TOASTER_MANAGED=1
export TOASTER_MANAGED=1
if ! webserverStartAll; then
echo "Failed to start the web server, stopping" 1>&2;
exit 1;
fi
xdg-open http://0.0.0.0:8000/ >/dev/null 2>&1 &
trap trap_ctrlc SIGINT
echo "Running. Stop with Ctrl-C"
while [ $RUNNING -gt 0 ]; do
python $BBBASEDIR/lib/toaster/manage.py runbuilds
sleep 1
done
echo "**** Exit"
exit 0
fi
# We make sure we're running in the current shell and in a good environment
if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; then
echo "Error: This script needs to be sourced. Please run as 'source toaster [start|stop]'" 1>&2;
exit 1
fi
if [ -z "$BUILDDIR" ] || [ -z `which bitbake` ]; then
echo "Error: Build environment is not setup or bitbake is not in path." 1>&2;
return 2
fi
BBBASEDIR=`dirname ${BASH_SOURCE}`/..
# Verify prerequisites
if ! echo "import django; print (1,) == django.VERSION[0:1] and django.VERSION[1:2][0] in (5,6)" | python 2>/dev/null | grep True >/dev/null; then
echo -e "This program needs Django 1.5 or 1.6. Please install with\n\npip install django==1.6"
if ! echo "import django; print (1,4,5) == django.VERSION[0:3]" | python 2>/dev/null | grep True >/dev/null; then
echo -e "This program needs Django 1.4.5. Please install with\n\nsudo pip install django==1.4.5"
return 2
fi
if ! echo "import south; print [0,8,4] == map(int,south.__version__.split(\".\"))" | python 2>/dev/null | grep True >/dev/null; then
echo -e "This program needs South 0.8.4. Please install with\n\npip install south==0.8.4"
return 2
fi
# Determine the action. If specified by arguments, fine, if not, toggle it
@@ -195,17 +106,9 @@ else
fi
NOTOASTERUI=0
WEBSERVER=1
for param in $*; do
case $param in
noui )
NOTOASTERUI=1
;;
noweb )
WEBSERVER=0
;;
esac
done
if [ "x$2" == "xnoui" ]; then
NOTOASTERUI=1
fi
echo "The system will $CMD."
@@ -217,51 +120,45 @@ if [ -e $BUILDDIR/bitbake.lock ]; then
fi
if [ ${CMD} == "start" ] && ( [ $lock -eq 0 ] || [ -e $BUILDDIR/.toastermain.pid ] ); then
echo "Error: bitbake lock state error. File locks show that the system is on." 2>&1
echo "If you see problems, stop and then start the system again." 2>&1
echo "Error: bitbake lock state error. System is already on." 2>&1
return 3
elif [ ${CMD} == "stop" ] && ( [ $lock -eq 1 ] || ! [ -e $BUILDDIR/.toastermain.pid ] ) ; then
echo "Error: bitbake lock state error. Trying to stop a stopped system ?
If you think the system is hanged up, you can try to manually stop system with the commands
# BBSERVER=localhost:8200 bitbake -m
and
# webserverKillAll
" 2>&1
return 3
fi
# Execute the commands
case $CMD in
start )
start_success=1
addtoConfiguration "INHERIT+=\"toaster buildhistory\"" toaster.conf
if [ $WEBSERVER -gt 0 ] && ! webserverStartAll; then
echo "Failed ${CMD}."
return 4
fi
webserverStartAll || return 4
unset BBSERVER
bitbake --postread conf/toaster.conf --server-only -t xmlrpc -B 0.0.0.0:8200
if [ $? -ne 0 ]; then
start_success=0
echo "Bitbake server start failed"
else
export BBSERVER=0.0.0.0:8200
if [ $NOTOASTERUI == 0 ]; then # we start the TOASTERUI only if not inhibited
bitbake --observe-only -u toasterui >${BUILDDIR}/toaster_ui.log 2>&1 & echo $! >${BUILDDIR}/.toasterui.pid
fi
fi
if [ $start_success -eq 1 ]; then
# set fail safe stop system on terminal exit
trap stop_system SIGHUP
echo "Successful ${CMD}."
else
# failed start, do stop
stop_system
echo "Failed ${CMD}."
bitbake --postread conf/toaster.conf --server-only -t xmlrpc -B localhost:8200
export BBSERVER=localhost:8200
if [ $NOTOASTERUI == 0 ]; then # we start the TOASTERUI only if not inhibited
bitbake --observe-only -u toasterui >${BUILDDIR}/toaster_ui.log 2>&1 & echo $! >${BUILDDIR}/.toasterui.pid
fi
# stop system on terminal exit
set -o monitor
trap stop_system SIGHUP
#trap notify_chldexit SIGCHLD
;;
stop )
stop_system
echo "Successful ${CMD}."
trap '' SIGHUP
;;
esac
echo "Successful ${CMD}."

View File

@@ -17,7 +17,7 @@ endif
fun! <SID>GetUserName()
let l:user_name = system("git config --get user.name")
if v:shell_error
return "Unknown User"
return "Unknow User"
else
return substitute(l:user_name, "\n", "", "")
endfun
@@ -53,6 +53,7 @@ fun! NewBBTemplate()
put ='LICENSE = \"\"'
put ='SECTION = \"\"'
put ='DEPENDS = \"\"'
put ='PR = \"r0\"'
put =''
put ='SRC_URI = \"\"'

View File

@@ -1,91 +0,0 @@
# This is a single Makefile to handle all generated BitBake documents.
# The Makefile needs to live in the documentation directory and all figures used
# in any manuals must be .PNG files and live in the individual book's figures
# directory.
#
# The Makefile has these targets:
#
# pdf: generates a PDF version of a manual.
# html: generates an HTML version of a manual.
# tarball: creates a tarball for the doc files.
# validate: validates
# clean: removes files
#
# The Makefile generates an HTML and PDF version of every document. The
# variable DOC indicates the folder name for a given manual.
#
# To build a manual, you must invoke 'make' with the DOC argument.
#
# Examples:
#
# make DOC=bitbake-user-manual
# make pdf DOC=bitbake-user-manual
#
# The first example generates the HTML and PDF versions of the User Manual.
# The second example generates the HTML version only of the User Manual.
#
ifeq ($(DOC),bitbake-user-manual)
XSLTOPTS = --stringparam html.stylesheet bitbake-user-manual-style.css \
--stringparam chapter.autolabel 1 \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--xinclude
ALLPREQ = html pdf tarball
TARFILES = bitbake-user-manual-style.css bitbake-user-manual.html bitbake-user-manual.pdf figures/bitbake-title.png
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
FIGURES = figures
STYLESHEET = $(DOC)/*.css
endif
##
# 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
all: $(ALLPREQ)
pdf:
ifeq ($(DOC),bitbake-user-manual)
@echo " "
@echo "********** Building."$(DOC)
@echo " "
cd $(DOC); ../tools/docbook-to-pdf $(DOC).xml ../template; cd ..
endif
html:
ifeq ($(DOC),bitbake-user-manual)
# See http://www.sagehill.net/docbookxsl/HtmlOutput.html
@echo " "
@echo "******** Building "$(DOC)
@echo " "
cd $(DOC); xsltproc $(XSLTOPTS) -o $(DOC).html $(DOC)-customization.xsl $(DOC).xml; cd ..
endif
tarball: html
@echo " "
@echo "******** Creating Tarball of document files"
@echo " "
cd $(DOC); tar -cvzf $(DOC).tgz $(TARFILES); cd ..
validate:
cd $(DOC); xmllint --postvalid --xinclude --noout $(DOC).xml; cd ..
publish:
@if test -f $(DOC)/$(DOC).html; \
then \
echo " "; \
echo "******** Publishing "$(DOC)".html"; \
echo " "; \
scp -r $(MANUALS) $(STYLESHEET) docs.yp:/var/www/www.yoctoproject.org-docs/$(VER)/$(DOC); \
cd $(DOC); scp -r $(FIGURES) docs.yp:/var/www/www.yoctoproject.org-docs/$(VER)/$(DOC); \
else \
echo " "; \
echo $(DOC)".html missing. Generate the file first then try again."; \
echo " "; \
fi
clean:
rm -rf $(MANUALS); rm $(DOC)/$(DOC).tgz;

View File

@@ -1,39 +0,0 @@
Documentation
=============
This is the directory that contains the BitBake documentation.
Manual Organization
===================
Folders exist for individual manuals as follows:
* bitbake-user-manual - The BitBake User Manual
Each folder is self-contained regarding content and figures.
If you want to find HTML versions of the BitBake manuals on the web,
go to http://www.openembedded.org/wiki/Documentation.
Makefile
========
The Makefile processes manual directories to create HTML, PDF,
tarballs, etc. Details on how the Makefile work are documented
inside the Makefile. See that file for more information.
To build a manual, you run the make command and pass it the name
of the folder containing the manual's contents.
For example, the following command run from the documentation directory
creates an HTML and a PDF version of the BitBake User Manual.
The DOC variable specifies the manual you are making:
$ make DOC=bitbake-user-manual
template
========
Contains various templates, fonts, and some old PNG files.
tools
=====
Contains a tool to convert the DocBook files to PDF format.

View File

@@ -1,21 +0,0 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" />
<xsl:include href="../template/permalinks.xsl"/>
<xsl:include href="../template/section.title.xsl"/>
<xsl:include href="../template/component.title.xsl"/>
<xsl:include href="../template/division.title.xsl"/>
<xsl:include href="../template/formal.object.heading.xsl"/>
<xsl:include href="../template/gloss-permalinks.xsl"/>
<xsl:param name="html.stylesheet" select="'user-manual-style.css'" />
<xsl:param name="chapter.autolabel" select="1" />
<xsl:param name="section.autolabel" select="1" />
<xsl:param name="section.label.includes.component.label" select="1" />
<xsl:param name="appendix.autolabel">A</xsl:param>
<!-- <xsl:param name="generate.toc" select="'article nop'"></xsl:param> -->
</xsl:stylesheet>

View File

@@ -1,910 +0,0 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<chapter id="bitbake-user-manual-execution">
<title>Execution</title>
<para>
The primary purpose for running BitBake is to produce some kind
of output such as a single installable package, a kernel, a software
development kit, or even a full, board-specific bootable Linux image,
complete with bootloader, kernel, and root filesystem.
Of course, you can execute the <filename>bitbake</filename>
command with options that cause it to execute single tasks,
compile single recipe files, capture or clear data, or simply
return information about the execution environment.
</para>
<para>
This chapter describes BitBake's execution process from start
to finish when you use it to create an image.
The execution process is launched using the following command
form:
<literallayout class='monospaced'>
$ bitbake &lt;target&gt;
</literallayout>
For information on the BitBake command and its options,
see
"<link linkend='bitbake-user-manual-command'>The BitBake Command</link>"
section.
<note>
<para>
Prior to executing BitBake, you should take advantage of available
parallel thread execution on your build host by setting the
<link linkend='var-BB_NUMBER_THREADS'><filename>BB_NUMBER_THREADS</filename></link>
variable in your project's <filename>local.conf</filename>
configuration file.
</para>
<para>
A common way to determine this value for your build host is to run:
<literallayout class='monospaced'>
$ grep processor /proc/cpuinfo
</literallayout>
and count the number of processors displayed. Note that the number of
processors will take into account hyper-threading, so that a quad-core
build host with hyper-threading will most likely show eight processors,
which is the value you would then assign to that variable.
</para>
<para>
A possibly simpler solution is that some Linux distributions
(e.g. Debian and Ubuntu) provide the <filename>ncpus</filename> command.
</para>
</note>
</para>
<section id='parsing-the-base-configuration-metadata'>
<title>Parsing the Base Configuration Metadata</title>
<para>
The first thing BitBake does is parse base configuration
metadata.
Base configuration metadata consists of your project's
<filename>bblayers.conf</filename> file to determine what
layers BitBake needs to recognize, all necessary
<filename>layer.conf</filename> files (one from each layer),
and <filename>bitbake.conf</filename>.
The data itself is of various types:
<itemizedlist>
<listitem><para><emphasis>Recipes:</emphasis>
Details about particular pieces of software.
</para></listitem>
<listitem><para><emphasis>Class Data:</emphasis>
An abstraction of common build information
(e.g. how to build a Linux kernel).
</para></listitem>
<listitem><para><emphasis>Configuration Data:</emphasis>
Machine-specific settings, policy decisions,
and so forth.
Configuration data acts as the glue to bind everything
together.</para></listitem>
</itemizedlist>
</para>
<para>
The <filename>layer.conf</filename> files are used to
construct key variables such as
<link linkend='var-BBPATH'><filename>BBPATH</filename></link>
and
<link linkend='var-BBFILES'><filename>BBFILES</filename></link>.
<filename>BBPATH</filename> is used to search for
configuration and class files under the
<filename>conf</filename> and <filename>classes</filename>
directories, respectively.
<filename>BBFILES</filename> is used to locate both recipe
and recipe append files
(<filename>.bb</filename> and <filename>.bbappend</filename>).
If there is no <filename>bblayers.conf</filename> file,
it is assumed the user has set the <filename>BBPATH</filename>
and <filename>BBFILES</filename> directly in the environment.
</para>
<para>
Next, the <filename>bitbake.conf</filename> file is located
using the <filename>BBPATH</filename> variable that was
just constructed.
The <filename>bitbake.conf</filename> file may also include other
configuration files using the
<filename>include</filename> or
<filename>require</filename> directives.
</para>
<para>
Prior to parsing configuration files, Bitbake looks
at certain variables, including:
<itemizedlist>
<listitem><para><link linkend='var-BB_ENV_WHITELIST'><filename>BB_ENV_WHITELIST</filename></link></para></listitem>
<listitem><para><link linkend='var-BB_PRESERVE_ENV'><filename>BB_PRESERVE_ENV</filename></link></para></listitem>
<listitem><para><link linkend='var-BB_ENV_EXTRAWHITE'><filename>BB_ENV_EXTRAWHITE</filename></link></para></listitem>
<listitem><para>
<link linkend='var-BITBAKE_UI'><filename>BITBAKE_UI</filename></link>
</para></listitem>
</itemizedlist>
You can find information on how to pass environment variables into the BitBake
execution environment in the
"<link linkend='passing-information-into-the-build-task-environment'>Passing Information Into the Build Task Environment</link>" section.
</para>
<para>
The base configuration metadata is global
and therefore affects all recipes and tasks that are executed.
</para>
<para>
BitBake first searches the current working directory for an
optional <filename>conf/bblayers.conf</filename> configuration file.
This file is expected to contain a
<link linkend='var-BBLAYERS'><filename>BBLAYERS</filename></link>
variable that is a space-delimited list of 'layer' directories.
Recall that if BitBake cannot find a <filename>bblayers.conf</filename>
file, then it is assumed the user has set the <filename>BBPATH</filename>
and <filename>BBFILES</filename> variables directly in the environment.
</para>
<para>
For each directory (layer) in this list, a <filename>conf/layer.conf</filename>
file is located and parsed with the
<link linkend='var-LAYERDIR'><filename>LAYERDIR</filename></link>
variable being set to the directory where the layer was found.
The idea is these files automatically set up
<link linkend='var-BBPATH'><filename>BBPATH</filename></link>
and other variables correctly for a given build directory.
</para>
<para>
BitBake then expects to find the <filename>conf/bitbake.conf</filename>
file somewhere in the user-specified <filename>BBPATH</filename>.
That configuration file generally has include directives to pull
in any other metadata such as files specific to the architecture,
the machine, the local environment, and so forth.
</para>
<para>
Only variable definitions and include directives are allowed
in BitBake <filename>.conf</filename> files.
Some variables directly influence BitBake's behavior.
These variables might have been set from the environment
depending on the environment variables previously
mentioned or set in the configuration files.
The
"<link linkend='ref-variables-glos'>Variables Glossary</link>"
chapter presents a full list of variables.
</para>
<para>
After parsing configuration files, BitBake uses its rudimentary
inheritance mechanism, which is through class files, to inherit
some standard classes.
BitBake parses a class when the inherit directive responsible
for getting that class is encountered.
</para>
<para>
The <filename>base.bbclass</filename> file is always included.
Other classes that are specified in the configuration using the
<link linkend='var-INHERIT'><filename>INHERIT</filename></link>
variable are also included.
BitBake searches for class files in a
<filename>classes</filename> subdirectory under
the paths in <filename>BBPATH</filename> in the same way as
configuration files.
</para>
<para>
A good way to get an idea of the configuration files and
the class files used in your execution environment is to
run the following BitBake command:
<literallayout class='monospaced'>
$ bitbake -e > mybb.log
</literallayout>
Examining the top of the <filename>mybb.log</filename>
shows you the many configuration files and class files
used in your execution environment.
</para>
<note>
<para>
You need to be aware of how BitBake parses curly braces.
If a recipe uses a closing curly brace within the function and
the character has no leading spaces, BitBake produces a parsing
error.
If you use a pair of curly braces in a shell function, the
closing curly brace must not be located at the start of the line
without leading spaces.
</para>
<para>
Here is an example that causes BitBake to produce a parsing
error:
<literallayout class='monospaced'>
fakeroot create_shar() {
cat &lt;&lt; "EOF" &gt; ${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.sh
usage()
{
echo "test"
###### The following "}" at the start of the line causes a parsing error ######
}
EOF
}
</literallayout>
Writing the recipe this way avoids the error:
<literallayout class='monospaced'>
fakeroot create_shar() {
cat &lt;&lt; "EOF" &gt; ${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.sh
usage()
{
echo "test"
######The following "}" with a leading space at the start of the line avoids the error ######
}
EOF
}
</literallayout>
</para>
</note>
</section>
<section id='locating-and-parsing-recipes'>
<title>Locating and Parsing Recipes</title>
<para>
During the configuration phase, BitBake will have set
<link linkend='var-BBFILES'><filename>BBFILES</filename></link>.
BitBake now uses it to construct a list of recipes to parse,
along with any append files (<filename>.bbappend</filename>)
to apply.
<filename>BBFILES</filename> is a space-separated list of
available files and supports wildcards.
An example would be:
<literallayout class='monospaced'>
BBFILES = "/path/to/bbfiles/*.bb /path/to/appends/*.bbappend"
</literallayout>
BitBake parses each recipe and append file located
with <filename>BBFILES</filename> and stores the values of
various variables into the datastore.
<note>
Append files are applied in the order they are encountered in
<filename>BBFILES</filename>.
</note>
For each file, a fresh copy of the base configuration is
made, then the recipe is parsed line by line.
Any inherit statements cause BitBake to find and
then parse class files (<filename>.bbclass</filename>)
using
<link linkend='var-BBPATH'><filename>BBPATH</filename></link>
as the search path.
Finally, BitBake parses in order any append files found in
<filename>BBFILES</filename>.
</para>
<para>
One common convention is to use the recipe filename to define
pieces of metadata.
For example, in <filename>bitbake.conf</filename> the recipe
name and version are used to set the variables
<link linkend='var-PN'><filename>PN</filename></link> and
<link linkend='var-PV'><filename>PV</filename></link>:
<literallayout class='monospaced'>
PN = "${@bb.parse.BBHandler.vars_from_file(d.getVar('FILE'),d)[0] or 'defaultpkgname'}"
PV = "${@bb.parse.BBHandler.vars_from_file(d.getVar('FILE'),d)[1] or '1.0'}"
</literallayout>
In this example, a recipe called "something_1.2.3.bb" would set
<filename>PN</filename> to "something" and
<filename>PV</filename> to "1.2.3".
</para>
<para>
By the time parsing is complete for a recipe, BitBake
has a list of tasks that the recipe defines and a set of
data consisting of keys and values as well as
dependency information about the tasks.
</para>
<para>
BitBake does not need all of this information.
It only needs a small subset of the information to make
decisions about the recipe.
Consequently, BitBake caches the values in which it is
interested and does not store the rest of the information.
Experience has shown it is faster to re-parse the metadata than to
try and write it out to the disk and then reload it.
</para>
<para>
Where possible, subsequent BitBake commands reuse this cache of
recipe information.
The validity of this cache is determined by first computing a
checksum of the base configuration data (see
<link linkend='var-BB_HASHCONFIG_WHITELIST'><filename>BB_HASHCONFIG_WHITELIST</filename></link>)
and then checking if the checksum matches.
If that checksum matches what is in the cache and the recipe
and class files have not changed, Bitbake is able to use
the cache.
BitBake then reloads the cached information about the recipe
instead of reparsing it from scratch.
</para>
<para>
Recipe file collections exist to allow the user to
have multiple repositories of
<filename>.bb</filename> files 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.
Here is an example:
<literallayout class='monospaced'>
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"
</literallayout>
<note>
The layers mechanism is now the preferred method of collecting
code.
While the collections code remains, its main use is to set layer
priorities and to deal with overlap (conflicts) between layers.
</note>
</para>
</section>
<section id='bb-bitbake-providers'>
<title>Providers</title>
<para>
Assuming BitBake has been instructed to execute a target
and that all the recipe files have been parsed, BitBake
starts to figure out how to build the target.
BitBake looks through the <filename>PROVIDES</filename> list
for each of the recipes.
A <filename>PROVIDES</filename> list is the list of names by which
the recipe can be known.
Each recipe's <filename>PROVIDES</filename> list is created
implicitly through the recipe's
<link linkend='var-PN'><filename>PN</filename></link> variable
and explicitly through the recipe's
<link linkend='var-PROVIDES'><filename>PROVIDES</filename></link>
variable, which is optional.
</para>
<para>
When a recipe uses <filename>PROVIDES</filename>, that recipe's
functionality can be found under an alternative name or names other
than the implicit <filename>PN</filename> name.
As an example, suppose a recipe named <filename>keyboard_1.0.bb</filename>
contained the following:
<literallayout class='monospaced'>
PROVIDES += "fullkeyboard"
</literallayout>
The <filename>PROVIDES</filename> list for this recipe becomes
"keyboard", which is implicit, and "fullkeyboard", which is explicit.
Consequently, the functionality found in
<filename>keyboard_1.0.bb</filename> can be found under two
different names.
</para>
</section>
<section id='bb-bitbake-preferences'>
<title>Preferences</title>
<para>
The <filename>PROVIDES</filename> list is only part of the solution
for figuring out a target's recipes.
Because targets might have multiple providers, BitBake needs
to prioritize providers by determining provider preferences.
</para>
<para>
A common example in which a target has multiple providers
is "virtual/kernel", which is on the
<filename>PROVIDES</filename> list for each kernel recipe.
Each machine often selects the best kernel provider by using a
line similar to the following in the machine configuration file:
<literallayout class='monospaced'>
PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"
</literallayout>
The default
<link linkend='var-PREFERRED_PROVIDER'><filename>PREFERRED_PROVIDER</filename></link>
is the provider with the same name as the target.
Bitbake iterates through each target it needs to build and
resolves them and their dependencies using this process.
</para>
<para>
Understanding how providers are chosen is made complicated by the fact
that multiple versions might exist for a given provider.
BitBake defaults to the highest version of a provider.
Version comparisons are made using the same method as Debian.
You can use the
<link linkend='var-PREFERRED_VERSION'><filename>PREFERRED_VERSION</filename></link>
variable to specify a particular version.
You can influence the order by using the
<link linkend='var-DEFAULT_PREFERENCE'><filename>DEFAULT_PREFERENCE</filename></link>
variable.
</para>
<para>
By default, files have a preference of "0".
Setting <filename>DEFAULT_PREFERENCE</filename> to "-1" makes the
recipe unlikely to be used unless it is explicitly referenced.
Setting <filename>DEFAULT_PREFERENCE</filename> to "1" makes it
likely the recipe is used.
<filename>PREFERRED_VERSION</filename> overrides any
<filename>DEFAULT_PREFERENCE</filename> setting.
<filename>DEFAULT_PREFERENCE</filename> is often used to mark newer
and more experimental recipe versions until they have undergone
sufficient testing to be considered stable.
</para>
<para>
When there are multiple “versions” of a given recipe,
BitBake defaults to selecting the most recent
version, unless otherwise specified.
If the recipe in question has a
<link linkend='var-DEFAULT_PREFERENCE'><filename>DEFAULT_PREFERENCE</filename></link>
set lower than the other recipes (default is 0), then
it will not be selected.
This allows the person or persons maintaining
the repository of recipe files to specify
their preference for the default selected version.
Additionally, the user can specify their preferred version.
</para>
<para>
If the first recipe is named <filename>a_1.1.bb</filename>, then the
<link linkend='var-PN'><filename>PN</filename></link> variable
will be set to “a”, and the
<link linkend='var-PV'><filename>PV</filename></link>
variable will be set to 1.1.
</para>
<para>
Thus, if a recipe named <filename>a_1.2.bb</filename> exists, BitBake
will choose 1.2 by default.
However, if you define the following variable in a
<filename>.conf</filename> file that BitBake parses, you
can change that preference:
<literallayout class='monospaced'>
PREFERRED_VERSION_a = "1.1"
</literallayout>
</para>
<note>
<para>
It is common for a recipe to provide two versions -- a stable,
numbered (and preferred) version, and a version that is
automatically checked out from a source code repository that
is considered more "bleeding edge" but can be selected only
explicitly.
</para>
<para>
For example, in the OpenEmbedded codebase, there is a standard,
versioned recipe file for BusyBox,
<filename>busybox_1.22.1.bb</filename>,
but there is also a Git-based version,
<filename>busybox_git.bb</filename>, which explicitly contains the line
<literallayout class='monospaced'>
DEFAULT_PREFERENCE = "-1"
</literallayout>
to ensure that the numbered, stable version is always preferred
unless the developer selects otherwise.
</para>
</note>
</section>
<section id='bb-bitbake-dependencies'>
<title>Dependencies</title>
<para>
Each target BitBake builds consists of multiple tasks such as
<filename>fetch</filename>, <filename>unpack</filename>,
<filename>patch</filename>, <filename>configure</filename>,
and <filename>compile</filename>.
For best performance on multi-core systems, BitBake considers each
task as an independent
entity with its own set of dependencies.
</para>
<para>
Dependencies are defined through several variables.
You can find information about variables BitBake uses in
the <link linkend='ref-variables-glos'>Variables Glossary</link>
near the end of this manual.
At a basic level, it is sufficient to know that BitBake uses the
<link linkend='var-DEPENDS'><filename>DEPENDS</filename></link> and
<link linkend='var-RDEPENDS'><filename>RDEPENDS</filename></link> variables when
calculating dependencies.
</para>
<para>
For more information on how BitBake handles dependencies, see the
"<link linkend='dependencies'>Dependencies</link>" section.
</para>
</section>
<section id='ref-bitbake-tasklist'>
<title>The Task List</title>
<para>
Based on the generated list of providers and the dependency information,
BitBake can now calculate exactly what tasks it needs to run and in what
order it needs to run them.
The
"<link linkend='executing-tasks'>Executing Tasks</link>" section has more
information on how BitBake chooses which task to execute next.
</para>
<para>
The build now starts with BitBake forking off threads up to the limit set in the
<link linkend='var-BB_NUMBER_THREADS'><filename>BB_NUMBER_THREADS</filename></link>
variable.
BitBake continues to fork threads as long as there are tasks ready to run,
those tasks have all their dependencies met, and the thread threshold has not been
exceeded.
</para>
<para>
It is worth noting that you can greatly speed up the build time by properly setting
the <filename>BB_NUMBER_THREADS</filename> variable.
</para>
<para>
As each task completes, a timestamp is written to the directory specified by the
<link linkend='var-STAMP'><filename>STAMP</filename></link> variable.
On subsequent runs, BitBake looks in the build directory within
<filename>tmp/stamps</filename> and does not rerun
tasks that are already completed unless a timestamp is found to be invalid.
Currently, invalid timestamps are only considered on a per
recipe file basis.
So, for example, if the configure stamp has a timestamp greater than the
compile timestamp for a given target, then the compile task would rerun.
Running the compile task again, however, has no effect on other providers
that depend on that target.
</para>
<para>
The exact format of the stamps is partly configurable.
In modern versions of BitBake, a hash is appended to the
stamp so that if the configuration changes, the stamp becomes
invalid and the task is automatically rerun.
This hash, or signature used, is governed by the signature policy
that is configured (see the
"<link linkend='checksums'>Checksums (Signatures)</link>"
section for information).
It is also possible to append extra metadata to the stamp using
the "stamp-extra-info" task flag.
For example, OpenEmbedded uses this flag to make some tasks machine-specific.
</para>
<note>
Some tasks are marked as "nostamp" tasks.
No timestamp file is created when these tasks are run.
Consequently, "nostamp" tasks are always rerun.
</note>
<para>
For more information on tasks, see the
"<link linkend='tasks'>Tasks</link>" section.
</para>
</section>
<section id='executing-tasks'>
<title>Executing Tasks</title>
<para>
Tasks can be either a shell task or a Python task.
For shell tasks, BitBake writes a shell script to
<filename>${</filename><link linkend='var-T'><filename>T</filename></link><filename>}/run.do_taskname.pid</filename>
and then executes the script.
The generated shell script contains all the exported variables,
and the shell functions with all variables expanded.
Output from the shell script goes to the file
<filename>${T}/log.do_taskname.pid</filename>.
Looking at the expanded shell functions in the run file and
the output in the log files is a useful debugging technique.
</para>
<para>
For Python tasks, BitBake executes the task internally and logs
information to the controlling terminal.
Future versions of BitBake will write the functions to files
similar to the way shell tasks are handled.
Logging will be handled in a way similar to shell tasks as well.
</para>
<para>
The order in which BitBake runs the tasks is controlled by its
task scheduler.
It is possible to configure the scheduler and define custom
implementations for specific use cases.
For more information, see these variables that control the
behavior:
<itemizedlist>
<listitem><para>
<link linkend='var-BB_SCHEDULER'><filename>BB_SCHEDULER</filename></link>
</para></listitem>
<listitem><para>
<link linkend='var-BB_SCHEDULERS'><filename>BB_SCHEDULERS</filename></link>
</para></listitem>
</itemizedlist>
It is possible to have functions run before and after a task's main
function.
This is done using the "prefuncs" and "postfuncs" flags of the task
that lists the functions to run.
</para>
</section>
<section id='checksums'>
<title>Checksums (Signatures)</title>
<para>
A checksum is a unique signature of a task's inputs.
The signature of a task can be used to determine if a task
needs to be run.
Because it is a change in a task's inputs that triggers running
the task, BitBake needs to detect all the inputs to a given task.
For shell tasks, this turns out to be fairly easy because
BitBake generates a "run" shell script for each task and
it is possible to create a checksum that gives you a good idea of when
the task's data changes.
</para>
<para>
To complicate the problem, some things should not be included in
the checksum.
First, there is the actual specific build path of a given task -
the working directory.
It does not matter if the working directory changes because it should not
affect the output for target packages.
The simplistic approach for excluding the working directory is to set
it to some fixed value and create the checksum for the "run" script.
BitBake goes one step better and uses the
<link linkend='var-BB_HASHBASE_WHITELIST'><filename>BB_HASHBASE_WHITELIST</filename></link>
variable to define a list of variables that should never be included
when generating the signatures.
</para>
<para>
Another problem results from the "run" scripts containing functions that
might or might not get called.
The incremental build solution contains code that figures out dependencies
between shell functions.
This code is used to prune the "run" scripts down to the minimum set,
thereby alleviating this problem and making the "run" scripts much more
readable as a bonus.
</para>
<para>
So far we have solutions for shell scripts.
What about Python tasks?
The same approach applies even though these tasks are more difficult.
The process needs to figure out what variables a Python function accesses
and what functions it calls.
Again, the incremental build solution contains code that first figures out
the variable and function dependencies, and then creates a checksum for the data
used as the input to the task.
</para>
<para>
Like the working directory case, situations exist where dependencies
should be ignored.
For these cases, you can instruct the build process to ignore a dependency
by using a line like the following:
<literallayout class='monospaced'>
PACKAGE_ARCHS[vardepsexclude] = "MACHINE"
</literallayout>
This example ensures that the <filename>PACKAGE_ARCHS</filename> variable does not
depend on the value of <filename>MACHINE</filename>, even if it does reference it.
</para>
<para>
Equally, there are cases where we need to add dependencies BitBake
is not able to find.
You can accomplish this by using a line like the following:
<literallayout class='monospaced'>
PACKAGE_ARCHS[vardeps] = "MACHINE"
</literallayout>
This example explicitly adds the <filename>MACHINE</filename> variable as a
dependency for <filename>PACKAGE_ARCHS</filename>.
</para>
<para>
Consider a case with in-line Python, for example, where BitBake is not
able to figure out dependencies.
When running in debug mode (i.e. using <filename>-DDD</filename>), BitBake
produces output when it discovers something for which it cannot figure out
dependencies.
</para>
<para>
Thus far, this section has limited discussion to the direct inputs into a task.
Information based on direct inputs is referred to as the "basehash" in the
code.
However, there is still the question of a task's indirect inputs - the
things that were already built and present in the build directory.
The checksum (or signature) for a particular task needs to add the hashes
of all the tasks on which the particular task depends.
Choosing which dependencies to add is a policy decision.
However, the effect is to generate a master checksum that combines the basehash
and the hashes of the task's dependencies.
</para>
<para>
At the code level, there are a variety of ways both the basehash and the
dependent task hashes can be influenced.
Within the BitBake configuration file, we can give BitBake some extra information
to help it construct the basehash.
The following statement effectively results in a list of global variable
dependency excludes - variables never included in any checksum.
This example uses variables from OpenEmbedded to help illustrate
the concept:
<literallayout class='monospaced'>
BB_HASHBASE_WHITELIST ?= "TMPDIR FILE PATH PWD BB_TASKHASH BBPATH DL_DIR \
SSTATE_DIR THISDIR FILESEXTRAPATHS FILE_DIRNAME HOME LOGNAME SHELL TERM \
USER FILESPATH STAGING_DIR_HOST STAGING_DIR_TARGET COREBASE PRSERV_HOST \
PRSERV_DUMPDIR PRSERV_DUMPFILE PRSERV_LOCKDOWN PARALLEL_MAKE \
CCACHE_DIR EXTERNAL_TOOLCHAIN CCACHE CCACHE_DISABLE LICENSE_PATH SDKPKGSUFFIX"
</literallayout>
The previous example excludes the work directory, which is part of
<filename>TMPDIR</filename>.
</para>
<para>
The rules for deciding which hashes of dependent tasks to include through
dependency chains are more complex and are generally accomplished with a
Python function.
The code in <filename>meta/lib/oe/sstatesig.py</filename> shows two examples
of this and also illustrates how you can insert your own policy into the system
if so desired.
This file defines the two basic signature generators OpenEmbedded Core
uses: "OEBasic" and "OEBasicHash".
By default, there is a dummy "noop" signature handler enabled in BitBake.
This means that behavior is unchanged from previous versions.
<filename>OE-Core</filename> uses the "OEBasicHash" signature handler by default
through this setting in the <filename>bitbake.conf</filename> file:
<literallayout class='monospaced'>
BB_SIGNATURE_HANDLER ?= "OEBasicHash"
</literallayout>
The "OEBasicHash" <filename>BB_SIGNATURE_HANDLER</filename> is the same as the
"OEBasic" version but adds the task hash to the stamp files.
This results in any metadata change that changes the task hash, automatically
causing the task to be run again.
This removes the need to bump
<link linkend='var-PR'><filename>PR</filename></link>
values, and changes to metadata automatically ripple across the build.
</para>
<para>
It is also worth noting that the end result of these signature generators is to
make some dependency and hash information available to the build.
This information includes:
<itemizedlist>
<listitem><para><filename>BB_BASEHASH_task-&lt;taskname&gt;</filename>:
The base hashes for each task in the recipe.
</para></listitem>
<listitem><para><filename>BB_BASEHASH_&lt;filename:taskname&gt;</filename>:
The base hashes for each dependent task.
</para></listitem>
<listitem><para><filename>BBHASHDEPS_&lt;filename:taskname&gt;</filename>:
The task dependencies for each task.
</para></listitem>
<listitem><para><filename>BB_TASKHASH</filename>:
The hash of the currently running task.
</para></listitem>
</itemizedlist>
</para>
<para>
It is worth noting that BitBake's "-S" option lets you
debug Bitbake's processing of signatures.
The options passed to -S allow different debugging modes
to be used, either using BitBake's own debug functions
or possibly those defined in the metadata/signature handler
itself.
The simplest parameter to pass is "none", which causes a
set of signature information to be written out into
<filename>STAMP_DIR</filename>
corresponding to the targets specified.
The other currently available parameter is "printdiff",
which causes BitBake to try to establish the closest
signature match it can (e.g. in the sstate cache) and then
run <filename>bitbake-diffsigs</filename> over the matches
to determine the stamps and delta where these two
stamp trees diverge.
<note>
It is likely that future versions of BitBake will
provide other signature handlers triggered through
additional "-S" parameters.
</note>
</para>
<para>
You can find more information on checksum metadata in the
"<link linkend='task-checksums-and-setscene'>Task Checksums and Setscene</link>"
section.
</para>
</section>
<section id='setscene'>
<title>Setscene</title>
<para>
The setscene process enables BitBake to handle "pre-built" artifacts.
The ability to handle and reuse these artifacts allows BitBake
the luxury of not having to build something from scratch every time.
Instead, BitBake can use, when possible, existing build artifacts.
</para>
<para>
BitBake needs to have reliable data indicating whether or not an
artifact is compatible.
Signatures, described in the previous section, provide an ideal
way of representing whether an artifact is compatible.
If a signature is the same, an object can be reused.
</para>
<para>
If an object can be reused, the problem then becomes how to
replace a given task or set of tasks with the pre-built artifact.
BitBake solves the problem with the "setscene" process.
</para>
<para>
When BitBake is asked to build a given target, before building anything,
it first asks whether cached information is available for any of the
targets it's building, or any of the intermediate targets.
If cached information is available, BitBake uses this information instead of
running the main tasks.
</para>
<para>
BitBake first calls the function defined by the
<link linkend='var-BB_HASHCHECK_FUNCTION'><filename>BB_HASHCHECK_FUNCTION</filename></link>
variable with a list of tasks and corresponding
hashes it wants to build.
This function is designed to be fast and returns a list
of the tasks for which it believes in can obtain artifacts.
</para>
<para>
Next, for each of the tasks that were returned as possibilities,
BitBake executes a setscene version of the task that the possible
artifact covers.
Setscene versions of a task have the string "_setscene" appended to the
task name.
So, for example, the task with the name <filename>xxx</filename> has
a setscene task named <filename>xxx_setscene</filename>.
The setscene version of the task executes and provides the necessary
artifacts returning either success or failure.
</para>
<para>
As previously mentioned, an artifact can cover more than one task.
For example, it is pointless to obtain a compiler if you
already have the compiled binary.
To handle this, BitBake calls the
<link linkend='var-BB_SETSCENE_DEPVALID'><filename>BB_SETSCENE_DEPVALID</filename></link>
function for each successful setscene task to know whether or not it needs
to obtain the dependencies of that task.
</para>
<para>
Finally, after all the setscene tasks have executed, BitBake calls the
function listed in
<link linkend='var-BB_SETSCENE_VERIFY_FUNCTION'><filename>BB_SETSCENE_VERIFY_FUNCTION</filename></link>
with the list of tasks BitBake thinks has been "covered".
The metadata can then ensure that this list is correct and can
inform BitBake that it wants specific tasks to be run regardless
of the setscene result.
</para>
<para>
You can find more information on setscene metadata in the
"<link linkend='task-checksums-and-setscene'>Task Checksums and Setscene</link>"
section.
</para>
</section>
</chapter>

View File

@@ -1,738 +0,0 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<chapter>
<title>File Download Support</title>
<para>
BitBake's fetch module is a standalone piece of library code
that deals with the intricacies of downloading source code
and files from remote systems.
Fetching source code is one of the cornerstones of building software.
As such, this module forms an important part of BitBake.
</para>
<para>
The current fetch module is called "fetch2" and refers to the
fact that it is the second major version of the API.
The original version is obsolete and has been removed from the codebase.
Thus, in all cases, "fetch" refers to "fetch2" in this
manual.
</para>
<section id='the-download-fetch'>
<title>The Download (Fetch)</title>
<para>
BitBake takes several steps when fetching source code or files.
The fetcher codebase deals with two distinct processes in order:
obtaining the files from somewhere (cached or otherwise)
and then unpacking those files into a specific location and
perhaps in a specific way.
Getting and unpacking the files is often optionally followed
by patching.
Patching, however, is not covered by this module.
</para>
<para>
The code to execute the first part of this process, a fetch,
looks something like the following:
<literallayout class='monospaced'>
src_uri = (d.getVar('SRC_URI', True) or "").split()
fetcher = bb.fetch2.Fetch(src_uri, d)
fetcher.download()
</literallayout>
This code sets up an instance of the fetch class.
The instance uses a space-separated list of URLs from the
<link linkend='var-SRC_URI'><filename>SRC_URI</filename></link>
variable and then calls the <filename>download</filename>
method to download the files.
</para>
<para>
The instantiation of the fetch class is usually followed by:
<literallayout class='monospaced'>
rootdir = l.getVar('WORKDIR', True)
fetcher.unpack(rootdir)
</literallayout>
This code unpacks the downloaded files to the
specified by <filename>WORKDIR</filename>.
<note>
For convenience, the naming in these examples matches
the variables used by OpenEmbedded.
If you want to see the above code in action, examine
the OpenEmbedded class file <filename>base.bbclass</filename>.
</note>
The <filename>SRC_URI</filename> and <filename>WORKDIR</filename>
variables are not hardcoded into the fetcher, since those fetcher
methods can be (and are) called with different variable names.
In OpenEmbedded for example, the shared state (sstate) code uses
the fetch module to fetch the sstate files.
</para>
<para>
When the <filename>download()</filename> method is called,
BitBake tries to resolve the URLs by looking for source files
in a specific search order:
<itemizedlist>
<listitem><para><emphasis>Pre-mirror Sites:</emphasis>
BitBake first uses pre-mirrors to try and find source files.
These locations are defined using the
<link linkend='var-PREMIRRORS'><filename>PREMIRRORS</filename></link>
variable.
</para></listitem>
<listitem><para><emphasis>Source URI:</emphasis>
If pre-mirrors fail, BitBake uses the original URL (e.g from
<filename>SRC_URI</filename>).
</para></listitem>
<listitem><para><emphasis>Mirror Sites:</emphasis>
If fetch failures occur, BitBake next uses mirror locations as
defined by the
<link linkend='var-MIRRORS'><filename>MIRRORS</filename></link>
variable.
</para></listitem>
</itemizedlist>
</para>
<para>
For each URL passed to the fetcher, the fetcher
calls the submodule that handles that particular URL type.
This behavior can be the source of some confusion when you
are providing URLs for the <filename>SRC_URI</filename>
variable.
Consider the following two URLs:
<literallayout class='monospaced'>
http://git.yoctoproject.org/git/poky;protocol=git
git://git.yoctoproject.org/git/poky;protocol=http
</literallayout>
In the former case, the URL is passed to the
<filename>wget</filename> fetcher, which does not
understand "git".
Therefore, the latter case is the correct form since the
Git fetcher does know how to use HTTP as a transport.
</para>
<para>
Here are some examples that show commonly used mirror
definitions:
<literallayout class='monospaced'>
PREMIRRORS ?= "\
bzr://.*/.* http://somemirror.org/sources/ \n \
cvs://.*/.* http://somemirror.org/sources/ \n \
git://.*/.* http://somemirror.org/sources/ \n \
hg://.*/.* http://somemirror.org/sources/ \n \
osc://.*/.* http://somemirror.org/sources/ \n \
p4://.*/.* http://somemirror.org/sources/ \n \
svn://.*/.* http://somemirror.org/sources/ \n"
MIRRORS =+ "\
ftp://.*/.* http://somemirror.org/sources/ \n \
http://.*/.* http://somemirror.org/sources/ \n \
https://.*/.* http://somemirror.org/sources/ \n"
</literallayout>
It is useful to note that BitBake supports
cross-URLs.
It is possible to mirror a Git repository on an HTTP
server as a tarball.
This is what the <filename>git://</filename> mapping in
the previous example does.
</para>
<para>
Since network accesses are slow, Bitbake maintains a
cache of files downloaded from the network.
Any source files that are not local (i.e.
downloaded from the Internet) are placed into the download
directory, which is specified by the
<link linkend='var-DL_DIR'><filename>DL_DIR</filename></link>
variable.
</para>
<para>
File integrity is of key importance for reproducing builds.
For non-local archive downloads, the fetcher code can verify
SHA-256 and MD5 checksums to ensure the archives have been
downloaded correctly.
You can specify these checksums by using the
<filename>SRC_URI</filename> variable with the appropriate
varflags as follows:
<literallayout class='monospaced'>
SRC_URI[md5sum] = "value"
SRC_URI[sha256sum] = "value"
</literallayout>
You can also specify the checksums as parameters on the
<filename>SRC_URI</filename> as shown below:
<literallayout class='monospaced'>
SRC_URI = "http://example.com/foobar.tar.bz2;md5sum=4a8e0f237e961fd7785d19d07fdb994d"
</literallayout>
If multiple URIs exist, you can specify the checksums either
directly as in the previous example, or you can name the URLs.
The following syntax shows how you name the URIs:
<literallayout class='monospaced'>
SRC_URI = "http://example.com/foobar.tar.bz2;name=foo"
SRC_URI[foo.md5sum] = 4a8e0f237e961fd7785d19d07fdb994d
</literallayout>
After a file has been downloaded and has had its checksum checked,
a ".done" stamp is placed in <filename>DL_DIR</filename>.
BitBake uses this stamp during subsequent builds to avoid
downloading or comparing a checksum for the file again.
<note>
It is assumed that local storage is safe from data corruption.
If this were not the case, there would be bigger issues to worry about.
</note>
</para>
<para>
If
<link linkend='var-BB_STRICT_CHECKSUM'><filename>BB_STRICT_CHECKSUM</filename></link>
is set, any download without a checksum triggers an
error message.
The
<link linkend='var-BB_NO_NETWORK'><filename>BB_NO_NETWORK</filename></link>
variable can be used to make any attempted network access a fatal
error, which is useful for checking that mirrors are complete
as well as other things.
</para>
</section>
<section id='bb-the-unpack'>
<title>The Unpack</title>
<para>
The unpack process usually immediately follows the download.
For all URLs except Git URLs, BitBake uses the common
<filename>unpack</filename> method.
</para>
<para>
A number of parameters exist that you can specify within the
URL to govern the behavior of the unpack stage:
<itemizedlist>
<listitem><para><emphasis>unpack:</emphasis>
Controls whether the URL components are unpacked.
If set to "1", which is the default, the components
are unpacked.
If set to "0", the unpack stage leaves the file alone.
This parameter is useful when you want an archive to be
copied in and not be unpacked.
</para></listitem>
<listitem><para><emphasis>dos:</emphasis>
Applies to <filename>.zip</filename> and
<filename>.jar</filename> files and specifies whether to
use DOS line ending conversion on text files.
</para></listitem>
<listitem><para><emphasis>basepath:</emphasis>
Instructs the unpack stage to strip the specified
directories from the source path when unpacking.
</para></listitem>
<listitem><para><emphasis>subdir:</emphasis>
Unpacks the specific URL to the specified subdirectory
within the root directory.
</para></listitem>
</itemizedlist>
The unpack call automatically decompresses and extracts files
with ".Z", ".z", ".gz", ".xz", ".zip", ".jar", ".ipk", ".rpm".
".srpm", ".deb" and ".bz2" extensions as well as various combinations
of tarball extensions.
</para>
<para>
As mentioned, the Git fetcher has its own unpack method that
is optimized to work with Git trees.
Basically, this method works by cloning the tree into the final
directory.
The process is completed using references so that there is
only one central copy of the Git metadata needed.
</para>
</section>
<section id='bb-fetchers'>
<title>Fetchers</title>
<para>
As mentioned earlier, the URL prefix determines which
fetcher submodule BitBake uses.
Each submodule can support different URL parameters,
which are described in the following sections.
</para>
<section id='local-file-fetcher'>
<title>Local file fetcher (<filename>file://</filename>)</title>
<para>
This submodule handles URLs that begin with
<filename>file://</filename>.
The filename you specify within the URL can be
either an absolute or relative path to a file.
If the filename is relative, the contents of the
<link linkend='var-FILESPATH'><filename>FILESPATH</filename></link>
variable is used in the same way
<filename>PATH</filename> is used to find executables.
Failing that,
<link linkend='var-FILESDIR'><filename>FILESDIR</filename></link>
is used to find the appropriate relative file.
<note>
<filename>FILESDIR</filename> is deprecated and can
be replaced with <filename>FILESPATH</filename>.
Because <filename>FILESDIR</filename> is likely to be
removed, you should not use this variable in any new code.
</note>
If the file cannot be found, it is assumed that it is available in
<link linkend='var-DL_DIR'><filename>DL_DIR</filename></link>
by the time the <filename>download()</filename> method is called.
</para>
<para>
If you specify a directory, the entire directory is
unpacked.
</para>
<para>
Here are a couple of example URLs, the first relative and
the second absolute:
<literallayout class='monospaced'>
SRC_URI = "file://relativefile.patch"
SRC_URI = "file:///Users/ich/very_important_software"
</literallayout>
</para>
</section>
<section id='http-ftp-fetcher'>
<title>HTTP/FTP wget fetcher (<filename>http://</filename>, <filename>ftp://</filename>, <filename>https://</filename>)</title>
<para>
This fetcher obtains files from web and FTP servers.
Internally, the fetcher uses the wget utility.
</para>
<para>
The executable and parameters used are specified by the
<filename>FETCHCMD_wget</filename> variable, which defaults
to sensible values.
The fetcher supports a parameter "downloadfilename" that
allows the name of the downloaded file to be specified.
Specifying the name of the downloaded file is useful
for avoiding collisions in
<link linkend='var-DL_DIR'><filename>DL_DIR</filename></link>
when dealing with multiple files that have the same name.
</para>
<para>
Some example URLs are as follows:
<literallayout class='monospaced'>
SRC_URI = "http://oe.handhelds.org/not_there.aac"
SRC_URI = "ftp://oe.handhelds.org/not_there_as_well.aac"
SRC_URI = "ftp://you@oe.handhelds.org/home/you/secret.plan"
</literallayout>
</para>
</section>
<section id='cvs-fetcher'>
<title>CVS fetcher (<filename>(cvs://</filename>)</title>
<para>
This submodule handles checking out files from the
CVS version control system.
You can configure it using a number of different variables:
<itemizedlist>
<listitem><para><emphasis><filename>FETCHCMD_cvs</filename>:</emphasis>
The name of the executable to use when running
the <filename>cvs</filename> command.
This name is usually "cvs".
</para></listitem>
<listitem><para><emphasis><filename>SRCDATE</filename>:</emphasis>
The date to use when fetching the CVS source code.
A special value of "now" causes the checkout to
be updated on every build.
</para></listitem>
<listitem><para><emphasis><filename>CVSDIR</filename>:</emphasis>
Specifies where a temporary checkout is saved.
The location is often <filename>DL_DIR/cvs</filename>.
</para></listitem>
<listitem><para><emphasis><filename>CVS_PROXY_HOST</filename>:</emphasis>
The name to use as a "proxy=" parameter to the
<filename>cvs</filename> command.
</para></listitem>
<listitem><para><emphasis><filename>CVS_PROXY_PORT</filename>:</emphasis>
The port number to use as a "proxyport=" parameter to
the <filename>cvs</filename> command.
</para></listitem>
</itemizedlist>
As well as the standard username and password URL syntax,
you can also configure the fetcher with various URL parameters:
</para>
<para>
The supported parameters are as follows:
<itemizedlist>
<listitem><para><emphasis>"method":</emphasis>
The protocol over which to communicate with the CVS server.
By default, this protocol is "pserver".
If "method" is set to "ext", BitBake examines the
"rsh" parameter and sets <filename>CVS_RSH</filename>.
You can use "dir" for local directories.
</para></listitem>
<listitem><para><emphasis>"module":</emphasis>
Specifies the module to check out.
You must supply this parameter.
</para></listitem>
<listitem><para><emphasis>"tag":</emphasis>
Describes which CVS TAG should be used for
the checkout.
By default, the TAG is empty.
</para></listitem>
<listitem><para><emphasis>"date":</emphasis>
Specifies a date.
If no "date" is specified, the
<link linkend='var-SRCDATE'><filename>SRCDATE</filename></link>
of the configuration is used to checkout a specific date.
The special value of "now" causes the checkout to be
updated on every build.
</para></listitem>
<listitem><para><emphasis>"localdir":</emphasis>
Used to rename the module.
Effectively, you are renaming the output directory
to which the module is unpacked.
You are forcing the module into a special
directory relative to <filename>CVSDIR</filename>.
</para></listitem>
<listitem><para><emphasis>"rsh"</emphasis>
Used in conjunction with the "method" parameter.
</para></listitem>
<listitem><para><emphasis>"scmdata":</emphasis>
Causes the CVS metadata to be maintained in the tarball
the fetcher creates when set to "keep".
The tarball is expanded into the work directory.
By default, the CVS metadata is removed.
</para></listitem>
<listitem><para><emphasis>"fullpath":</emphasis>
Controls whether the resulting checkout is at the
module level, which is the default, or is at deeper
paths.
</para></listitem>
<listitem><para><emphasis>"norecurse":</emphasis>
Causes the fetcher to only checkout the specified
directory with no recurse into any subdirectories.
</para></listitem>
<listitem><para><emphasis>"port":</emphasis>
The port to which the CVS server connects.
</para></listitem>
</itemizedlist>
Some example URLs are as follows:
<literallayout class='monospaced'>
SRC_URI = "cvs://CVSROOT;module=mymodule;tag=some-version;method=ext"
SRC_URI = "cvs://CVSROOT;module=mymodule;date=20060126;localdir=usethat"
</literallayout>
</para>
</section>
<section id='svn-fetcher'>
<title>Subversion (SVN) Fetcher (<filename>svn://</filename>)</title>
<para>
This fetcher submodule fetches code from the
Subversion source control system.
The executable used is specified by
<filename>FETCHCMD_svn</filename>, which defaults
to "svn".
The fetcher's temporary working directory is set
by <filename>SVNDIR</filename>, which is usually
<filename>DL_DIR/svn</filename>.
</para>
<para>
The supported parameters are as follows:
<itemizedlist>
<listitem><para><emphasis>"module":</emphasis>
The name of the svn module to checkout.
You must provide this parameter.
You can think of this parameter as the top-level
directory of the repository data you want.
</para></listitem>
<listitem><para><emphasis>"protocol":</emphasis>
The protocol to use, which defaults to "svn".
Other options are "svn+ssh" and "rsh".
For "rsh", the "rsh" parameter is also used.
</para></listitem>
<listitem><para><emphasis>"rev":</emphasis>
The revision of the source code to checkout.
</para></listitem>
<listitem><para><emphasis>"date":</emphasis>
The date of the source code to checkout.
Specific revisions are generally much safer to checkout
rather than by date as they do not involve timezones
(e.g. they are much more deterministic).
</para></listitem>
<listitem><para><emphasis>"scmdata":</emphasis>
Causes the “.svn” directories to be available during
compile-time when set to "keep".
By default, these directories are removed.
</para></listitem>
<listitem><para><emphasis>"transportuser":</emphasis>
When required, sets the username for the transport.
By default, this parameter is empty.
The transport username is different than the username
used in the main URL, which is passed to the subversion
command.
</para></listitem>
</itemizedlist>
Following are two examples using svn:
<literallayout class='monospaced'>
SRC_URI = "svn://svn.oe.handhelds.org/svn;module=vip;proto=http;rev=667"
SRC_URI = "svn://svn.oe.handhelds.org/svn/;module=opie;proto=svn+ssh;date=20060126"
</literallayout>
</para>
</section>
<section id='git-fetcher'>
<title>Git Fetcher (<filename>git://</filename>)</title>
<para>
This fetcher submodule fetches code from the Git
source control system.
The fetcher works by creating a bare clone of the
remote into <filename>GITDIR</filename>, which is
usually <filename>DL_DIR/git2</filename>.
This bare clone is then cloned into the work directory during the
unpack stage when a specific tree is checked out.
This is done using alternates and by reference to
minimize the amount of duplicate data on the disk and
make the unpack process fast.
The executable used can be set with
<filename>FETCHCMD_git</filename>.
</para>
<para>
This fetcher supports the following parameters:
<itemizedlist>
<listitem><para><emphasis>"protocol":</emphasis>
The protocol used to fetch the files.
The default is "git" when a hostname is set.
If a hostname is not set, the Git protocol is "file".
You can also use "http", "https", "ssh" and "rsync".
</para></listitem>
<listitem><para><emphasis>"nocheckout":</emphasis>
Tells the fetcher to not checkout source code when
unpacking when set to "1".
Set this option for the URL where there is a custom
routine to checkout code.
The default is "0".
</para></listitem>
<listitem><para><emphasis>"rebaseable":</emphasis>
Indicates that the upstream Git repository can be rebased.
You should set this parameter to "1" if
revisions can become detached from branches.
In this case, the source mirror tarball is done per
revision, which has a loss of efficiency.
Rebasing the upstream Git repository could cause the
current revision to disappear from the upstream repository.
This option reminds the fetcher to preserve the local cache
carefully for future use.
The default value for this parameter is "0".
</para></listitem>
<listitem><para><emphasis>"nobranch":</emphasis>
Tells the fetcher to not check the SHA validation
for the branch when set to "1".
The default is "0".
Set this option for the recipe that refers to
the commit that is valid for a tag instead of
the branch.
</para></listitem>
<listitem><para><emphasis>"bareclone":</emphasis>
Tells the fetcher to clone a bare clone into the
destination directory without checking out a working tree.
Only the raw Git metadata is provided.
This parameter implies the "nocheckout" parameter as well.
</para></listitem>
<listitem><para><emphasis>"branch":</emphasis>
The branch(es) of the Git tree to clone.
If unset, this is assumed to be "master".
The number of branch parameters much match the number of
name parameters.
</para></listitem>
<listitem><para><emphasis>"rev":</emphasis>
The revision to use for the checkout.
The default is "master".
</para></listitem>
<listitem><para><emphasis>"tag":</emphasis>
Specifies a tag to use for the checkout.
To correctly resolve tags, BitBake must access the
network.
For that reason, tags are often not used.
As far as Git is concerned, the "tag" parameter behaves
effectively the same as the "rev" parameter.
</para></listitem>
<listitem><para><emphasis>"subpath":</emphasis>
Limits the checkout to a specific subpath of the tree.
By default, the whole tree is checked out.
</para></listitem>
<listitem><para><emphasis>"destsuffix":</emphasis>
The name of the path in which to place the checkout.
By default, the path is <filename>git/</filename>.
</para></listitem>
</itemizedlist>
Here are some example URLs:
<literallayout class='monospaced'>
SRC_URI = "git://git.oe.handhelds.org/git/vip.git;tag=version-1"
SRC_URI = "git://git.oe.handhelds.org/git/vip.git;protocol=http"
</literallayout>
</para>
</section>
<section id='gitsm-fetcher'>
<title>Git Submodule Fetcher (<filename>gitsm://</filename>)</title>
<para>
This fetcher submodule inherits from the
<link linkend='git-fetcher'>Git fetcher</link> and extends
that fetcher's behavior by fetching a repository's submodules.
<link linkend='var-SRC_URI'><filename>SRC_URI</filename></link>
is passed to the Git fetcher as described in the
"<link linkend='git-fetcher'>Git Fetcher (<filename>git://</filename>)</link>"
section.
<note>
<title>Notes and Warnings</title>
<para>
You must clean a recipe when switching between
'<filename>git://</filename>' and
'<filename>gitsm://</filename>' URLs.
</para>
<para>
The Git Submodules fetcher is not a complete fetcher
implementation.
The fetcher has known issues where it does not use the
normal source mirroring infrastructure properly.
</para>
</note>
</para>
</section>
<section id='clearcase-fetcher'>
<title>ClearCase Fetcher (<filename>ccrc://</filename>)</title>
<para>
This fetcher submodule fetches code from a
<ulink url='http://en.wikipedia.org/wiki/Rational_ClearCase'>ClearCase</ulink>
repository.
</para>
<para>
To use this fetcher, make sure your recipe has proper
<link linkend='var-SRC_URI'><filename>SRC_URI</filename></link>,
<link linkend='var-SRCREV'><filename>SRCREV</filename></link>, and
<link linkend='var-PV'><filename>PV</filename></link> settings.
Here is an example:
<literallayout class='monospaced'>
SRC_URI = "ccrc://cc.example.org/ccrc;vob=/example_vob;module=/example_module"
SRCREV = "EXAMPLE_CLEARCASE_TAG"
PV = "${@d.getVar("SRCREV").replace("/", "+")}"
</literallayout>
The fetcher uses the <filename>rcleartool</filename> or
<filename>cleartool</filename> remote client, depending on
which one is available.
</para>
<para>
Following are options for the <filename>SRC_URI</filename>
statement:
<itemizedlist>
<listitem><para><emphasis><filename>vob</filename></emphasis>:
The name, which must include the
prepending "/" character, of the ClearCase VOB.
This option is required.
</para></listitem>
<listitem><para><emphasis><filename>module</filename></emphasis>:
The module, which must include the
prepending "/" character, in the selected VOB
The <filename>module</filename> and <filename>vob</filename>
options are combined to create the following load rule in
the view config spec:
<literallayout class='monospaced'>
load &lt;vob&gt;&lt;module&gt;
</literallayout>
</para></listitem>
<listitem><para><emphasis><filename>proto</filename></emphasis>:
The protocol, which can be either <filename>http</filename> or
<filename>https</filename>.
</para></listitem>
</itemizedlist>
</para>
<para>
By default, the fetcher creates a configuration specification.
If you want this specification written to an area other than the default,
use the <filename>CCASE_CUSTOM_CONFIG_SPEC</filename> variable
in your recipe to define where the specification is written.
<note>
the <filename>SRCREV</filename> loses its functionality if you
specify this variable.
However, <filename>SRCREV</filename> is still used to label the
archive after a fetch even though it does not define what is
fetched.
</note>
</para>
<para>
Here are a couple of other behaviors worth mentioning:
<itemizedlist>
<listitem><para>
When using <filename>cleartool</filename>, the login of
<filename>cleartool</filename> is handled by the system.
The login require no special steps.
</para></listitem>
<listitem><para>
In order to use <filename>rcleartool</filename> with authenticated
users, an "rcleartool login" is necessary before using the fetcher.
</para></listitem>
</itemizedlist>
</para>
</section>
<section id='other-fetchers'>
<title>Other Fetchers</title>
<para>
Fetch submodules also exist for the following:
<itemizedlist>
<listitem><para>
Bazaar (<filename>bzr://</filename>)
</para></listitem>
<listitem><para>
Perforce (<filename>p4://</filename>)
</para></listitem>
<listitem><para>
Trees using Git Annex (<filename>gitannex://</filename>)
</para></listitem>
<listitem><para>
Secure FTP (<filename>sftp://</filename>)
</para></listitem>
<listitem><para>
Secure Shell (<filename>ssh://</filename>)
</para></listitem>
<listitem><para>
Repo (<filename>repo://</filename>)
</para></listitem>
<listitem><para>
OSC (<filename>osc://</filename>)
</para></listitem>
<listitem><para>
Mercurial (<filename>hg://</filename>)
</para></listitem>
</itemizedlist>
No documentation currently exists for these lesser used
fetcher submodules.
However, you might find the code helpful and readable.
</para>
</section>
</section>
<section id='auto-revisions'>
<title>Auto Revisions</title>
<para>
We need to document <filename>AUTOREV</filename> and
<filename>SRCREV_FORMAT</filename> here.
</para>
</section>
</chapter>

View File

@@ -1,506 +0,0 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<appendix id='hello-world-example'>
<title>Hello World Example</title>
<section id='bitbake-hello-world'>
<title>BitBake Hello World</title>
<para>
The simplest example commonly used to demonstrate any new
programming language or tool is the
"<ulink url="http://en.wikipedia.org/wiki/Hello_world_program">Hello World</ulink>"
example.
This appendix demonstrates, in tutorial form, Hello
World within the context of BitBake.
The tutorial describes how to create a new project
and the applicable metadata files necessary to allow
BitBake to build it.
</para>
</section>
<section id='example-obtaining-bitbake'>
<title>Obtaining BitBake</title>
<para>
See the
"<link linkend='obtaining-bitbake'>Obtaining BitBake</link>"
section for information on how to obtain BitBake.
Once you have the source code on your machine, the BitBake directory
appears as follows:
<literallayout class='monospaced'>
$ ls -al
total 100
drwxrwxr-x. 9 wmat wmat 4096 Jan 31 13:44 .
drwxrwxr-x. 3 wmat wmat 4096 Feb 4 10:45 ..
-rw-rw-r--. 1 wmat wmat 365 Nov 26 04:55 AUTHORS
drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 bin
drwxrwxr-x. 4 wmat wmat 4096 Jan 31 13:44 build
-rw-rw-r--. 1 wmat wmat 16501 Nov 26 04:55 ChangeLog
drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 classes
drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 conf
drwxrwxr-x. 3 wmat wmat 4096 Nov 26 04:55 contrib
-rw-rw-r--. 1 wmat wmat 17987 Nov 26 04:55 COPYING
drwxrwxr-x. 3 wmat wmat 4096 Nov 26 04:55 doc
-rw-rw-r--. 1 wmat wmat 69 Nov 26 04:55 .gitignore
-rw-rw-r--. 1 wmat wmat 849 Nov 26 04:55 HEADER
drwxrwxr-x. 5 wmat wmat 4096 Jan 31 13:44 lib
-rw-rw-r--. 1 wmat wmat 195 Nov 26 04:55 MANIFEST.in
-rwxrwxr-x. 1 wmat wmat 3195 Jan 31 11:57 setup.py
-rw-rw-r--. 1 wmat wmat 2887 Nov 26 04:55 TODO
</literallayout>
</para>
<para>
At this point, you should have BitBake cloned to
a directory that matches the previous listing except for
dates and user names.
</para>
</section>
<section id='setting-up-the-bitbake-environment'>
<title>Setting Up the BitBake Environment</title>
<para>
First, you need to be sure that you can run BitBake.
Set your working directory to where your local BitBake
files are and run the following command:
<literallayout class='monospaced'>
$ ./bin/bitbake --version
BitBake Build Tool Core version 1.23.0, bitbake version 1.23.0
</literallayout>
The console output tells you what version you are running.
</para>
<para>
The recommended method to run BitBake is from a directory of your
choice.
To be able to run BitBake from any directory, you need to add the
executable binary to your binary to your shell's environment
<filename>PATH</filename> variable.
First, look at your current <filename>PATH</filename> variable
by entering the following:
<literallayout class='monospaced'>
$ echo $PATH
</literallayout>
Next, add the directory location for the BitBake binary to the
<filename>PATH</filename>.
Here is an example that adds the
<filename>/home/scott-lenovo/bitbake/bin</filename> directory
to the front of the <filename>PATH</filename> variable:
<literallayout class='monospaced'>
$ export PATH=/home/scott-lenovo/bitbake/bin:$PATH
</literallayout>
You should now be able to enter the <filename>bitbake</filename>
command from the command line while working from any directory.
</para>
</section>
<section id='the-hello-world-example'>
<title>The Hello World Example</title>
<para>
The overall goal of this exercise is to build a
complete "Hello World" example utilizing task and layer
concepts.
Because this is how modern projects such as OpenEmbedded and
the Yocto Project utilize BitBake, the example
provides an excellent starting point for understanding
BitBake.
</para>
<para>
To help you understand how to use BitBake to build targets,
the example starts with nothing but the <filename>bitbake</filename>
command, which causes BitBake to fail and report problems.
The example progresses by adding pieces to the build to
eventually conclude with a working, minimal "Hello World"
example.
</para>
<para>
While every attempt is made to explain what is happening during
the example, the descriptions cannot cover everything.
You can find further information throughout this manual.
Also, you can actively participate in the
<ulink url='http://lists.openembedded.org/mailman/listinfo/bitbake-devel'></ulink>
discussion mailing list about the BitBake build tool.
</para>
<note>
This example was inspired by and drew heavily from these sources:
<itemizedlist>
<listitem><para>
<ulink url="http://www.mail-archive.com/yocto@yoctoproject.org/msg09379.html">Mailing List post - The BitBake equivalent of "Hello, World!"</ulink>
</para></listitem>
<listitem><para>
<ulink url="https://web.archive.org/web/20150325165911/http://hambedded.org/blog/2012/11/24/from-bitbake-hello-world-to-an-image/">Hambedded Linux blog post - From Bitbake Hello World to an Image</ulink>
</para></listitem>
</itemizedlist>
</note>
<para>
As stated earlier, the goal of this example
is to eventually compile "Hello World".
However, it is unknown what BitBake needs and what you have
to provide in order to achieve that goal.
Recall that BitBake utilizes three types of metadata files:
<link linkend='configuration-files'>Configuration Files</link>,
<link linkend='classes'>Classes</link>, and
<link linkend='recipes'>Recipes</link>.
But where do they go?
How does BitBake find them?
BitBake's error messaging helps you answer these types of questions
and helps you better understand exactly what is going on.
</para>
<para>
Following is the complete "Hello World" example.
</para>
<orderedlist>
<listitem><para><emphasis>Create a Project Directory:</emphasis>
First, set up a directory for the "Hello World" project.
Here is how you can do so in your home directory:
<literallayout class='monospaced'>
$ mkdir ~/hello
$ cd ~/hello
</literallayout>
This is the directory that BitBake will use to do all of
its work.
You can use this directory to keep all the metafiles needed
by BitBake.
Having a project directory is a good way to isolate your
project.
</para></listitem>
<listitem><para><emphasis>Run Bitbake:</emphasis>
At this point, you have nothing but a project directory.
Run the <filename>bitbake</filename> command and see what
it does:
<literallayout class='monospaced'>
$ bitbake
The BBPATH variable is not set and bitbake did not
find a conf/bblayers.conf file in the expected location.
Maybe you accidentally invoked bitbake from the wrong directory?
DEBUG: Removed the following variables from the environment:
GNOME_DESKTOP_SESSION_ID, XDG_CURRENT_DESKTOP,
GNOME_KEYRING_CONTROL, DISPLAY, SSH_AGENT_PID, LANG, no_proxy,
XDG_SESSION_PATH, XAUTHORITY, SESSION_MANAGER, SHLVL,
MANDATORY_PATH, COMPIZ_CONFIG_PROFILE, WINDOWID, EDITOR,
GPG_AGENT_INFO, SSH_AUTH_SOCK, GDMSESSION, GNOME_KEYRING_PID,
XDG_SEAT_PATH, XDG_CONFIG_DIRS, LESSOPEN, DBUS_SESSION_BUS_ADDRESS,
_, XDG_SESSION_COOKIE, DESKTOP_SESSION, LESSCLOSE, DEFAULTS_PATH,
UBUNTU_MENUPROXY, OLDPWD, XDG_DATA_DIRS, COLORTERM, LS_COLORS
</literallayout>
The majority of this output is specific to environment variables
that are not directly relevant to BitBake.
However, the very first message regarding the
<filename>BBPATH</filename> variable and the
<filename>conf/bblayers.conf</filename> file
is relevant.</para>
<para>
When you run BitBake, it begins looking for metadata files.
The
<link linkend='var-BBPATH'><filename>BBPATH</filename></link>
variable is what tells BitBake where to look for those files.
<filename>BBPATH</filename> is not set and you need to set it.
Without <filename>BBPATH</filename>, Bitbake cannot
find any configuration files (<filename>.conf</filename>)
or recipe files (<filename>.bb</filename>) at all.
BitBake also cannot find the <filename>bitbake.conf</filename>
file.
</para></listitem>
<listitem><para><emphasis>Setting <filename>BBPATH</filename>:</emphasis>
For this example, you can set <filename>BBPATH</filename>
in the same manner that you set <filename>PATH</filename>
earlier in the appendix.
You should realize, though, that it is much more flexible to set the
<filename>BBPATH</filename> variable up in a configuration
file for each project.</para>
<para>From your shell, enter the following commands to set and
export the <filename>BBPATH</filename> variable:
<literallayout class='monospaced'>
$ BBPATH="&lt;projectdirectory&gt;"
$ export BBPATH
</literallayout>
Use your actual project directory in the command.
BitBake uses that directory to find the metadata it needs for
your project.
<note>
When specifying your project directory, do not use the
tilde ("~") character as BitBake does not expand that character
as the shell would.
</note>
</para></listitem>
<listitem><para><emphasis>Run Bitbake:</emphasis>
Now that you have <filename>BBPATH</filename> defined, run
the <filename>bitbake</filename> command again:
<literallayout class='monospaced'>
$ bitbake
ERROR: Traceback (most recent call last):
File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 163, in wrapped
return func(fn, *args)
File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 173, in parse_config_file
return bb.parse.handle(fn, data, include)
File "/home/scott-lenovo/bitbake/lib/bb/parse/__init__.py", line 99, in handle
return h['handle'](fn, data, include)
File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/ConfHandler.py", line 120, in handle
abs_fn = resolve_file(fn, data)
File "/home/scott-lenovo/bitbake/lib/bb/parse/__init__.py", line 117, in resolve_file
raise IOError("file %s not found in %s" % (fn, bbpath))
IOError: file conf/bitbake.conf not found in /home/scott-lenovo/hello
ERROR: Unable to parse conf/bitbake.conf: file conf/bitbake.conf not found in /home/scott-lenovo/hello
</literallayout>
This sample output shows that BitBake could not find the
<filename>conf/bitbake.conf</filename> file in the project
directory.
This file is the first thing BitBake must find in order
to build a target.
And, since the project directory for this example is
empty, you need to provide a <filename>conf/bitbake.conf</filename>
file.
</para></listitem>
<listitem><para><emphasis>Creating <filename>conf/bitbake.conf</filename>:</emphasis>
The <filename>conf/bitbake.conf</filename> includes a number of
configuration variables BitBake uses for metadata and recipe
files.
For this example, you need to create the file in your project directory
and define some key BitBake variables.
For more information on the <filename>bitbake.conf</filename>,
see
<ulink url='https://web.archive.org/web/20150325165911/http://hambedded.org/blog/2012/11/24/from-bitbake-hello-world-to-an-image/#an-overview-of-bitbakeconf'></ulink>
</para>
<para>Use the following commands to create the <filename>conf</filename>
directory in the project directory:
<literallayout class='monospaced'>
$ mkdir conf
</literallayout>
From within the <filename>conf</filename> directory, use
some editor to create the <filename>bitbake.conf</filename>
so that it contains the following:
<literallayout class='monospaced'>
TMPDIR = "${<link linkend='var-TOPDIR'>TOPDIR</link>}/tmp"
<link linkend='var-CACHE'>CACHE</link> = "${TMPDIR}/cache"
<link linkend='var-STAMP'>STAMP</link> = "${TMPDIR}/stamps"
<link linkend='var-T'>T</link> = "${TMPDIR}/work"
<link linkend='var-B'>B</link> = "${TMPDIR}"
</literallayout>
The <filename>TMPDIR</filename> variable establishes a directory
that BitBake uses for build output and intermediate files (other
than the cached information used by the
<link linkend='setscene'>Setscene</link> process.
Here, the <filename>TMPDIR</filename> directory is set to
<filename>hello/tmp</filename>.
<note><title>Tip</title>
You can always safely delete the <filename>tmp</filename>
directory in order to rebuild a BitBake target.
The build process creates the directory for you
when you run BitBake.
</note></para>
<para>For information about each of the other variables defined in this
example, click on the links to take you to the definitions in
the glossary.
</para></listitem>
<listitem><para><emphasis>Run Bitbake:</emphasis>
After making sure that the <filename>conf/bitbake.conf</filename>
file exists, you can run the <filename>bitbake</filename>
command again:
<literallayout class='monospaced'>
$ bitbake
ERROR: Traceback (most recent call last):
File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 163, in wrapped
return func(fn, *args)
File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 177, in _inherit
bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/BBHandler.py", line 92, in inherit
include(fn, file, lineno, d, "inherit")
File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/ConfHandler.py", line 100, in include
raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
ParseError: ParseError in configuration INHERITs: Could not inherit file classes/base.bbclass
ERROR: Unable to parse base: ParseError in configuration INHERITs: Could not inherit file classes/base.bbclass
</literallayout>
In the sample output, BitBake could not find the
<filename>classes/base.bbclass</filename> file.
You need to create that file next.
</para></listitem>
<listitem><para><emphasis>Creating <filename>classes/base.bbclass</filename>:</emphasis>
BitBake uses class files to provide common code and functionality.
The minimally required class for BitBake is the
<filename>classes/base.bbclass</filename> file.
The <filename>base</filename> class is implicitly inherited by
every recipe.
BitBake looks for the class in the <filename>classes</filename>
directory of the project (i.e <filename>hello/classes</filename>
in this example).
</para>
<para>Create the <filename>classes</filename> directory as follows:
<literallayout class='monospaced'>
$ cd $HOME/hello
$ mkdir classes
</literallayout>
Move to the <filename>classes</filename> directory and then
create the <filename>base.bbclass</filename> file by inserting
this single line:
<literallayout class='monospaced'>
addtask build
</literallayout>
The minimal task that BitBake runs is the
<filename>do_build</filename> task.
This is all the example needs in order to build the project.
Of course, the <filename>base.bbclass</filename> can have much
more depending on which build environments BitBake is
supporting.
For more information on the <filename>base.bbclass</filename> file,
you can look at
<ulink url='https://web.archive.org/web/20150325165911/http://hambedded.org/blog/2012/11/24/from-bitbake-hello-world-to-an-image/#tasks'></ulink>.
</para></listitem>
<listitem><para><emphasis>Run Bitbake:</emphasis>
After making sure that the <filename>classes/base.bbclass</filename>
file exists, you can run the <filename>bitbake</filename>
command again:
<literallayout class='monospaced'>
$ bitbake
Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.
</literallayout>
BitBake is finally reporting no errors.
However, you can see that it really does not have anything
to do.
You need to create a recipe that gives BitBake something to do.
</para></listitem>
<listitem><para><emphasis>Creating a Layer:</emphasis>
While it is not really necessary for such a small example,
it is good practice to create a layer in which to keep your
code separate from the general metadata used by BitBake.
Thus, this example creates and uses a layer called "mylayer".
<note>
You can find additional information on adding a layer at
<ulink url='https://web.archive.org/web/20150325165911/http://hambedded.org/blog/2012/11/24/from-bitbake-hello-world-to-an-image/#adding-an-example-layer'></ulink>.
</note>
</para>
<para>Minimally, you need a recipe file and a layer configuration
file in your layer.
The configuration file needs to be in the <filename>conf</filename>
directory inside the layer.
Use these commands to set up the layer and the <filename>conf</filename>
directory:
<literallayout class='monospaced'>
$ cd $HOME
$ mkdir mylayer
$ cd mylayer
$ mkdir conf
</literallayout>
Move to the <filename>conf</filename> directory and create a
<filename>layer.conf</filename> file that has the following:
<literallayout class='monospaced'>
BBPATH .= ":${<link linkend='var-LAYERDIR'>LAYERDIR</link>}"
<link linkend='var-BBFILES'>BBFILES</link> += "${LAYERDIR}/*.bb"
<link linkend='var-BBFILE_COLLECTIONS'>BBFILE_COLLECTIONS</link> += "mylayer"
<link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR}/"
</literallayout>
For information on these variables, click the links
to go to the definitions in the glossary.</para>
<para>You need to create the recipe file next.
Inside your layer at the top-level, use an editor and create
a recipe file named <filename>printhello.bb</filename> that
has the following:
<literallayout class='monospaced'>
<link linkend='var-DESCRIPTION'>DESCRIPTION</link> = "Prints Hello World"
<link linkend='var-PN'>PN</link> = 'printhello'
<link linkend='var-PV'>PV</link> = '1'
python do_build() {
bb.plain("********************");
bb.plain("* *");
bb.plain("* Hello, World! *");
bb.plain("* *");
bb.plain("********************");
}
</literallayout>
The recipe file simply provides a description of the
recipe, the name, version, and the <filename>do_build</filename>
task, which prints out "Hello World" to the console.
For more information on these variables, follow the links
to the glossary.
</para></listitem>
<listitem><para><emphasis>Run Bitbake With a Target:</emphasis>
Now that a BitBake target exists, run the command and provide
that target:
<literallayout class='monospaced'>
$ cd $HOME/hello
$ bitbake printhello
ERROR: no recipe files to build, check your BBPATH and BBFILES?
Summary: There was 1 ERROR message shown, returning a non-zero exit code.
</literallayout>
We have created the layer with the recipe and the layer
configuration file but it still seems that BitBake cannot
find the recipe.
BitBake needs a <filename>conf/bblayers.conf</filename> that
lists the layers for the project.
Without this file, BitBake cannot find the recipe.
</para></listitem>
<listitem><para><emphasis>Creating <filename>conf/bblayers.conf</filename>:</emphasis>
BitBake uses the <filename>conf/bblayers.conf</filename> file
to locate layers needed for the project.
This file must reside in the <filename>conf</filename> directory
of the project (i.e. <filename>hello/conf</filename> for this
example).</para>
<para>Set your working directory to the <filename>hello/conf</filename>
directory and then create the <filename>bblayers.conf</filename>
file so that it contains the following:
<literallayout class='monospaced'>
BBLAYERS ?= " \
/home/&lt;you&gt;/mylayer \
"
</literallayout>
You need to provide your own information for
<filename>you</filename> in the file.
</para></listitem>
<listitem><para><emphasis>Run Bitbake With a Target:</emphasis>
Now that you have supplied the <filename>bblayers.conf</filename>
file, run the <filename>bitbake</filename> command and provide
the target:
<literallayout class='monospaced'>
$ bitbake printhello
Parsing recipes: 100% |##################################################################################|
Time: 00:00:00
Parsing of 1 .bb files complete (0 cached, 1 parsed). 1 targets, 0 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
NOTE: Preparing runqueue
NOTE: Executing RunQueue Tasks
********************
* *
* Hello, World! *
* *
********************
NOTE: Tasks Summary: Attempted 1 tasks of which 0 didn't need to be rerun and all succeeded.
</literallayout>
BitBake finds the <filename>printhello</filename> recipe and
successfully runs the task.
<note>
After the first execution, re-running
<filename>bitbake printhello</filename> again will not
result in a BitBake run that prints the same console
output.
The reason for this is that the first time the
<filename>printhello.bb</filename> recipe's
<filename>do_build</filename> task executes
successfully, BitBake writes a stamp file for the task.
Thus, the next time you attempt to run the task
using that same <filename>bitbake</filename> command,
BitBake notices the stamp and therefore determines
that the task does not need to be re-run.
If you delete the <filename>tmp</filename> directory
or run <filename>bitbake -c clean printhello</filename>
and then re-run the build, the "Hello, World!" message will
be printed again.
</note>
</para></listitem>
</orderedlist>
</section>
</appendix>

View File

@@ -1,685 +0,0 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<chapter id="bitbake-user-manual-intro">
<title>Overview</title>
<para>
Welcome to the BitBake User Manual.
This manual provides information on the BitBake tool.
The information attempts to be as independent as possible regarding
systems that use BitBake, such as OpenEmbedded and the
Yocto Project.
In some cases, scenarios or examples within the context of
a build system are used in the manual to help with understanding.
For these cases, the manual clearly states the context.
</para>
<section id="intro">
<title>Introduction</title>
<para>
Fundamentally, BitBake is a generic task execution
engine that allows shell and Python tasks to be run
efficiently and in parallel while working within
complex inter-task dependency constraints.
One of BitBake's main users, OpenEmbedded, takes this core
and builds embedded Linux software stacks using
a task-oriented approach.
</para>
<para>
Conceptually, BitBake is similar to GNU Make in
some regards but has significant differences:
<itemizedlist>
<listitem><para>
BitBake executes tasks according to provided
metadata that builds up the tasks.
Metadata is stored in recipe (<filename>.bb</filename>)
and related recipe "append" (<filename>.bbappend</filename>)
files, configuration (<filename>.conf</filename>) and
underlying include (<filename>.inc</filename>) files, and
in class (<filename>.bbclass</filename>) files.
The metadata provides
BitBake with instructions on what tasks to run and
the dependencies between those tasks.
</para></listitem>
<listitem><para>
BitBake includes a fetcher library for obtaining source
code from various places such as local files, source control
systems, or websites.
</para></listitem>
<listitem><para>
The instructions for each unit to be built (e.g. a piece
of software) are known as "recipe" files and
contain all the information about the unit
(dependencies, source file locations, checksums, description
and so on).
</para></listitem>
<listitem><para>
BitBake includes a client/server abstraction and can
be used from a command line or used as a service over
XML-RPC and has several different user interfaces.
</para></listitem>
</itemizedlist>
</para>
</section>
<section id="history-and-goals">
<title>History and Goals</title>
<para>
BitBake was originally a part of the OpenEmbedded project.
It was inspired by the Portage package management system
used by the Gentoo Linux distribution.
On December 7, 2004, OpenEmbedded project team member
Chris Larson split the project into two distinct pieces:
<itemizedlist>
<listitem><para>BitBake, a generic task executor</para></listitem>
<listitem><para>OpenEmbedded, a metadata set utilized by
BitBake</para></listitem>
</itemizedlist>
Today, BitBake is the primary basis of the
<ulink url="http://www.openembedded.org/">OpenEmbedded</ulink>
project, which is being used to build and maintain Linux
distributions such as the
<ulink url='http://www.angstrom-distribution.org/'>Angstrom Distribution</ulink>,
and which is also being used as the build tool for Linux projects
such as the
<ulink url='http://www.yoctoproject.org'>Yocto Project</ulink>.
</para>
<para>
Prior to BitBake, no other build tool adequately met the needs of
an aspiring embedded Linux distribution.
All of the build systems used by traditional desktop Linux
distributions lacked important functionality, and none of the
ad hoc Buildroot-based systems, prevalent in the
embedded space, were scalable or maintainable.
</para>
<para>
Some important original goals for BitBake were:
<itemizedlist>
<listitem><para>
Handle cross-compilation.
</para></listitem>
<listitem><para>
Handle inter-package 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, and so forth.
</para></listitem>
<listitem><para>
Be Linux distribution agnostic for both build and
target systems.
</para></listitem>
<listitem><para>
Be architecture agnostic.
</para></listitem>
<listitem><para>
Support multiple build and target operating systems
(e.g. Cygwin, the BSDs, and so forth).
</para></listitem>
<listitem><para>
Be self contained, rather than tightly
integrated into the build machine's root
filesystem.
</para></listitem>
<listitem><para>
Handle conditional metadata on the target architecture,
operating system, distribution, and machine.
</para></listitem>
<listitem><para>
Be easy to use the tools to supply local metadata and packages
against which to operate.
</para></listitem>
<listitem><para>
Be easy to use BitBake to collaborate between multiple
projects for their builds.
</para></listitem>
<listitem><para>
Provide an inheritance mechanism to share
common metadata between many packages.
</para></listitem>
</itemizedlist>
Over time it became apparent that some further requirements
were necessary:
<itemizedlist>
<listitem><para>
Handle variants of a base recipe (e.g. native, sdk,
and multilib).
</para></listitem>
<listitem><para>
Split metadata into layers and allow layers
to enhance or override other layers.
</para></listitem>
<listitem><para>
Allow representation of a given set of input variables
to a task as a checksum.
Based on that checksum, allow acceleration of builds
with prebuilt components.
</para></listitem>
</itemizedlist>
BitBake satisfies all the original requirements and many more
with extensions being made to the basic functionality to
reflect the additional requirements.
Flexibility and power have always been the priorities.
BitBake is highly extensible and supports embedded Python code and
execution of any arbitrary tasks.
</para>
</section>
<section id="Concepts">
<title>Concepts</title>
<para>
BitBake is a program written in the Python language.
At the highest level, BitBake interprets metadata, decides
what tasks are required to run, and executes those tasks.
Similar to GNU Make, BitBake controls how software is
built.
GNU Make achieves its control through "makefiles", while
BitBake uses "recipes".
</para>
<para>
BitBake extends the capabilities of a simple
tool like GNU Make by allowing for the definition of much more
complex tasks, such as assembling entire embedded Linux
distributions.
</para>
<para>
The remainder of this section introduces several concepts
that should be understood in order to better leverage
the power of BitBake.
</para>
<section id='recipes'>
<title>Recipes</title>
<para>
BitBake Recipes, which are denoted by the file extension
<filename>.bb</filename>, are the most basic metadata files.
These recipe files provide BitBake with the following:
<itemizedlist>
<listitem><para>Descriptive information about the
package (author, homepage, license, and so on)</para></listitem>
<listitem><para>The version of the recipe</para></listitem>
<listitem><para>Existing dependencies (both build
and runtime dependencies)</para></listitem>
<listitem><para>Where the source code resides and
how to fetch it</para></listitem>
<listitem><para>Whether the source code requires
any patches, where to find them, and how to apply
them</para></listitem>
<listitem><para>How to configure and compile the
source code</para></listitem>
<listitem><para>Where on the target machine to install the
package or packages created</para></listitem>
</itemizedlist>
</para>
<para>
Within the context of BitBake, or any project utilizing BitBake
as its build system, files with the <filename>.bb</filename>
extension are referred to as recipes.
<note>
The term "package" is also commonly used to describe recipes.
However, since the same word is used to describe packaged
output from a project, it is best to maintain a single
descriptive term - "recipes".
Put another way, a single "recipe" file is quite capable
of generating a number of related but separately installable
"packages".
In fact, that ability is fairly common.
</note>
</para>
</section>
<section id='configuration-files'>
<title>Configuration Files</title>
<para>
Configuration files, which are denoted by the
<filename>.conf</filename> extension, define
various configuration variables that govern the project's build
process.
These files fall into several areas that define
machine configuration options, distribution configuration
options, compiler tuning options, general common
configuration options, and user configuration options.
The main configuration file is the sample
<filename>bitbake.conf</filename> file, which is
located within the BitBake source tree
<filename>conf</filename> directory.
</para>
</section>
<section id='classes'>
<title>Classes</title>
<para>
Class files, which are denoted by the
<filename>.bbclass</filename> extension, contain
information that is useful to share between metadata files.
The BitBake source tree currently comes with one class metadata file
called <filename>base.bbclass</filename>.
You can find this file in the
<filename>classes</filename> directory.
The <filename>base.bbclass</filename> class files is special since it
is always included automatically for all recipes
and classes.
This class contains definitions for standard basic tasks such
as fetching, unpacking, configuring (empty by default),
compiling (runs any Makefile present), installing (empty by
default) and packaging (empty by default).
These tasks are often overridden or extended by other classes
added during the project development process.
</para>
</section>
<section id='layers'>
<title>Layers</title>
<para>
Layers allow you to isolate different types of
customizations from each other.
While you might find it tempting to keep everything in one layer
when working on a single project, the more modular you organize
your metadata, the easier it is to cope with future changes.
</para>
<para>
To illustrate how you can use layers to keep things modular,
consider customizations you might make to support a specific target machine.
These types of customizations typically reside in a special layer,
rather than a general layer, called a Board Support Package (BSP)
Layer.
Furthermore, the machine customizations should be isolated from
recipes and metadata that support a new GUI environment, for
example.
This situation gives you a couple of layers: one for the machine
configurations and one for the GUI environment.
It is important to understand, however, that the BSP layer can still
make machine-specific additions to recipes within
the GUI environment layer without polluting the GUI layer itself
with those machine-specific changes.
You can accomplish this through a recipe that is a BitBake append
(<filename>.bbappend</filename>) file.
</para>
</section>
<section id='append-bbappend-files'>
<title>Append Files</title>
<para>
Append files, which are files that have the
<filename>.bbappend</filename> file extension, extend or
override information in an existing recipe file.
</para>
<para>
BitBake expects every append file to have a corresponding recipe file.
Furthermore, the append file and corresponding recipe file
must use the same root filename.
The filenames can differ only in the file type suffix used
(e.g. <filename>formfactor_0.0.bb</filename> and
<filename>formfactor_0.0.bbappend</filename>).
</para>
<para>
Information in append files extends or
overrides the information in the underlying,
similarly-named recipe files.
</para>
<para>
When you name an append file, you can use the
wildcard character (%) to allow for matching recipe names.
For example, suppose you have an append file named
as follows:
<literallayout class='monospaced'>
busybox_1.21.%.bbappend
</literallayout>
That append file would match any <filename>busybox_1.21.x.bb</filename>
version of the recipe.
So, the append file would match the following recipe names:
<literallayout class='monospaced'>
busybox_1.21.1.bb
busybox_1.21.2.bb
busybox_1.21.3.bb
</literallayout>
If the <filename>busybox</filename> recipe was updated to
<filename>busybox_1.3.0.bb</filename>, the append name would not
match.
However, if you named the append file
<filename>busybox_1.%.bbappend</filename>, then you would have a match.
</para>
<para>
In the most general case, you could name the append file something as
simple as <filename>busybox_%.bbappend</filename> to be entirely
version independent.
</para>
</section>
</section>
<section id='obtaining-bitbake'>
<title>Obtaining BitBake</title>
<para>
You can obtain BitBake several different ways:
<itemizedlist>
<listitem><para><emphasis>Cloning BitBake:</emphasis>
Using Git to clone the BitBake source code repository
is the recommended method for obtaining BitBake.
Cloning the repository makes it easy to get bug fixes
and have access to stable branches and the master
branch.
Once you have cloned BitBake, you should use
the latest stable
branch for development since the master branch is for
BitBake development and might contain less stable changes.
</para>
<para>You usually need a version of BitBake
that matches the metadata you are using.
The metadata is generally backwards compatible but
not forward compatible.</para>
<para>Here is an example that clones the BitBake repository:
<literallayout class='monospaced'>
$ git clone git://git.openembedded.org/bitbake
</literallayout>
This command clones the BitBake Git repository into a
directory called <filename>bitbake</filename>.
Alternatively, you can
designate a directory after the
<filename>git clone</filename> command
if you want to call the new directory something
other than <filename>bitbake</filename>.
Here is an example that names the directory
<filename>bbdev</filename>:
<literallayout class='monospaced'>
$ git clone git://git.openembedded.org/bitbake bbdev
</literallayout></para></listitem>
<listitem><para><emphasis>Installation using your Distribution
Package Management System:</emphasis>
This method is not
recommended because the BitBake version that is
provided by your distribution, in most cases,
is several
releases behind a snapshot of the BitBake repository.
</para></listitem>
<listitem><para><emphasis>Taking a snapshot of BitBake:</emphasis>
Downloading a snapshot of BitBake from the
source code repository gives you access to a known
branch or release of BitBake.
<note>
Cloning the Git repository, as described earlier,
is the preferred method for getting BitBake.
Cloning the repository makes it easier to update as
patches are added to the stable branches.
</note></para>
<para>The following example downloads a snapshot of
BitBake version 1.17.0:
<literallayout class='monospaced'>
$ wget http://git.openembedded.org/bitbake/snapshot/bitbake-1.17.0.tar.gz
$ tar zxpvf bitbake-1.17.0.tar.gz
</literallayout>
After extraction of the tarball using the tar utility,
you have a directory entitled
<filename>bitbake-1.17.0</filename>.
</para></listitem>
<listitem><para><emphasis>Using the BitBake that Comes With Your
Build Checkout:</emphasis>
A final possibility for getting a copy of BitBake is that it
already comes with your checkout of a larger Bitbake-based build
system, such as Poky or Yocto Project.
Rather than manually checking out individual layers and
gluing them together yourself, you can check
out an entire build system.
The checkout will already include a version of BitBake that
has been thoroughly tested for compatibility with the other
components.
For information on how to check out a particular BitBake-based
build system, consult that build system's supporting documentation.
</para></listitem>
</itemizedlist>
</para>
</section>
<section id="bitbake-user-manual-command">
<title>The BitBake Command</title>
<para>
The <filename>bitbake</filename> command is the primary interface
to the BitBake tool.
This section presents the BitBake command syntax and provides
several execution examples.
</para>
<section id='usage-and-syntax'>
<title>Usage and syntax</title>
<para>
Following is the usage and syntax for BitBake:
<literallayout class='monospaced'>
$ bitbake -h
Usage: bitbake [options] [recipename/target ...]
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
will provide the layer, BBFILES and other configuration information.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-b BUILDFILE, --buildfile=BUILDFILE
Execute tasks from a specific .bb recipe directly.
WARNING: Does not handle any dependencies from other
recipes.
-k, --continue Continue as much as possible after an error. While the
target that failed and anything depending on it cannot
be built, as much as possible will be built before
stopping.
-a, --tryaltconfigs Continue with builds by trying to use alternative
providers where possible.
-f, --force Force the specified targets/task to run (invalidating
any existing stamp file).
-c CMD, --cmd=CMD Specify the task to execute. The exact options
available depend on the metadata. Some examples might
be 'compile' or 'populate_sysroot' or 'listtasks' may
give a list of the tasks available.
-C INVALIDATE_STAMP, --clear-stamp=INVALIDATE_STAMP
Invalidate the stamp for the specified task such as
'compile' and then run the default task for the
specified target(s).
-r PREFILE, --read=PREFILE
Read the specified file before bitbake.conf.
-R POSTFILE, --postread=POSTFILE
Read the specified file after bitbake.conf.
-v, --verbose Output more log message data 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.
-S SIGNATURE_HANDLER, --dump-signatures=SIGNATURE_HANDLER
Dump out the signature construction information, with
no task execution. The SIGNATURE_HANDLER parameter is
passed to the handler. Two common values are none and
printdiff but the handler may define more/less. none
means only dump the signature, printdiff means compare
the dumped signature with the cached one.
-p, --parse-only Quit after parsing the BB recipes.
-s, --show-versions Show current and preferred versions of all recipes.
-e, --environment Show the global or per-recipe environment complete
with information about where variables were
set/changed.
-g, --graphviz Save dependency tree information for the specified
targets in the dot syntax.
-I EXTRA_ASSUME_PROVIDED, --ignore-deps=EXTRA_ASSUME_PROVIDED
Assume these dependencies don't exist and are already
provided (equivalent to ASSUME_PROVIDED). Useful to
make dependency graphs more appealing
-l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS
Show debug logging for the specified logging domains
-P, --profile Profile the command and save reports.
-u UI, --ui=UI The user interface to use (e.g. knotty, hob, depexp).
-t SERVERTYPE, --servertype=SERVERTYPE
Choose which server to use, process or xmlrpc.
--token=XMLRPCTOKEN Specify the connection token to be used when
connecting to a remote server.
--revisions-changed Set the exit code depending on whether upstream
floating revisions have changed or not.
--server-only Run bitbake without a UI, only starting a server
(cooker) process.
-B BIND, --bind=BIND The name/address for the bitbake server to bind to.
--no-setscene Do not run any setscene tasks. sstate will be ignored
and everything needed, built.
--remote-server=REMOTE_SERVER
Connect to the specified server.
-m, --kill-server Terminate the remote server.
--observe-only Connect to a server as an observing-only client.
--status-only Check the status of the remote bitbake server.
</literallayout>
</para>
</section>
<section id='bitbake-examples'>
<title>Examples</title>
<para>
This section presents some examples showing how to use BitBake.
</para>
<section id='example-executing-a-task-against-a-single-recipe'>
<title>Executing a Task Against a Single Recipe</title>
<para>
Executing tasks for a single recipe file is relatively simple.
You specify the file in question, and BitBake parses
it and executes the specified task.
If you do not specify a task, BitBake executes the default
task, which is "build”.
BitBake obeys inter-task dependencies when doing
so.
</para>
<para>
The following command runs the build task, which is
the default task, on the <filename>foo_1.0.bb</filename>
recipe file:
<literallayout class='monospaced'>
$ bitbake -b foo_1.0.bb
</literallayout>
The following command runs the clean task on the
<filename>foo.bb</filename> recipe file:
<literallayout class='monospaced'>
$ bitbake -b foo.bb -c clean
</literallayout>
<note>
The "-b" option explicitly does not handle recipe
dependencies.
Other than for debugging purposes, it is instead
recommended that you use the syntax presented in the
next section.
</note>
</para>
</section>
<section id='executing-tasks-against-a-set-of-recipe-files'>
<title>Executing Tasks Against a Set of Recipe Files</title>
<para>
There are a number of additional complexities introduced
when one wants to manage multiple <filename>.bb</filename>
files.
Clearly there needs to be a way to tell BitBake what
files are available and, of those, which you
want to execute.
There also needs to be a way for each recipe
to express its dependencies, both for build-time and
runtime.
There must be a way for you to express recipe preferences
when multiple recipes provide the same functionality, or when
there are multiple versions of a recipe.
</para>
<para>
The <filename>bitbake</filename> command, when not using
"--buildfile" or "-b" only accepts a "PROVIDES".
You cannot provide anything else.
By default, a recipe file generally "PROVIDES" its
"packagename" as shown in the following example:
<literallayout class='monospaced'>
$ bitbake foo
</literallayout>
This next example "PROVIDES" the package name and also uses
the "-c" option to tell BitBake to just execute the
<filename>do_clean</filename> task:
<literallayout class='monospaced'>
$ bitbake -c clean foo
</literallayout>
</para>
</section>
<section id='generating-dependency-graphs'>
<title>Generating Dependency Graphs</title>
<para>
BitBake is able to generate dependency graphs using
the <filename>dot</filename> syntax.
You can convert these graphs into images using the
<filename>dot</filename> tool from
<ulink url='http://www.graphviz.org'>Graphviz</ulink>.
</para>
<para>
When you generate a dependency graph, BitBake writes four files
to the current working directory:
<itemizedlist>
<listitem><para><emphasis><filename>package-depends.dot</filename>:</emphasis>
Shows BitBake's knowledge of dependencies between
runtime targets.
</para></listitem>
<listitem><para><emphasis><filename>pn-depends.dot</filename>:</emphasis>
Shows dependencies between build-time targets
(i.e. recipes).
</para></listitem>
<listitem><para><emphasis><filename>task-depends.dot</filename>:</emphasis>
Shows dependencies between tasks.
</para></listitem>
<listitem><para><emphasis><filename>pn-buildlist</filename>:</emphasis>
Shows a simple list of targets that are to be built.
</para></listitem>
</itemizedlist>
</para>
<para>
To stop depending on common depends, use the "-I" depend
option and BitBake omits them from the graph.
Leaving this information out can produce more readable graphs.
This way, you can remove from the graph
<filename>DEPENDS</filename> from inherited classes
such as <filename>base.bbclass</filename>.
</para>
<para>
Here are two examples that create dependency graphs.
The second example omits depends common in OpenEmbedded from
the graph:
<literallayout class='monospaced'>
$ bitbake -g foo
$ bitbake -g -I virtual/kernel -I eglibc foo
</literallayout>
</para>
</section>
</section>
</section>
</chapter>

View File

@@ -1,984 +0,0 @@
/*
Generic XHTML / DocBook XHTML CSS Stylesheet.
Browser wrangling and typographic design by
Oyvind Kolas / pippin@gimp.org
Customised for Poky by
Matthew Allum / mallum@o-hand.com
Thanks to:
Liam R. E. Quin
William Skaggs
Jakub Steiner
Structure
---------
The stylesheet is divided into the following sections:
Positioning
Margins, paddings, width, font-size, clearing.
Decorations
Borders, style
Colors
Colors
Graphics
Graphical backgrounds
Nasty IE tweaks
Workarounds needed to make it work in internet explorer,
currently makes the stylesheet non validating, but up until
this point it is validating.
Mozilla extensions
Transparency for footer
Rounded corners on boxes
*/
/*************** /
/ Positioning /
/ ***************/
body {
font-family: Verdana, Sans, sans-serif;
min-width: 640px;
width: 80%;
margin: 0em auto;
padding: 2em 5em 5em 5em;
color: #333;
}
h1,h2,h3,h4,h5,h6,h7 {
font-family: Arial, Sans;
color: #00557D;
clear: both;
}
h1 {
font-size: 2em;
text-align: left;
padding: 0em 0em 0em 0em;
margin: 2em 0em 0em 0em;
}
h2.subtitle {
margin: 0.10em 0em 3.0em 0em;
padding: 0em 0em 0em 0em;
font-size: 1.8em;
padding-left: 20%;
font-weight: normal;
font-style: italic;
}
h2 {
margin: 2em 0em 0.66em 0em;
padding: 0.5em 0em 0em 0em;
font-size: 1.5em;
font-weight: bold;
}
h3.subtitle {
margin: 0em 0em 1em 0em;
padding: 0em 0em 0em 0em;
font-size: 142.14%;
text-align: right;
}
h3 {
margin: 1em 0em 0.5em 0em;
padding: 1em 0em 0em 0em;
font-size: 140%;
font-weight: bold;
}
h4 {
margin: 1em 0em 0.5em 0em;
padding: 1em 0em 0em 0em;
font-size: 120%;
font-weight: bold;
}
h5 {
margin: 1em 0em 0.5em 0em;
padding: 1em 0em 0em 0em;
font-size: 110%;
font-weight: bold;
}
h6 {
margin: 1em 0em 0em 0em;
padding: 1em 0em 0em 0em;
font-size: 110%;
font-weight: bold;
}
.authorgroup {
background-color: transparent;
background-repeat: no-repeat;
padding-top: 256px;
background-image: url("figures/bitbake-title.png");
background-position: left top;
margin-top: -256px;
padding-right: 50px;
margin-left: 0px;
text-align: right;
width: 740px;
}
h3.author {
margin: 0em 0me 0em 0em;
padding: 0em 0em 0em 0em;
font-weight: normal;
font-size: 100%;
color: #333;
clear: both;
}
.author tt.email {
font-size: 66%;
}
.titlepage hr {
width: 0em;
clear: both;
}
.revhistory {
padding-top: 2em;
clear: both;
}
.toc,
.list-of-tables,
.list-of-examples,
.list-of-figures {
padding: 1.33em 0em 2.5em 0em;
color: #00557D;
}
.toc p,
.list-of-tables p,
.list-of-figures p,
.list-of-examples p {
padding: 0em 0em 0em 0em;
padding: 0em 0em 0.3em;
margin: 1.5em 0em 0em 0em;
}
.toc p b,
.list-of-tables p b,
.list-of-figures p b,
.list-of-examples p b{
font-size: 100.0%;
font-weight: bold;
}
.toc dl,
.list-of-tables dl,
.list-of-figures dl,
.list-of-examples dl {
margin: 0em 0em 0.5em 0em;
padding: 0em 0em 0em 0em;
}
.toc dt {
margin: 0em 0em 0em 0em;
padding: 0em 0em 0em 0em;
}
.toc dd {
margin: 0em 0em 0em 2.6em;
padding: 0em 0em 0em 0em;
}
div.glossary dl,
div.variablelist dl {
}
.glossary dl dt,
.variablelist dl dt,
.variablelist dl dt span.term {
font-weight: normal;
width: 20em;
text-align: right;
}
.variablelist dl dt {
margin-top: 0.5em;
}
.glossary dl dd,
.variablelist dl dd {
margin-top: -1em;
margin-left: 25.5em;
}
.glossary dd p,
.variablelist dd p {
margin-top: 0em;
margin-bottom: 1em;
}
div.calloutlist table td {
padding: 0em 0em 0em 0em;
margin: 0em 0em 0em 0em;
}
div.calloutlist table td p {
margin-top: 0em;
margin-bottom: 1em;
}
div p.copyright {
text-align: left;
}
div.legalnotice p.legalnotice-title {
margin-bottom: 0em;
}
p {
line-height: 1.5em;
margin-top: 0em;
}
dl {
padding-top: 0em;
}
hr {
border: solid 1px;
}
.mediaobject,
.mediaobjectco {
text-align: center;
}
img {
border: none;
}
ul {
padding: 0em 0em 0em 1.5em;
}
ul li {
padding: 0em 0em 0em 0em;
}
ul li p {
text-align: left;
}
table {
width :100%;
}
th {
padding: 0.25em;
text-align: left;
font-weight: normal;
vertical-align: top;
}
td {
padding: 0.25em;
vertical-align: top;
}
p a[id] {
margin: 0px;
padding: 0px;
display: inline;
background-image: none;
}
a {
text-decoration: underline;
color: #444;
}
pre {
overflow: auto;
}
a:hover {
text-decoration: underline;
/*font-weight: bold;*/
}
/* This style defines how the permalink character
appears by itself and when hovered over with
the mouse. */
[alt='Permalink'] { color: #eee; }
[alt='Permalink']:hover { color: black; }
div.informalfigure,
div.informalexample,
div.informaltable,
div.figure,
div.table,
div.example {
margin: 1em 0em;
padding: 1em;
page-break-inside: avoid;
}
div.informalfigure p.title b,
div.informalexample p.title b,
div.informaltable p.title b,
div.figure p.title b,
div.example p.title b,
div.table p.title b{
padding-top: 0em;
margin-top: 0em;
font-size: 100%;
font-weight: normal;
}
.mediaobject .caption,
.mediaobject .caption p {
text-align: center;
font-size: 80%;
padding-top: 0.5em;
padding-bottom: 0.5em;
}
.epigraph {
padding-left: 55%;
margin-bottom: 1em;
}
.epigraph p {
text-align: left;
}
.epigraph .quote {
font-style: italic;
}
.epigraph .attribution {
font-style: normal;
text-align: right;
}
span.application {
font-style: italic;
}
.programlisting {
font-family: monospace;
font-size: 80%;
white-space: pre;
margin: 1.33em 0em;
padding: 1.33em;
}
.tip,
.warning,
.caution,
.note {
margin-top: 1em;
margin-bottom: 1em;
}
/* force full width of table within div */
.tip table,
.warning table,
.caution table,
.note table {
border: none;
width: 100%;
}
.tip table th,
.warning table th,
.caution table th,
.note table th {
padding: 0.8em 0.0em 0.0em 0.0em;
margin : 0em 0em 0em 0em;
}
.tip p,
.warning p,
.caution p,
.note p {
margin-top: 0.5em;
margin-bottom: 0.5em;
padding-right: 1em;
text-align: left;
}
.acronym {
text-transform: uppercase;
}
b.keycap,
.keycap {
padding: 0.09em 0.3em;
margin: 0em;
}
.itemizedlist li {
clear: none;
}
.filename {
font-size: medium;
font-family: Courier, monospace;
}
div.navheader, div.heading{
position: absolute;
left: 0em;
top: 0em;
width: 100%;
background-color: #cdf;
width: 100%;
}
div.navfooter, div.footing{
position: fixed;
left: 0em;
bottom: 0em;
background-color: #eee;
width: 100%;
}
div.navheader td,
div.navfooter td {
font-size: 66%;
}
div.navheader table th {
/*font-family: Georgia, Times, serif;*/
/*font-size: x-large;*/
font-size: 80%;
}
div.navheader table {
border-left: 0em;
border-right: 0em;
border-top: 0em;
width: 100%;
}
div.navfooter table {
border-left: 0em;
border-right: 0em;
border-bottom: 0em;
width: 100%;
}
div.navheader table td a,
div.navfooter table td a {
color: #777;
text-decoration: none;
}
/* normal text in the footer */
div.navfooter table td {
color: black;
}
div.navheader table td a:visited,
div.navfooter table td a:visited {
color: #444;
}
/* links in header and footer */
div.navheader table td a:hover,
div.navfooter table td a:hover {
text-decoration: underline;
background-color: transparent;
color: #33a;
}
div.navheader hr,
div.navfooter hr {
display: none;
}
.qandaset tr.question td p {
margin: 0em 0em 1em 0em;
padding: 0em 0em 0em 0em;
}
.qandaset tr.answer td p {
margin: 0em 0em 1em 0em;
padding: 0em 0em 0em 0em;
}
.answer td {
padding-bottom: 1.5em;
}
.emphasis {
font-weight: bold;
}
/************* /
/ decorations /
/ *************/
.titlepage {
}
.part .title {
}
.subtitle {
border: none;
}
/*
h1 {
border: none;
}
h2 {
border-top: solid 0.2em;
border-bottom: solid 0.06em;
}
h3 {
border-top: 0em;
border-bottom: solid 0.06em;
}
h4 {
border: 0em;
border-bottom: solid 0.06em;
}
h5 {
border: 0em;
}
*/
.programlisting {
border: solid 1px;
}
div.figure,
div.table,
div.informalfigure,
div.informaltable,
div.informalexample,
div.example {
border: 1px solid;
}
.tip,
.warning,
.caution,
.note {
border: 1px solid;
}
.tip table th,
.warning table th,
.caution table th,
.note table th {
border-bottom: 1px solid;
}
.question td {
border-top: 1px solid black;
}
.answer {
}
b.keycap,
.keycap {
border: 1px solid;
}
div.navheader, div.heading{
border-bottom: 1px solid;
}
div.navfooter, div.footing{
border-top: 1px solid;
}
/********* /
/ colors /
/ *********/
body {
color: #333;
background: white;
}
a {
background: transparent;
}
a:hover {
background-color: #dedede;
}
h1,
h2,
h3,
h4,
h5,
h6,
h7,
h8 {
background-color: transparent;
}
hr {
border-color: #aaa;
}
.tip, .warning, .caution, .note {
border-color: #fff;
}
.tip table th,
.warning table th,
.caution table th,
.note table th {
border-bottom-color: #fff;
}
.warning {
background-color: #f0f0f2;
}
.caution {
background-color: #f0f0f2;
}
.tip {
background-color: #f0f0f2;
}
.note {
background-color: #f0f0f2;
}
.glossary dl dt,
.variablelist dl dt,
.variablelist dl dt span.term {
color: #044;
}
div.figure,
div.table,
div.example,
div.informalfigure,
div.informaltable,
div.informalexample {
border-color: #aaa;
}
pre.programlisting {
color: black;
background-color: #fff;
border-color: #aaa;
border-width: 2px;
}
.guimenu,
.guilabel,
.guimenuitem {
background-color: #eee;
}
b.keycap,
.keycap {
background-color: #eee;
border-color: #999;
}
div.navheader {
border-color: black;
}
div.navfooter {
border-color: black;
}
/*********** /
/ graphics /
/ ***********/
/*
body {
background-image: url("images/body_bg.jpg");
background-attachment: fixed;
}
.navheader,
.note,
.tip {
background-image: url("images/note_bg.jpg");
background-attachment: fixed;
}
.warning,
.caution {
background-image: url("images/warning_bg.jpg");
background-attachment: fixed;
}
.figure,
.informalfigure,
.example,
.informalexample,
.table,
.informaltable {
background-image: url("images/figure_bg.jpg");
background-attachment: fixed;
}
*/
h1,
h2,
h3,
h4,
h5,
h6,
h7{
}
/*
Example of how to stick an image as part of the title.
div.article .titlepage .title
{
background-image: url("figures/white-on-black.png");
background-position: center;
background-repeat: repeat-x;
}
*/
div.preface .titlepage .title,
div.colophon .title,
div.chapter .titlepage .title,
div.article .titlepage .title
{
}
div.section div.section .titlepage .title,
div.sect2 .titlepage .title {
background: none;
}
h1.title {
background-color: transparent;
background-repeat: no-repeat;
height: 256px;
text-indent: -9000px;
overflow:hidden;
}
h2.subtitle {
background-color: transparent;
text-indent: -9000px;
overflow:hidden;
width: 0px;
display: none;
}
/*************************************** /
/ pippin.gimp.org specific alterations /
/ ***************************************/
/*
div.heading, div.navheader {
color: #777;
font-size: 80%;
padding: 0;
margin: 0;
text-align: left;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 50px;
background: url('/gfx/heading_bg.png') transparent;
background-repeat: repeat-x;
background-attachment: fixed;
border: none;
}
div.heading a {
color: #444;
}
div.footing, div.navfooter {
border: none;
color: #ddd;
font-size: 80%;
text-align:right;
width: 100%;
padding-top: 10px;
position: absolute;
bottom: 0px;
left: 0px;
background: url('/gfx/footing_bg.png') transparent;
}
*/
/****************** /
/ nasty ie tweaks /
/ ******************/
/*
div.heading, div.navheader {
width:expression(document.body.clientWidth + "px");
}
div.footing, div.navfooter {
width:expression(document.body.clientWidth + "px");
margin-left:expression("-5em");
}
body {
padding:expression("4em 5em 0em 5em");
}
*/
/**************************************** /
/ mozilla vendor specific css extensions /
/ ****************************************/
/*
div.navfooter, div.footing{
-moz-opacity: 0.8em;
}
div.figure,
div.table,
div.informalfigure,
div.informaltable,
div.informalexample,
div.example,
.tip,
.warning,
.caution,
.note {
-moz-border-radius: 0.5em;
}
b.keycap,
.keycap {
-moz-border-radius: 0.3em;
}
*/
table tr td table tr td {
display: none;
}
hr {
display: none;
}
table {
border: 0em;
}
.photo {
float: right;
margin-left: 1.5em;
margin-bottom: 1.5em;
margin-top: 0em;
max-width: 17em;
border: 1px solid gray;
padding: 3px;
background: white;
}
.seperator {
padding-top: 2em;
clear: both;
}
#validators {
margin-top: 5em;
text-align: right;
color: #777;
}
@media print {
body {
font-size: 8pt;
}
.noprint {
display: none;
}
}
.tip,
.note {
background: #f0f0f2;
color: #333;
padding: 20px;
margin: 20px;
}
.tip h3,
.note h3 {
padding: 0em;
margin: 0em;
font-size: 2em;
font-weight: bold;
color: #333;
}
.tip a,
.note a {
color: #333;
text-decoration: underline;
}
.footnote {
font-size: small;
color: #333;
}
/* Changes the announcement text */
.tip h3,
.warning h3,
.caution h3,
.note h3 {
font-size:large;
color: #00557D;
}

View File

@@ -1,88 +0,0 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<book id='bitbake-user-manual' lang='en'
xmlns:xi="http://www.w3.org/2003/XInclude"
xmlns="http://docbook.org/ns/docbook"
>
<bookinfo>
<mediaobject>
<imageobject>
<imagedata fileref='figures/bitbake-title.png'
format='SVG'
align='left' scalefit='1' width='100%'/>
</imageobject>
</mediaobject>
<title>
BitBake User Manual
</title>
<authorgroup>
<author>
<firstname>Richard Purdie, Chris Larson, and </firstname> <surname>Phil Blundell</surname>
<affiliation>
<orgname>BitBake Community</orgname>
</affiliation>
<email>bitbake-devel@lists.openembedded.org</email>
</author>
</authorgroup>
<!--
# Add in some revision history if we want it here.
<revhistory>
<revision>
<revnumber>x.x</revnumber>
<date>dd month year</date>
<revremark>Some relevent comment</revremark>
</revision>
<revision>
<revnumber>x.x</revnumber>
<date>dd month year</date>
<revremark>Some relevent comment</revremark>
</revision>
<revision>
<revnumber>x.x</revnumber>
<date>dd month year</date>
<revremark>Some relevent comment</revremark>
</revision>
<revision>
<revnumber>x.x</revnumber>
<date>dd month year</date>
<revremark>Some relevent comment</revremark>
</revision>
</revhistory>
-->
<copyright>
<year>2004-2015</year>
<holder>Richard Purdie</holder>
<holder>Chris Larson</holder>
<holder>and 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, 444 Castro Street,
Suite 900, Mountain View, California 94041, USA.
</para>
</legalnotice>
</bookinfo>
<xi:include href="bitbake-user-manual-intro.xml"/>
<xi:include href="bitbake-user-manual-execution.xml"/>
<xi:include href="bitbake-user-manual-metadata.xml"/>
<xi:include href="bitbake-user-manual-fetching.xml"/>
<xi:include href="bitbake-user-manual-ref-variables.xml"/>
<xi:include href="bitbake-user-manual-hello.xml"/>
</book>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -89,7 +89,7 @@ quit after parsing the BB files (developers only)
show current and preferred versions of all packages
.TP
.B \-e, \-\-environment
show the global or per-recipe environment (this is what used to be bbread)
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

View 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 --with-dblatex --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:

View File

@@ -0,0 +1,627 @@
<?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, 2011</year>
<holder>Chris Larson</holder>
<holder>Phil Blundell</holder>
<holder>Richard Purdie</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/projects such as Angstrom and the Yocto project.</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 original 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>
</itemizedlist>
</para>
<para>Over time it has become apparent that some further requirements were necessary:
<itemizedlist>
<listitem><para>Handle variants of a base recipe (native, sdk, multilib).</para></listitem>
<listitem><para>Able to split metadata into layers and allow layers to override each other.</para></listitem>
<listitem><para>Allow representation of a given set of input variables to a task as a checksum.</para></listitem>
<listitem><para>based on that checksum, allow acceleration of builds with prebuilt components.</para></listitem>
</itemizedlist>
</para>
<para>BitBake satisfies all the original requirements and many more with extensions being made to the basic functionality to reflect the additionl requirements. 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 its 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 weak default value (??=)</title>
<para><screen><varname>A</varname> ??= "somevalue"
<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/weak assignment 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. Any other setting of A using = or ?= will however override the value set with ??=</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>Appending and Prepending (override style syntax)</title>
<para><screen><varname>B</varname> = "bval"
<varname>B_append</varname> = " additional data"
<varname>C</varname> = "cval"
<varname>C_prepend</varname> = "additional data "</screen></para>
<para>This example results in <varname>B</varname> becoming <literal>bval additional data</literal>
and <varname>C</varname> becoming <literal>additional data cval</literal>. Note the spaces in the append.
Unlike the += operator, additional space is not automatically added. You must take steps to add space
yourself.</para>
</section>
<section>
<title>Removing (override style syntax)</title>
<para><screen><varname>FOO</varname> = "123 456 789 123456 123 456 123 456"
<varname>FOO_remove</varname> = "123"
<varname>FOO_remove</varname> = "456"</screen></para>
<para>In this example, <varname>FOO</varname> is now <literal>789 123456</literal>.</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 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 file to be included cannot 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 d.getVar('SOMECONDITION', 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.bbclass 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>Task Flags</title>
<para>Tasks support a number of flags which control various functionality of the task. These are as follows:</para>
<para>'dirs' - directories which should be created before the task runs</para>
<para>'cleandirs' - directories which should created before the task runs but should be empty</para>
<para>'noexec' - marks the tasks as being empty and no execution required. These are used as dependency placeholders or used when added tasks need to be subsequently disabled.</para>
<para>'nostamp' - don't generate a stamp file for a task. This means the task is always rexecuted.</para>
<para>'fakeroot' - this task needs to be run in a fakeroot environment, obtained by adding the variables in FAKEROOTENV to the environment.</para>
<para>'umask' - the umask to run the task under.</para>
<para> For the 'deptask', 'rdeptask', 'depends', 'rdepends' and 'recrdeptask' flags please see the dependencies section.</para>
</section>
<section>
<title>Events</title>
<para><emphasis>NOTE:</emphasis> This is only supported in .bb and .bbclass files.</para>
<para>BitBake allows installation of 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 is to make it easy to do things like email notification 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 used 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 incarnation will have the "native" class inherited.</para>
<para>The second feature is <varname>BBVERSIONS</varname>. This variable allows a single recipe 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>Variable interaction: Worked Examples</title>
<para>Despite the documentation of the different forms of variable definition above, it can be hard to work out what happens when variable operators are combined. This section documents some common questions people have regarding the way variables interact.</para>
<section>
<title>Override and append ordering</title>
<para>There is often confusion about which order overrides and the various append operators take effect.</para>
<para><screen><varname>OVERRIDES</varname> = "foo"
<varname>A_foo_append</varname> = "X"</screen></para>
<para>In this case, X is unconditionally appended to the variable <varname>A_foo</varname>. Since foo is an override, A_foo would then replace <varname>A</varname>.</para>
<para><screen><varname>OVERRIDES</varname> = "foo"
<varname>A</varname> = "X"
<varname>A_append_foo</varname> = "Y"</screen></para>
<para>In this case, only when foo is in OVERRIDES, Y is appended to the variable <varname>A</varname> so the value of <varname>A</varname> would become XY (NB: no spaces are appended).</para>
<para><screen><varname>OVERRIDES</varname> = "foo"
<varname>A_foo_append</varname> = "X"
<varname>A_foo_append</varname> += "Y"</screen></para>
<para>This behaves as per the first case above, but the value of <varname>A</varname> would be "X Y" instead of just "X".</para>
<para><screen><varname>A</varname> = "1"
<varname>A_append</varname> = "2"
<varname>A_append</varname> = "3"
<varname>A</varname> += "4"
<varname>A</varname> .= "5"</screen></para>
<para>Would ultimately result in <varname>A</varname> taking the value "1 4523" since the _append operator executes at the same time as the expansion of other overrides.</para>
</section>
<section>
<title>Key Expansion</title>
<para>Key expansion happens at the data store finalisation time just before overrides are expanded.</para>
<para><screen><varname>A${B}</varname> = "X"
<varname>B</varname> = "2"
<varname>A2</varname> = "Y"</screen></para>
<para>So in this case <varname>A2</varname> would take the value of "X".</para>
</section>
</section>
<section>
<title>Dependency handling</title>
<para>BitBake handles dependencies at the task level since to allow for efficient operation with multiple processed executing in parallel. A robust method of specifying 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>Build Dependencies</title>
<para>DEPENDS lists build time dependencies. The 'deptask' flag for tasks is used to signify the task of each item listed in 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>Runtime Dependencies</title>
<para>The PACKAGES variable lists runtime packages and each of these can have RDEPENDS and RRECOMMENDS runtime dependencies. The 'rdeptask' flag for tasks is used to signify the task of each item runtime dependency 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 Dependencies</title>
<para>These are specified with the 'recrdeptask' flag which is used signify the task(s) of dependencies which must have completed before that task can be executed. It works by looking though the build and runtime dependencies of the current recipe as well as any inter-task dependencies the task has, then adding a dependency on the listed task. It will then recurse through the dependencies of those tasks and so on.</para>
<para>It may be desireable to recurse not just through the dependencies of those tasks but through the build and runtime dependencies of dependent tasks too. If that is the case, the taskname itself should be referenced in the task list, e.g. do_a[recrdeptask] = "do_a do_b".</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.</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>
<para>The 'rdepends' flag works in a similar way but takes targets in the runtime namespace instead of the build time dependency namespace.</para>
</section>
</section>
<section>
<title>Parsing</title>
<section>
<title>Configuration files</title>
<para>The first kind 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 directories 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 and it handled by the fetch and fetch2 modules. At this point the original fetch code is considered to be replaced by fetch2 and this manual only related to the fetch2 codebase.</para>
<para>The SRC_URI is normally used to tell BitBake which files to fetch. The next sections will describe the available fetchers and their options. Each fetcher honors a set of variables and per URI parameters separated by a <quote>;</quote> consisting of a key and a value. The semantics of the variables and parameters are defined by the fetcher. BitBake tries to have consistent semantics between the different fetchers.
</para>
<para>The overall fetch process is that first, fetches are attempted from PREMIRRORS. If those don't work, the original SRC_URI is attempted and if that fails, BitBake will fall back to MIRRORS. Cross urls are supported, so its possible to mirror a git repository on an http server as a tarball for example. Some example commonly used mirror definitions are:</para>
<para><screen><varname>PREMIRRORS</varname> ?= "\
bzr://.*/.* http://somemirror.org/sources/ \n \
cvs://.*/.* http://somemirror.org/sources/ \n \
git://.*/.* http://somemirror.org/sources/ \n \
hg://.*/.* http://somemirror.org/sources/ \n \
osc://.*/.* http://somemirror.org/sources/ \n \
p4://.*/.* http://somemirror.org/sources/ \n \
svk://.*/.* http://somemirror.org/sources/ \n \
svn://.*/.* http://somemirror.org/sources/ \n"
<varname>MIRRORS</varname> =+ "\
ftp://.*/.* http://somemirror.org/sources/ \n \
http://.*/.* http://somemirror.org/sources/ \n \
https://.*/.* http://somemirror.org/sources/ \n"</screen></para>
<para>Non-local downloaded output is placed into the directory specified by the <varname>DL_DIR</varname>. For non local archive downloads the code can verify sha256 and md5 checksums for the download to ensure the file has been downloaded correctly. These may be specified either in the form <varname>SRC_URI[md5sum]</varname> for the md5 checksum and <varname>SRC_URI[sha256sum]</varname> for the sha256 checksum or as parameters on the SRC_URI such as SRC_URI="http://example.com/foobar.tar.bz2;md5sum=4a8e0f237e961fd7785d19d07fdb994d". If <varname>BB_STRICT_CHECKSUM</varname> is set, any download without a checksum will trigger an error message. In cases where multiple files are listed in SRC_URI, the name parameter is used assign names to the urls and these are then specified in the checksums in the form SRC_URI[name.sha256sum].</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 failing that <varname>FILESDIR</varname> will be used to find the appropriate relative file. The metadata usually extend these variables to include variations of the values in <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 fetcher</title>
<para>The URN for the CVS fetcher is <emphasis>cvs</emphasis>. This fetcher honors the variables <varname>CVSDIR</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 to use for 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> and <varname>scmdata</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 fetcher are <emphasis>http</emphasis>, <emphasis>https</emphasis> and <emphasis>ftp</emphasis>. This fetcher honors the variables <varname>FETCHCOMMAND_wget</varname>. <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 file to be fetched.
</para>
<para><screen><varname>SRC_URI</varname> = "http://oe.handhelds.org/not_there.aac"
<varname>SRC_URI</varname> = "ftp://oe.handhelds.org/not_there_as_well.aac"
<varname>SRC_URI</varname> = "ftp://you@oe.handheld.sorg/home/you/secret.plan"
</screen></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>SVNDIR</varname>, <varname>SRCREV</varname>. <varname>FETCHCOMMAND</varname> contains the subversion command. <varname>SRCREV</varname> specifies which revision to use when doing the fetching.
</para>
<para>The supported parameters are <varname>proto</varname>, <varname>rev</varname> and <varname>scmdata</varname>. <varname>proto</varname> is the Subversion protocol, <varname>rev</varname> is the Subversion revision. If <varname>scmdata</varname> is set to <quote>keep</quote>, the <quote>.svn</quote> directories will be available during compile-time.
</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 variable <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> and <emphasis>scmdata</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>git</quote> if a hostname is set, otherwise its <quote>file</quote>. If <emphasis>scmdata</emphasis> is set to <quote>keep</quote>, the <quote>.git</quote> directory will be available during compile-time.
</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)
-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 to specify 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. This way, <varname>DEPENDS</varname> from inherited classes such as 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 <varname>BBFILES</varname> 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, set to a component of the .bb filename by default.</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 <varname>PROVIDES</varname> 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, we have 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 preference for the default selected version. In addition, the user can specify their preferred 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>

View File

@@ -1,59 +0,0 @@
<!ENTITY DISTRO "1.4">
<!ENTITY DISTRO_NAME "tbd">
<!ENTITY YOCTO_DOC_VERSION "1.4">
<!ENTITY POKYVERSION "8.0">
<!ENTITY YOCTO_POKY "poky-&DISTRO_NAME;-&POKYVERSION;">
<!ENTITY COPYRIGHT_YEAR "2010-2013">
<!ENTITY YOCTO_DL_URL "http://downloads.yoctoproject.org">
<!ENTITY YOCTO_HOME_URL "http://www.yoctoproject.org">
<!ENTITY YOCTO_LISTS_URL "http://lists.yoctoproject.org">
<!ENTITY YOCTO_BUGZILLA_URL "http://bugzilla.yoctoproject.org">
<!ENTITY YOCTO_WIKI_URL "https://wiki.yoctoproject.org">
<!ENTITY YOCTO_AB_URL "http://autobuilder.yoctoproject.org">
<!ENTITY YOCTO_GIT_URL "http://git.yoctoproject.org">
<!ENTITY YOCTO_ADTREPO_URL "http://adtrepo.yoctoproject.org">
<!ENTITY OE_HOME_URL "http://www.openembedded.org">
<!ENTITY OE_LISTS_URL "http://lists.linuxtogo.org/cgi-bin/mailman">
<!ENTITY OE_DOCS_URL "http://docs.openembedded.org">
<!ENTITY OH_HOME_URL "http://o-hand.com">
<!ENTITY BITBAKE_HOME_URL "http://developer.berlios.de/projects/bitbake/">
<!ENTITY ECLIPSE_MAIN_URL "http://www.eclipse.org/downloads">
<!ENTITY ECLIPSE_DL_URL "http://download.eclipse.org">
<!ENTITY ECLIPSE_DL_PLUGIN_URL "&YOCTO_DL_URL;/releases/eclipse-plugin/&DISTRO;">
<!ENTITY ECLIPSE_UPDATES_URL "&ECLIPSE_DL_URL;/tm/updates/3.3">
<!ENTITY ECLIPSE_INDIGO_URL "&ECLIPSE_DL_URL;/releases/indigo">
<!ENTITY ECLIPSE_JUNO_URL "&ECLIPSE_DL_URL;/releases/juno">
<!ENTITY ECLIPSE_INDIGO_CDT_URL "&ECLIPSE_DL_URL;tools/cdt/releases/indigo">
<!ENTITY YOCTO_DOCS_URL "&YOCTO_HOME_URL;/docs">
<!ENTITY YOCTO_SOURCES_URL "&YOCTO_HOME_URL;/sources/">
<!ENTITY YOCTO_AB_PORT_URL "&YOCTO_AB_URL;:8010">
<!ENTITY YOCTO_AB_NIGHTLY_URL "&YOCTO_AB_URL;/nightly/">
<!ENTITY YOCTO_POKY_URL "&YOCTO_DL_URL;/releases/poky/">
<!ENTITY YOCTO_RELEASE_DL_URL "&YOCTO_DL_URL;/releases/yocto/yocto-&DISTRO;">
<!ENTITY YOCTO_TOOLCHAIN_DL_URL "&YOCTO_RELEASE_DL_URL;/toolchain/">
<!ENTITY YOCTO_ECLIPSE_DL_URL "&YOCTO_RELEASE_DL_URL;/eclipse-plugin/indigo;">
<!ENTITY YOCTO_ADTINSTALLER_DL_URL "&YOCTO_RELEASE_DL_URL;/adt_installer">
<!ENTITY YOCTO_POKY_DL_URL "&YOCTO_RELEASE_DL_URL;/&YOCTO_POKY;.tar.bz2">
<!ENTITY YOCTO_MACHINES_DL_URL "&YOCTO_RELEASE_DL_URL;/machines">
<!ENTITY YOCTO_QEMU_DL_URL "&YOCTO_MACHINES_DL_URL;/qemu">
<!ENTITY YOCTO_PYTHON-i686_DL_URL "&YOCTO_DL_URL;/releases/miscsupport/python-nativesdk-standalone-i686.tar.bz2">
<!ENTITY YOCTO_PYTHON-x86_64_DL_URL "&YOCTO_DL_URL;/releases/miscsupport/python-nativesdk-standalone-x86_64.tar.bz2">
<!ENTITY YOCTO_DOCS_QS_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/yocto-project-qs/yocto-project-qs.html">
<!ENTITY YOCTO_DOCS_ADT_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/adt-manual/adt-manual.html">
<!ENTITY YOCTO_DOCS_REF_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/ref-manual/ref-manual.html">
<!ENTITY YOCTO_DOCS_BSP_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/bsp-guide/bsp-guide.html">
<!ENTITY YOCTO_DOCS_DEV_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/dev-manual/dev-manual.html">
<!ENTITY YOCTO_DOCS_KERNEL_URL "&YOCTO_DOCS_URL;/&YOCTO_DOC_VERSION;/kernel-manual/kernel-manual.html">
<!ENTITY YOCTO_ADTPATH_DIR "/opt/poky/&DISTRO;">
<!ENTITY YOCTO_POKY_TARBALL "&YOCTO_POKY;.tar.bz2">
<!ENTITY OE_INIT_PATH "&YOCTO_POKY;/oe-init-build-env">
<!ENTITY OE_INIT_FILE "oe-init-build-env">
<!ENTITY UBUNTU_HOST_PACKAGES_ESSENTIAL "gawk wget git-core diffstat unzip texinfo \
build-essential chrpath">
<!ENTITY FEDORA_HOST_PACKAGES_ESSENTIAL "gawk make wget tar bzip2 gzip python unzip perl patch \
diffutils diffstat git cpp gcc gcc-c++ eglibc-devel texinfo chrpath \
ccache">
<!ENTITY OPENSUSE_HOST_PACKAGES_ESSENTIAL "python gcc gcc-c++ git chrpath make wget python-xml \
diffstat texinfo python-curses">
<!ENTITY CENTOS_HOST_PACKAGES_ESSENTIAL "gawk make wget tar bzip2 gzip python unzip perl patch \
diffutils diffstat git cpp gcc gcc-c++ glibc-devel texinfo chrpath">

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1,39 +0,0 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://docbook.org/ns/docbook"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="d">
<xsl:template name="component.title">
<xsl:param name="node" select="."/>
<xsl:variable name="level">
<xsl:choose>
<xsl:when test="ancestor::d:section">
<xsl:value-of select="count(ancestor::d:section)+1"/>
</xsl:when>
<xsl:when test="ancestor::d:sect5">6</xsl:when>
<xsl:when test="ancestor::d:sect4">5</xsl:when>
<xsl:when test="ancestor::d:sect3">4</xsl:when>
<xsl:when test="ancestor::d:sect2">3</xsl:when>
<xsl:when test="ancestor::d:sect1">2</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="h{$level+1}" namespace="http://www.w3.org/1999/xhtml">
<xsl:attribute name="class">title</xsl:attribute>
<xsl:if test="$generate.id.attributes = 0">
<xsl:call-template name="anchor">
<xsl:with-param name="node" select="$node"/>
<xsl:with-param name="conditional" select="0"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="$node" mode="object.title.markup">
<xsl:with-param name="allow-anchors" select="1"/>
</xsl:apply-templates>
<xsl:call-template name="permalink">
<xsl:with-param name="node" select="$node"/>
</xsl:call-template>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,64 +0,0 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl" />
<!-- check project-plan.sh for how this is generated, needed to tweak
the cover page
-->
<xsl:include href="/tmp/titlepage.xsl"/>
<!-- To force a page break in document, i.e per section add a
<?hard-pagebreak?> tag.
-->
<xsl:template match="processing-instruction('hard-pagebreak')">
<fo:block break-before='page' />
</xsl:template>
<!--Fix for defualt indent getting TOC all wierd..
See http://sources.redhat.com/ml/docbook-apps/2005-q1/msg00455.html
FIXME: must be a better fix
-->
<xsl:param name="body.start.indent" select="'0'"/>
<!--<xsl:param name="title.margin.left" select="'0'"/>-->
<!-- stop long-ish header titles getting wrapped -->
<xsl:param name="header.column.widths">1 10 1</xsl:param>
<!-- customise headers and footers a little -->
<xsl:template name="head.sep.rule">
<xsl:if test="$header.rule != 0">
<xsl:attribute name="border-bottom-width">0.5pt</xsl:attribute>
<xsl:attribute name="border-bottom-style">solid</xsl:attribute>
<xsl:attribute name="border-bottom-color">#cccccc</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template name="foot.sep.rule">
<xsl:if test="$footer.rule != 0">
<xsl:attribute name="border-top-width">0.5pt</xsl:attribute>
<xsl:attribute name="border-top-style">solid</xsl:attribute>
<xsl:attribute name="border-top-color">#cccccc</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:attribute-set name="header.content.properties">
<xsl:attribute name="color">#cccccc</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="footer.content.properties">
<xsl:attribute name="color">#cccccc</xsl:attribute>
</xsl:attribute-set>
<!-- general settings -->
<xsl:param name="fop1.extensions" select="1"></xsl:param>
<xsl:param name="paper.type" select="'A4'"></xsl:param>
<xsl:param name="section.autolabel" select="1"></xsl:param>
<xsl:param name="body.font.family" select="'verasans'"></xsl:param>
<xsl:param name="title.font.family" select="'verasans'"></xsl:param>
<xsl:param name="monospace.font.family" select="'veramono'"></xsl:param>
</xsl:stylesheet>

View File

@@ -1,25 +0,0 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://docbook.org/ns/docbook"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="d">
<xsl:template name="division.title">
<xsl:param name="node" select="."/>
<h1>
<xsl:attribute name="class">title</xsl:attribute>
<xsl:call-template name="anchor">
<xsl:with-param name="node" select="$node"/>
<xsl:with-param name="conditional" select="0"/>
</xsl:call-template>
<xsl:apply-templates select="$node" mode="object.title.markup">
<xsl:with-param name="allow-anchors" select="1"/>
</xsl:apply-templates>
<xsl:call-template name="permalink">
<xsl:with-param name="node" select="$node"/>
</xsl:call-template>
</h1>
</xsl:template>
</xsl:stylesheet>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,58 +0,0 @@
<fop version="1.0">
<!-- Strict user configuration -->
<strict-configuration>true</strict-configuration>
<!-- Strict FO validation -->
<strict-validation>true</strict-validation>
<!--
Set the baseDir so common/openedhand.svg references in plans still
work ok. Note, relative file references to current dir should still work.
-->
<base>../template</base>
<font-base>../template</font-base>
<!-- Source resolution in dpi (dots/pixels per inch) for determining the
size of pixels in SVG and bitmap images, default: 72dpi -->
<!-- <source-resolution>72</source-resolution> -->
<!-- Target resolution in dpi (dots/pixels per inch) for specifying the
target resolution for generated bitmaps, default: 72dpi -->
<!-- <target-resolution>72</target-resolution> -->
<!-- default page-height and page-width, in case
value is specified as auto -->
<default-page-settings height="11in" width="8.26in"/>
<!-- <use-cache>false</use-cache> -->
<renderers>
<renderer mime="application/pdf">
<fonts>
<font metrics-file="VeraMono.xml"
kerning="yes"
embed-url="VeraMono.ttf">
<font-triplet name="veramono" style="normal" weight="normal"/>
</font>
<font metrics-file="VeraMoBd.xml"
kerning="yes"
embed-url="VeraMoBd.ttf">
<font-triplet name="veramono" style="normal" weight="bold"/>
</font>
<font metrics-file="Vera.xml"
kerning="yes"
embed-url="Vera.ttf">
<font-triplet name="verasans" style="normal" weight="normal"/>
<font-triplet name="verasans" style="normal" weight="bold"/>
<font-triplet name="verasans" style="italic" weight="normal"/>
<font-triplet name="verasans" style="italic" weight="bold"/>
</font>
<auto-detect/>
</fonts>
</renderer>
</renderers>
</fop>

View File

@@ -1,21 +0,0 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://docbook.org/ns/docbook"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="d">
<xsl:template name="formal.object.heading">
<xsl:param name="object" select="."/>
<xsl:param name="title">
<xsl:apply-templates select="$object" mode="object.title.markup">
<xsl:with-param name="allow-anchors" select="1"/>
</xsl:apply-templates>
</xsl:param>
<p class="title">
<b><xsl:copy-of select="$title"/></b>
<xsl:call-template name="permalink">
<xsl:with-param name="node" select="$object"/>
</xsl:call-template>
</p>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,14 +0,0 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://docbook.org/ns/docbook"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:template match="glossentry/glossterm">
<xsl:apply-imports/>
<xsl:if test="$generate.permalink != 0">
<xsl:call-template name="permalink">
<xsl:with-param name="node" select=".."/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="generate.permalink" select="1"/>
<xsl:param name="permalink.text"></xsl:param>
<xsl:template name="permalink">
<xsl:param name="node"/>
<xsl:if test="$generate.permalink != '0'">
<span class="permalink">
<a alt="Permalink" title="Permalink">
<xsl:attribute name="href">
<xsl:call-template name="href.target">
<xsl:with-param name="object" select="$node"/>
</xsl:call-template>
</xsl:attribute>
<xsl:copy-of select="$permalink.text"/>
</a>
</span>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,55 +0,0 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://docbook.org/ns/docbook"
xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="d">
<xsl:template name="section.title">
<xsl:variable name="section"
select="(ancestor::section |
ancestor::simplesect|
ancestor::sect1|
ancestor::sect2|
ancestor::sect3|
ancestor::sect4|
ancestor::sect5)[last()]"/>
<xsl:variable name="renderas">
<xsl:choose>
<xsl:when test="$section/@renderas = 'sect1'">1</xsl:when>
<xsl:when test="$section/@renderas = 'sect2'">2</xsl:when>
<xsl:when test="$section/@renderas = 'sect3'">3</xsl:when>
<xsl:when test="$section/@renderas = 'sect4'">4</xsl:when>
<xsl:when test="$section/@renderas = 'sect5'">5</xsl:when>
<xsl:otherwise><xsl:value-of select="''"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="level">
<xsl:choose>
<xsl:when test="$renderas != ''">
<xsl:value-of select="$renderas"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="section.level">
<xsl:with-param name="node" select="$section"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="section.heading">
<xsl:with-param name="section" select="$section"/>
<xsl:with-param name="level" select="$level"/>
<xsl:with-param name="title">
<xsl:apply-templates select="$section" mode="object.title.markup">
<xsl:with-param name="allow-anchors" select="1"/>
</xsl:apply-templates>
<xsl:if test="$level &gt; 0">
<xsl:call-template name="permalink">
<xsl:with-param name="node" select="$section"/>
</xsl:call-template>
</xsl:if>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +0,0 @@
#!/bin/sh
if [ -z "$1" -o -z "$2" ]; then
echo "usage: [-v] $0 <docbook file> <templatedir>"
echo
echo "*NOTE* you need xsltproc, fop and nwalsh docbook stylesheets"
echo " installed for this to work!"
echo
exit 0
fi
FO=`echo $1 | sed s/.xml/.fo/` || exit 1
PDF=`echo $1 | sed s/.xml/.pdf/` || exit 1
TEMPLATEDIR=$2
##
# 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"
# Creates a temporary XSL stylesheet based on titlepage.xsl
xsltproc -o /tmp/titlepage.xsl \
--xinclude \
$XSL_BASE_URI/template/titlepage.xsl \
$TEMPLATEDIR/titlepage.templates.xml || exit 1
# Creates the file needed for FOP
xsltproc --xinclude \
--stringparam hyphenate false \
--stringparam formal.title.placement "figure after" \
--stringparam ulink.show 1 \
--stringparam body.font.master 9 \
--stringparam title.font.master 11 \
--stringparam draft.watermark.image "$TEMPLATEDIR/draft.png" \
--stringparam chapter.autolabel 1 \
--stringparam appendix.autolabel A \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--output $FO \
$TEMPLATEDIR/db-pdf.xsl \
$1 || exit 1
# Invokes the Java version of FOP. Uses the additional configuration file common/fop-config.xml
fop -c $TEMPLATEDIR/fop-config.xml -fo $FO -pdf $PDF || exit 1
rm -f $FO
rm -f /tmp/titlepage.xsl
echo
echo " #### Success! $PDF ready. ####"
echo

View File

@@ -21,7 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
__version__ = "1.24.0"
__version__ = "1.20.0"
import sys
if sys.version_info < (2, 7, 3):
@@ -99,11 +99,12 @@ def error(*args):
def fatal(*args):
logger.critical(''.join(args))
raise BBHandledException()
sys.exit(1)
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 emitted
as deprecated. It will result in a warning being emmitted
when the function is used."""
import warnings
@@ -140,3 +141,6 @@ def deprecate_import(current, modulename, fromlist, renames = None):
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"])

View File

@@ -23,14 +23,13 @@
# 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
#Based on functions from the base bb module, Copyright 2003 Holger Schurig
import os
import sys
import logging
import shlex
import glob
import time
import bb
import bb.msg
import bb.process
@@ -42,8 +41,9 @@ logger = logging.getLogger('BitBake.Build')
NULL = open(os.devnull, 'r+')
# When we execute a Python function, we'd like certain things
# in all namespaces, hence we add them to __builtins__.
# 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
@@ -75,7 +75,6 @@ class TaskBase(event.Event):
self.taskfile = d.getVar("FILE", True)
self.taskname = self._task
self.logfile = logfile
self.time = time.time()
event.Event.__init__(self)
self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
@@ -142,7 +141,7 @@ class LogTee(object):
self.outfile.flush()
def exec_func(func, d, dirs = None):
"""Execute a BB 'function'"""
"""Execute an BB 'function'"""
body = d.getVar(func)
if not body:
@@ -155,7 +154,6 @@ def exec_func(func, d, dirs = None):
if cleandirs:
for cdir in d.expand(cleandirs).split():
bb.utils.remove(cdir, True)
bb.utils.mkdirhier(cdir)
if dirs is None:
dirs = flags.get('dirs')
@@ -174,7 +172,7 @@ def exec_func(func, d, dirs = None):
lockflag = flags.get('lockfiles')
if lockflag:
lockfiles = [f for f in d.expand(lockflag).split()]
lockfiles = [d.expand(f) for f in lockflag.split()]
else:
lockfiles = None
@@ -227,7 +225,7 @@ def exec_func_python(func, d, runfile, cwd=None):
code = _functionfmt.format(function=func, body=d.getVar(func, True))
bb.utils.mkdirhier(os.path.dirname(runfile))
with open(runfile, 'w') as script:
bb.data.emit_func_python(func, script, d)
script.write(code)
if cwd:
try:
@@ -241,9 +239,10 @@ def exec_func_python(func, d, runfile, cwd=None):
try:
comp = utils.better_compile(code, func, bbfile)
utils.better_exec(comp, {"d": d}, code, bbfile)
except (bb.parse.SkipRecipe, bb.build.FuncFailed):
raise
except:
if sys.exc_info()[0] in (bb.parse.SkipPackage, bb.build.FuncFailed):
raise
raise FuncFailed(func, None)
finally:
bb.debug(2, "Python function %s finished" % func)
@@ -254,8 +253,19 @@ def exec_func_python(func, d, runfile, cwd=None):
except OSError:
pass
def shell_trap_code():
return '''#!/bin/sh\n
def exec_func_shell(func, d, runfile, cwd=None):
"""Execute a shell function from the metadata
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.
"""
# Don't let the emitted shell script override PWD
d.delVarFlag('PWD', 'export')
with open(runfile, 'w') as script:
script.write('''#!/bin/sh\n
# Emit a useful diagnostic if something fails:
bb_exit_handler() {
ret=$?
@@ -271,28 +281,14 @@ bb_exit_handler() {
}
trap 'bb_exit_handler' 0
set -e
'''
def exec_func_shell(func, d, runfile, cwd=None):
"""Execute a shell function from the metadata
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.
"""
# Don't let the emitted shell script override PWD
d.delVarFlag('PWD', 'export')
with open(runfile, 'w') as script:
script.write(shell_trap_code())
''')
bb.data.emit_func(func, script, d)
if bb.msg.loggerVerboseLogs:
script.write("set -x\n")
if cwd:
script.write("cd '%s'\n" % cwd)
script.write("cd %s\n" % cwd)
script.write("%s\n" % func)
script.write('''
# cleanup
@@ -330,7 +326,7 @@ def _task_data(fn, task, d):
localdata.setVar('BB_FILENAME', fn)
localdata.setVar('BB_CURRENTTASK', task[3:])
localdata.setVar('OVERRIDES', 'task-%s:%s' %
(task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
(task[3:], d.getVar('OVERRIDES', False)))
localdata.finalize()
bb.data.expandKeys(localdata)
return localdata
@@ -416,7 +412,7 @@ def _exec_task(fn, task, d, quieterr):
os.dup2(logfile.fileno(), oso[1])
os.dup2(logfile.fileno(), ose[1])
# Ensure Python logging goes to the logfile
# Ensure python logging goes to the logfile
handler = logging.StreamHandler(logfile)
handler.setFormatter(logformatter)
# Always enable full debug output into task logfiles
@@ -476,12 +472,12 @@ def _exec_task(fn, task, d, quieterr):
return 0
def exec_task(fn, task, d, profile = False):
try:
try:
quieterr = False
if d.getVarFlag(task, "quieterrors") is not None:
quieterr = True
if profile:
if profile:
profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
try:
import cProfile as profile
@@ -505,7 +501,7 @@ def exec_task(fn, task, d, profile = False):
event.fire(failedevent, d)
return 1
def stamp_internal(taskname, d, file_name, baseonly=False):
def stamp_internal(taskname, d, file_name):
"""
Internal stamp helper function
Makes sure the stamp directory exists
@@ -526,9 +522,6 @@ def stamp_internal(taskname, d, file_name, baseonly=False):
file_name = d.getVar('BB_FILENAME', True)
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
if baseonly:
return stamp
if not stamp:
return
@@ -582,7 +575,7 @@ def make_stamp(task, d, file_name = None):
if name.endswith('.taint'):
continue
os.unlink(name)
stamp = stamp_internal(task, d, file_name)
# Remove the file and recreate to force timestamp
# change on broken NFS filesystems
@@ -593,9 +586,8 @@ def make_stamp(task, d, file_name = None):
# If we're in task context, write out a signature file for each task
# as it completes
if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
stampbase = stamp_internal(task, d, None, True)
file_name = d.getVar('BB_FILENAME', True)
bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
bb.parse.siggen.dump_sigtask(file_name, task, d.getVar('STAMP', True), True)
def del_stamp(task, d, file_name = None):
"""
@@ -630,7 +622,7 @@ def stampfile(taskname, d, file_name = None):
"""
return stamp_internal(taskname, d, file_name)
def add_tasks(tasklist, deltasklist, d):
def add_tasks(tasklist, d):
task_deps = d.getVar('_task_deps')
if not task_deps:
task_deps = {}
@@ -641,10 +633,6 @@ def add_tasks(tasklist, deltasklist, d):
for task in tasklist:
task = d.expand(task)
if task in deltasklist:
continue
d.setVarFlag(task, 'task', 1)
if not task in task_deps['tasks']:
@@ -676,36 +664,9 @@ def add_tasks(tasklist, deltasklist, d):
# don't assume holding a reference
d.setVar('_task_deps', task_deps)
def addtask(task, before, after, d):
if task[:3] != "do_":
task = "do_" + task
def remove_task(task, kill, d):
"""Remove an BB 'task'.
d.setVarFlag(task, "task", 1)
bbtasks = d.getVar('__BBTASKS') or []
if not task in bbtasks:
bbtasks.append(task)
d.setVar('__BBTASKS', bbtasks)
existing = d.getVarFlag(task, "deps") or []
if after is not None:
# set up deps for function
for entry in after.split():
if entry not in existing:
existing.append(entry)
d.setVarFlag(task, "deps", existing)
if before is not None:
# set up things that depend on this func
for entry in before.split():
existing = d.getVarFlag(entry, "deps") or []
if task not in existing:
d.setVarFlag(entry, "deps", [task] + existing)
def deltask(task, d):
if task[:3] != "do_":
task = "do_" + task
bbtasks = d.getVar('__BBDELTASKS') or []
if not task in bbtasks:
bbtasks.append(task)
d.setVar('__BBDELTASKS', bbtasks)
If kill is 1, also remove tasks that depend on this task."""
d.delVarFlag(task, 'task')

View File

@@ -43,7 +43,7 @@ except ImportError:
logger.info("Importing cPickle failed. "
"Falling back to a very slow implementation.")
__cache_version__ = "148"
__cache_version__ = "147"
def getCacheFile(path, filename, data_hash):
return os.path.join(path, filename + "." + data_hash)
@@ -225,16 +225,14 @@ class CoreRecipeInfo(RecipeInfoCommon):
for package in self.packages_dynamic:
cachedata.packages_dynamic[package].append(fn)
# Build hash of runtime depends and recommends
# Build hash of runtime depends and rececommends
for package in self.packages + [self.pn]:
cachedata.rundeps[fn][package] = list(self.rdepends) + self.rdepends_pkg[package]
cachedata.runrecs[fn][package] = list(self.rrecommends) + self.rrecommends_pkg[package]
# Collect files we may need for possible world-dep
# calculations
if self.not_world:
logger.debug(1, "EXCLUDE FROM WORLD: %s", fn)
else:
if not self.not_world:
cachedata.possible_world.append(fn)
# create a collection of all targets for sanity checking
@@ -261,7 +259,7 @@ class Cache(object):
def __init__(self, data, data_hash, caches_array):
# Pass caches_array information into Cache Constructor
# It will be used later for deciding whether we
# It will be used in later for deciding whether we
# need extra cache file dump/load support
self.caches_array = caches_array
self.cachedir = data.getVar("CACHE", True)
@@ -529,11 +527,8 @@ class Cache(object):
if hasattr(info_array[0], 'file_checksums'):
for _, fl in info_array[0].file_checksums.items():
for f in fl.split():
if "*" in f:
continue
f, exist = f.split(":")
if (exist == "True" and not os.path.exists(f)) or (exist == "False" and os.path.exists(f)):
logger.debug(2, "Cache: %s's file checksum list file %s changed",
if not os.path.exists(f):
logger.debug(2, "Cache: %s's file checksum list file %s was removed",
fn, f)
self.remove(fn)
return False
@@ -623,13 +618,10 @@ class Cache(object):
def mtime(cachefile):
return bb.parse.cached_mtime_noerror(cachefile)
def add_info(self, filename, info_array, cacheData, parsed=None, watcher=None):
def add_info(self, filename, info_array, cacheData, parsed=None):
if isinstance(info_array[0], CoreRecipeInfo) and (not info_array[0].skipped):
cacheData.add_from_recipeinfo(filename, info_array)
if watcher:
watcher(info_array[0].file_depends)
if not self.has_cache:
return
@@ -700,7 +692,7 @@ def init(cooker):
* Its mtime
* The mtimes of all its dependencies
* Whether it caused a parse.SkipRecipe exception
* Whether it caused a parse.SkipPackage exception
Files causing parsing errors are evicted from the cache.
@@ -770,6 +762,16 @@ class MultiProcessCache(object):
self.cachedata = data
def internSet(self, items):
new = set()
for i in items:
new.add(intern(i))
return new
def compress_keys(self, data):
# Override in subclasses if desired
return
def create_cachedata(self):
data = [{}]
return data
@@ -810,7 +812,15 @@ class MultiProcessCache(object):
glf = bb.utils.lockfile(self.cachefile + ".lock")
data = self.cachedata
try:
with open(self.cachefile, "rb") as f:
p = pickle.Unpickler(f)
data, version = p.load()
except (IOError, EOFError):
data, version = None, None
if version != self.__class__.CACHE_VERSION:
data = self.create_cachedata()
for f in [y for y in os.listdir(os.path.dirname(self.cachefile)) if y.startswith(os.path.basename(self.cachefile) + '-')]:
f = os.path.join(os.path.dirname(self.cachefile), f)
@@ -819,16 +829,16 @@ class MultiProcessCache(object):
p = pickle.Unpickler(fd)
extradata, version = p.load()
except (IOError, EOFError):
os.unlink(f)
continue
extradata, version = self.create_cachedata(), None
if version != self.__class__.CACHE_VERSION:
os.unlink(f)
continue
self.merge_data(extradata, data)
os.unlink(f)
self.compress_keys(data)
with open(self.cachefile, "wb") as f:
p = pickle.Pickler(f, -1)
p.dump([data, self.__class__.CACHE_VERSION])

View File

@@ -33,82 +33,9 @@ def check_indent(codestr):
return codestr
# Basically pickle, in python 2.7.3 at least, does badly with data duplication
# upon pickling and unpickling. Combine this with duplicate objects and things
# are a mess.
#
# When the sets are originally created, python calls intern() on the set keys
# which significantly improves memory usage. Sadly the pickle/unpickle process
# doesn't call intern() on the keys and results in the same strings being duplicated
# in memory. This also means pickle will save the same string multiple times in
# the cache file.
#
# By having shell and python cacheline objects with setstate/getstate, we force
# the object creation through our own routine where we can call intern (via internSet).
#
# We also use hashable frozensets and ensure we use references to these so that
# duplicates can be removed, both in memory and in the resulting pickled data.
#
# By playing these games, the size of the cache file shrinks dramatically
# meaning faster load times and the reloaded cache files also consume much less
# memory. Smaller cache files, faster load times and lower memory usage is good.
#
# A custom getstate/setstate using tuples is actually worth 15% cachesize by
# avoiding duplication of the attribute names!
class SetCache(object):
def __init__(self):
self.setcache = {}
def internSet(self, items):
new = []
for i in items:
new.append(intern(i))
s = frozenset(new)
if hash(s) in self.setcache:
return self.setcache[hash(s)]
self.setcache[hash(s)] = s
return s
codecache = SetCache()
class pythonCacheLine(object):
def __init__(self, refs, execs, contains):
self.refs = codecache.internSet(refs)
self.execs = codecache.internSet(execs)
self.contains = {}
for c in contains:
self.contains[c] = codecache.internSet(contains[c])
def __getstate__(self):
return (self.refs, self.execs, self.contains)
def __setstate__(self, state):
(refs, execs, contains) = state
self.__init__(refs, execs, contains)
def __hash__(self):
l = (hash(self.refs), hash(self.execs))
for c in sorted(self.contains.keys()):
l = l + (c, hash(self.contains[c]))
return hash(l)
class shellCacheLine(object):
def __init__(self, execs):
self.execs = codecache.internSet(execs)
def __getstate__(self):
return (self.execs)
def __setstate__(self, state):
(execs) = state
self.__init__(execs)
def __hash__(self):
return hash(self.execs)
class CodeParserCache(MultiProcessCache):
cache_file_name = "bb_codeparser.dat"
CACHE_VERSION = 7
CACHE_VERSION = 3
def __init__(self):
MultiProcessCache.__init__(self)
@@ -117,27 +44,6 @@ class CodeParserCache(MultiProcessCache):
self.pythoncacheextras = self.cachedata_extras[0]
self.shellcacheextras = self.cachedata_extras[1]
# To avoid duplication in the codeparser cache, keep
# a lookup of hashes of objects we already have
self.pythoncachelines = {}
self.shellcachelines = {}
def newPythonCacheLine(self, refs, execs, contains):
cacheline = pythonCacheLine(refs, execs, contains)
h = hash(cacheline)
if h in self.pythoncachelines:
return self.pythoncachelines[h]
self.pythoncachelines[h] = cacheline
return cacheline
def newShellCacheLine(self, execs):
cacheline = shellCacheLine(execs)
h = hash(cacheline)
if h in self.shellcachelines:
return self.shellcachelines[h]
self.shellcachelines[h] = cacheline
return cacheline
def init_cache(self, d):
MultiProcessCache.init_cache(self, d)
@@ -145,6 +51,23 @@ class CodeParserCache(MultiProcessCache):
self.pythoncache = self.cachedata[0]
self.shellcache = self.cachedata[1]
def compress_keys(self, data):
# When the dicts are originally created, python calls intern() on the set keys
# which significantly improves memory usage. Sadly the pickle/unpickle process
# doesn't call intern() on the keys and results in the same strings being duplicated
# in memory. This also means pickle will save the same string multiple times in
# the cache file. By interning the data here, the cache file shrinks dramatically
# meaning faster load times and the reloaded cache files also consume much less
# memory. This is worth any performance hit from this loops and the use of the
# intern() data storage.
# Python 3.x may behave better in this area
for h in data[0]:
data[0][h]["refs"] = self.internSet(data[0][h]["refs"])
data[0][h]["execs"] = self.internSet(data[0][h]["execs"])
for h in data[1]:
data[1][h]["execs"] = self.internSet(data[1][h]["execs"])
return
def create_cachedata(self):
data = [{}, {}]
return data
@@ -177,8 +100,8 @@ class BufferedLogger(Logger):
self.buffer = []
class PythonParser():
getvars = (".getVar", ".appendVar", ".prependVar")
containsfuncs = ("bb.utils.contains", "base_contains", "oe.utils.contains", "bb.utils.contains_any")
getvars = ("d.getVar", "bb.data.getVar", "data.getVar", "d.appendVar", "d.prependVar")
containsfuncs = ("bb.utils.contains", "base_contains", "oe.utils.contains")
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
def warn(self, func, arg):
@@ -197,15 +120,9 @@ class PythonParser():
def visit_Call(self, node):
name = self.called_node_name(node.func)
if name and name.endswith(self.getvars) or name in self.containsfuncs:
if name in self.getvars or name in self.containsfuncs:
if isinstance(node.args[0], ast.Str):
varname = node.args[0].s
if name in self.containsfuncs and isinstance(node.args[1], ast.Str):
if varname not in self.contains:
self.contains[varname] = set()
self.contains[varname].add(node.args[1].s)
else:
self.references.add(node.args[0].s)
self.var_references.add(node.args[0].s)
else:
self.warn(node.func, node.args[0])
elif name in self.execfuncs:
@@ -230,11 +147,11 @@ class PythonParser():
break
def __init__(self, name, log):
self.var_references = set()
self.var_execs = set()
self.contains = {}
self.execs = set()
self.references = set()
self.log = BufferedLogger('BitBake.Data.PythonParser', logging.DEBUG, log)
self.log = BufferedLogger('BitBake.Data.%s' % name, logging.DEBUG, log)
self.unhandled_message = "in call of %s, argument '%s' is not a string literal"
self.unhandled_message = "while parsing %s, %s" % (name, self.unhandled_message)
@@ -243,21 +160,16 @@ class PythonParser():
h = hash(str(node))
if h in codeparsercache.pythoncache:
self.references = set(codeparsercache.pythoncache[h].refs)
self.execs = set(codeparsercache.pythoncache[h].execs)
self.contains = {}
for i in codeparsercache.pythoncache[h].contains:
self.contains[i] = set(codeparsercache.pythoncache[h].contains[i])
self.references = codeparsercache.pythoncache[h]["refs"]
self.execs = codeparsercache.pythoncache[h]["execs"]
return
if h in codeparsercache.pythoncacheextras:
self.references = set(codeparsercache.pythoncacheextras[h].refs)
self.execs = set(codeparsercache.pythoncacheextras[h].execs)
self.contains = {}
for i in codeparsercache.pythoncacheextras[h].contains:
self.contains[i] = set(codeparsercache.pythoncacheextras[h].contains[i])
self.references = codeparsercache.pythoncacheextras[h]["refs"]
self.execs = codeparsercache.pythoncacheextras[h]["execs"]
return
code = compile(check_indent(str(node)), "<string>", "exec",
ast.PyCF_ONLY_AST)
@@ -265,9 +177,12 @@ class PythonParser():
if n.__class__.__name__ == "Call":
self.visit_Call(n)
self.execs.update(self.var_execs)
self.references.update(self.var_references)
self.references.update(self.var_execs)
codeparsercache.pythoncacheextras[h] = codeparsercache.newPythonCacheLine(self.references, self.execs, self.contains)
codeparsercache.pythoncacheextras[h] = {}
codeparsercache.pythoncacheextras[h]["refs"] = self.references
codeparsercache.pythoncacheextras[h]["execs"] = self.execs
class ShellParser():
def __init__(self, name, log):
@@ -286,21 +201,13 @@ class ShellParser():
h = hash(str(value))
if h in codeparsercache.shellcache:
self.execs = set(codeparsercache.shellcache[h].execs)
self.execs = codeparsercache.shellcache[h]["execs"]
return self.execs
if h in codeparsercache.shellcacheextras:
self.execs = set(codeparsercache.shellcacheextras[h].execs)
self.execs = codeparsercache.shellcacheextras[h]["execs"]
return self.execs
self._parse_shell(value)
self.execs = set(cmd for cmd in self.allexecs if cmd not in self.funcdefs)
codeparsercache.shellcacheextras[h] = codeparsercache.newShellCacheLine(self.execs)
return self.execs
def _parse_shell(self, value):
try:
tokens, _ = pyshyacc.parse(value, eof=True, debug=False)
except pyshlex.NeedMore:
@@ -308,6 +215,12 @@ class ShellParser():
for token in tokens:
self.process_tokens(token)
self.execs = set(cmd for cmd in self.allexecs if cmd not in self.funcdefs)
codeparsercache.shellcacheextras[h] = {}
codeparsercache.shellcacheextras[h]["execs"] = self.execs
return self.execs
def process_tokens(self, tokens):
"""Process a supplied portion of the syntax tree as returned by
@@ -381,7 +294,7 @@ class ShellParser():
if part[0] in ('`', '$('):
command = pyshlex.wordtree_as_string(part[1:-1])
self._parse_shell(command)
self.parse_shell(command)
if word[0] in ("cmd_name", "cmd_word"):
if word in words:
@@ -400,7 +313,7 @@ class ShellParser():
self.log.debug(1, self.unhandled_template % cmd)
elif cmd == "eval":
command = " ".join(word for _, word in words[1:])
self._parse_shell(command)
self.parse_shell(command)
else:
self.allexecs.add(cmd)
break

View File

@@ -86,11 +86,6 @@ class Command:
def runAsyncCommand(self):
try:
if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
# updateCache will trigger a shutdown of the parser
# and then raise BBHandledException triggering an exit
self.cooker.updateCache()
return False
if self.currentAsyncCommand is not None:
(command, options) = self.currentAsyncCommand
commandmethod = getattr(CommandsAsync, command)
@@ -199,11 +194,18 @@ class CommandsSync:
"""
command.cooker.disableDataTracking()
def setPrePostConfFiles(self, command, params):
prefiles = params[0].split()
postfiles = params[1].split()
command.cooker.configuration.prefile = prefiles
command.cooker.configuration.postfile = postfiles
def initCooker(self, command, params):
"""
Init the cooker to initial state with nothing parsed
"""
command.cooker.initialize()
def resetCooker(self, command, params):
"""
Reset the cooker to its initial state, thus forcing a reparse for
any async command that has the needcache property set to True
"""
command.cooker.reset()
def getCpuCount(self, command, params):
"""
@@ -260,21 +262,6 @@ class CommandsSync:
mask = params[3]
return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
def setFeatures(self, command, params):
"""
Set the cooker features to include the passed list of features
"""
features = params[0]
command.cooker.setFeatures(features)
# although we change the internal state of the cooker, this is transparent since
# we always take and leave the cooker in state.initial
setFeatures.readonly = True
def updateConfig(self, command, params):
options = params[0]
command.cooker.updateConfigOpts(options)
class CommandsAsync:
"""
A class of asynchronous commands
@@ -431,6 +418,18 @@ class CommandsAsync:
command.finishAsyncCommand()
compareRevisions.needcache = True
def parseConfigurationFiles(self, command, params):
"""
Parse the configuration files
"""
prefiles = params[0].split()
postfiles = params[1].split()
command.cooker.configuration.prefile = prefiles
command.cooker.configuration.postfile = postfiles
command.cooker.loadConfigurationData()
command.finishAsyncCommand()
parseConfigurationFiles.needcache = False
def triggerEvent(self, command, params):
"""
Trigger a certain event
@@ -440,12 +439,3 @@ class CommandsAsync:
command.currentAsyncCommand = None
triggerEvent.needcache = False
def resetCooker(self, command, params):
"""
Reset the cooker to its initial state, thus forcing a reparse for
any async command that has the needcache property set to True
"""
command.cooker.reset()
command.finishAsyncCommand()
resetCooker.needcache = False

View File

@@ -37,11 +37,7 @@ from collections import defaultdict
import bb, bb.exceptions, bb.command
from bb import utils, data, parse, event, cache, providers, taskdata, runqueue
import Queue
import signal
import subprocess
import errno
import prserv.serv
import pyinotify
logger = logging.getLogger("BitBake")
collectlog = logging.getLogger("BitBake.Collection")
@@ -65,7 +61,7 @@ class CollectionError(bb.BBHandledException):
"""
class state:
initial, parsing, running, shutdown, forceshutdown, stopped, error = range(7)
initial, parsing, running, shutdown, forceshutdown, stopped = range(6)
class SkippedPackage:
@@ -85,7 +81,7 @@ class SkippedPackage:
class CookerFeatures(object):
_feature_list = [HOB_EXTRA_CACHES, SEND_DEPENDS_TREE, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = range(4)
_feature_list = [HOB_EXTRA_CACHES, SEND_DEPENDS_TREE, BASEDATASTORE_TRACKING] = range(3)
def __init__(self):
self._features=set()
@@ -114,42 +110,14 @@ class BBCooker:
Manages one bitbake build run
"""
def __init__(self, configuration, featureSet = []):
def __init__(self, configuration):
self.recipecache = None
self.skiplist = {}
self.featureset = CookerFeatures()
for f in featureSet:
self.featureset.setFeature(f)
self.configuration = configuration
self.configwatcher = pyinotify.WatchManager()
self.configwatcher.bbseen = []
self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
self.watcher = pyinotify.WatchManager()
self.watcher.bbseen = []
self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
self.initConfigurationData()
self.inotify_modified_files = []
def _process_inotify_updates(server, notifier_list, abort):
for n in notifier_list:
if n.check_events(timeout=0):
# read notified events and enqeue them
n.read_events()
n.process_events()
return 1.0
self.configuration.server_register_idlecallback(_process_inotify_updates, [self.confignotifier, self.notifier])
self.baseconfig_valid = True
self.parsecache_valid = False
self.loadConfigurationData()
# Take a lock so only one copy of bitbake can run against a given build
# directory at a time
@@ -157,14 +125,6 @@ class BBCooker:
self.lock = bb.utils.lockfile(lockfile, False, False)
if not self.lock:
bb.fatal("Only one copy of bitbake should be run against a build directory")
try:
self.lock.seek(0)
self.lock.truncate()
if len(configuration.interface) >= 2:
self.lock.write("%s:%s\n" % (configuration.interface[0], configuration.interface[1]));
self.lock.flush()
except:
pass
# TOSTOP must not be set or our children will hang when they output
fd = sys.stdout.fileno()
@@ -181,67 +141,11 @@ class BBCooker:
self.parser = None
signal.signal(signal.SIGTERM, self.sigterm_exception)
# Let SIGHUP exit as SIGTERM
signal.signal(signal.SIGHUP, self.sigterm_exception)
def config_notifications(self, event):
if not event.path in self.inotify_modified_files:
self.inotify_modified_files.append(event.path)
self.baseconfig_valid = False
def notifications(self, event):
if not event.path in self.inotify_modified_files:
self.inotify_modified_files.append(event.path)
self.parsecache_valid = False
def add_filewatch(self, deps, watcher=None):
if not watcher:
watcher = self.watcher
for i in deps:
f = i[0]
if f in watcher.bbseen:
continue
watcher.bbseen.append(f)
while True:
# We try and add watches for files that don't exist but if they did, would influence
# the parser. The parent directory of these files may not exist, in which case we need
# to watch any parent that does exist for changes.
try:
watcher.add_watch(f, self.watchmask, quiet=False)
break
except pyinotify.WatchManagerError as e:
if 'ENOENT' in str(e):
f = os.path.dirname(f)
watcher.bbseen.append(f)
continue
raise
def sigterm_exception(self, signum, stackframe):
if signum == signal.SIGTERM:
bb.warn("Cooker recieved SIGTERM, shutting down...")
elif signum == signal.SIGHUP:
bb.warn("Cooker recieved SIGHUP, shutting down...")
self.state = state.forceshutdown
def setFeatures(self, features):
# we only accept a new feature set if we're in state initial, so we can reset without problems
if self.state != state.initial:
raise Exception("Illegal state for feature set change")
original_featureset = list(self.featureset)
for feature in features:
self.featureset.setFeature(feature)
bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
if (original_featureset != list(self.featureset)):
self.reset()
def initConfigurationData(self):
self.state = state.initial
self.caches_array = []
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.enableDataTracking()
self.caches_array = []
all_extra_cache_names = []
# We hardcode all known cache types in a single place, here.
@@ -262,6 +166,21 @@ class BBCooker:
sys.exit("FATAL: Failed to import extra cache class '%s'." % cache_name)
self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
self.data = self.databuilder.data
def enableDataTracking(self):
self.configuration.tracking = True
self.data.enableTracking()
def disableDataTracking(self):
self.configuration.tracking = False
self.data.disableTracking()
def loadConfigurationData(self):
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.enableDataTracking()
self.initConfigurationData()
self.databuilder.parseBaseConfiguration()
self.data = self.databuilder.data
self.data_hash = self.databuilder.data_hash
@@ -276,15 +195,6 @@ class BBCooker:
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.disableDataTracking()
def enableDataTracking(self):
self.configuration.tracking = True
if hasattr(self, "data"):
self.data.enableTracking()
def disableDataTracking(self):
self.configuration.tracking = False
if hasattr(self, "data"):
self.data.disableTracking()
def modifyConfigurationVar(self, var, val, default_file, op):
if op == "append":
@@ -299,11 +209,20 @@ class BBCooker:
#add append var operation to the end of default_file
default_file = bb.cookerdata.findConfigFile(default_file, self.data)
total = "#added by hob"
with open(default_file, 'r') as f:
contents = f.readlines()
f.close()
total = ""
for c in contents:
total += c
total += "#added by hob"
total += "\n%s += \"%s\"\n" % (var, val)
with open(default_file, 'a') as f:
with open(default_file, 'w') as f:
f.write(total)
f.close()
#add to history
loginfo = {"op":append, "file":default_file, "line":total.count("\n")}
@@ -332,6 +251,7 @@ class BBCooker:
if topdir in conf_file:
with open(conf_file, 'r') as f:
contents = f.readlines()
f.close()
lines = self.data.varhistory.get_variable_lines(var, conf_file)
for line in lines:
@@ -357,8 +277,12 @@ class BBCooker:
for ii in range(begin_line, end_line):
contents[ii] = "#" + contents[ii]
total = ""
for c in contents:
total += c
with open(conf_file, 'w') as f:
f.writelines(contents)
f.write(total)
f.close()
if replaced == False:
#remove var from history
@@ -367,12 +291,21 @@ class BBCooker:
#add var to the end of default_file
default_file = bb.cookerdata.findConfigFile(default_file, self.data)
with open(default_file, 'r') as f:
contents = f.readlines()
f.close()
total = ""
for c in contents:
total += c
#add the variable on a single line, to be easy to replace the second time
total = "\n#added by hob"
total += "\n#added by hob"
total += "\n%s %s \"%s\"\n" % (var, op, val)
with open(default_file, 'a') as f:
with open(default_file, 'w') as f:
f.write(total)
f.close()
#add to history
loginfo = {"op":set, "file":default_file, "line":total.count("\n")}
@@ -386,6 +319,7 @@ class BBCooker:
if topdir in conf_file:
with open(conf_file, 'r') as f:
contents = f.readlines()
f.close()
lines = self.data.varhistory.get_variable_lines(var, conf_file)
for line in lines:
@@ -407,11 +341,13 @@ class BBCooker:
contents[begin_line] = "\n"
#remove var from history
self.data.varhistory.del_var_history(var, conf_file, line)
#remove variable
self.data.delVar(var)
total = ""
for c in contents:
total += c
with open(conf_file, 'w') as f:
f.writelines(contents)
f.write(total)
f.close()
def createConfigFile(self, name):
path = os.getcwd()
@@ -437,10 +373,6 @@ class BBCooker:
self.handleCollections( self.data.getVar("BBFILE_COLLECTIONS", True) )
def updateConfigOpts(self,options):
for o in options:
setattr(self.configuration, o, options[o])
def runCommands(self, server, data, abort):
"""
Run any queued asynchronous command
@@ -472,7 +404,7 @@ class BBCooker:
def showEnvironment(self, buildfile = None, pkgs_to_build = []):
"""
Show the outer or per-recipe environment
Show the outer or per-package environment
"""
fn = None
envdata = None
@@ -543,14 +475,9 @@ class BBCooker:
current = 0
runlist = []
for k in fulltargetlist:
ktask = task
if ":do_" in k:
k2 = k.split(":do_")
k = k2[0]
ktask = k2[1]
taskdata.add_provider(localdata, self.recipecache, k)
current += 1
runlist.append([k, "do_%s" % ktask])
runlist.append([k, "do_%s" % task])
bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
taskdata.add_unresolved(localdata, self.recipecache)
bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
@@ -807,9 +734,14 @@ class BBCooker:
logger.info("Task dependencies saved to 'task-depends.dot'")
def show_appends_with_no_recipes( self ):
recipes = set(os.path.basename(f)
for f in self.recipecache.pkg_fn.iterkeys())
recipes |= set(os.path.basename(f)
for f in self.skiplist.iterkeys())
appended_recipes = self.collection.appendlist.iterkeys()
appends_without_recipes = [self.collection.appendlist[recipe]
for recipe in self.collection.appendlist
if recipe not in self.collection.appliedappendlist]
for recipe in appended_recipes
if recipe not in recipes]
if appends_without_recipes:
appendlines = (' %s' % append
for appends in appends_without_recipes
@@ -888,6 +820,7 @@ class BBCooker:
or to find all machine configuration files one could call:
findFilesMatchingInDir(self, 'conf/machines', 'conf')
"""
import re
matches = []
p = re.compile(re.escape(filepattern))
@@ -1204,13 +1137,10 @@ class BBCooker:
def buildFileIdle(server, rq, abort):
msg = None
if abort or self.state == state.forceshutdown:
rq.finish_runqueue(True)
msg = "Forced shutdown"
elif self.state == state.shutdown:
rq.finish_runqueue(False)
msg = "Stopped build"
failures = 0
try:
retval = rq.execute_runqueue()
@@ -1223,7 +1153,7 @@ class BBCooker:
if not retval:
bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runq_fnid), buildname, item, failures), self.event_data)
self.command.finishAsyncCommand(msg)
self.command.finishAsyncCommand()
return False
if retval is True:
return True
@@ -1237,13 +1167,10 @@ class BBCooker:
"""
def buildTargetsIdle(server, rq, abort):
msg = None
if abort or self.state == state.forceshutdown:
rq.finish_runqueue(True)
msg = "Forced shutdown"
elif self.state == state.shutdown:
rq.finish_runqueue(False)
msg = "Stopped build"
failures = 0
try:
retval = rq.execute_runqueue()
@@ -1256,7 +1183,7 @@ class BBCooker:
if not retval:
bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runq_fnid), buildname, targets, failures), self.data)
self.command.finishAsyncCommand(msg)
self.command.finishAsyncCommand()
return False
if retval is True:
return True
@@ -1307,18 +1234,13 @@ class BBCooker:
else:
dest = image
basename = False
if base_image:
with open(base_image, 'r') as f:
require_line = f.readline()
p = re.compile("IMAGE_BASENAME *=")
for line in f:
if p.search(line):
basename = True
with open(dest, "w") as imagefile:
if base_image is None:
imagefile.write("inherit core-image\n")
imagefile.write("inherit image\n")
else:
topdir = self.data.getVar("TOPDIR")
if topdir in base_image:
@@ -1333,11 +1255,6 @@ class BBCooker:
description_var = "DESCRIPTION = \"" + description + "\"\n"
imagefile.write(description_var)
if basename:
# If this is overwritten in a inherited image, reset it to default
image_basename = "IMAGE_BASENAME = \"${PN}\"\n"
imagefile.write(image_basename)
self.state = state.initial
if timestamp:
return timestr
@@ -1347,28 +1264,12 @@ class BBCooker:
if self.state == state.running:
return
if self.state in (state.shutdown, state.forceshutdown, state.error):
if hasattr(self.parser, 'shutdown'):
self.parser.shutdown(clean=False, force = True)
if self.state in (state.shutdown, state.forceshutdown):
self.parser.shutdown(clean=False, force = True)
raise bb.BBHandledException()
if self.state != state.parsing:
# reload files for which we got notifications
for p in self.inotify_modified_files:
bb.parse.update_cache(p)
self.inotify_modified_files = []
if not self.baseconfig_valid:
logger.debug(1, "Reloading base configuration data")
self.initConfigurationData()
self.baseconfig_valid = True
self.parsecache_valid = False
if self.state != state.parsing and not self.parsecache_valid:
self.parseConfiguration ()
if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
bb.event.fire(bb.event.SanityCheck(False), self.data)
ignore = self.data.getVar("ASSUME_PROVIDED", True) or ""
self.recipecache.ignored_dependencies = set(ignore.split())
@@ -1380,12 +1281,9 @@ class BBCooker:
(filelist, masked) = self.collection.collect_bbfiles(self.data, self.event_data)
self.data.renameVar("__depends", "__base_depends")
self.add_filewatch(self.data.getVar("__base_depends"), self.configwatcher)
self.parser = CookerParser(self, filelist, masked)
self.parsecache_valid = True
self.state = state.parsing
self.state = state.parsing
if not self.parser.parse_next():
collectlog.debug(1, "parsing complete")
@@ -1407,11 +1305,6 @@ class BBCooker:
if len(pkgs_to_build) == 0:
raise NothingToBuild
ignore = (self.data.getVar("ASSUME_PROVIDED", True) or "").split()
for pkg in pkgs_to_build:
if pkg in ignore:
parselog.warn("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
if 'world' in pkgs_to_build:
self.buildWorldTargetList()
pkgs_to_build.remove('world')
@@ -1438,39 +1331,11 @@ class BBCooker:
self.prhost = prserv.serv.auto_start(self.data)
except prserv.serv.PRServiceConfigError:
bb.event.fire(CookerExit(), self.event_data)
self.state = state.error
return
def post_serve(self):
prserv.serv.auto_shutdown(self.data)
bb.event.fire(CookerExit(), self.event_data)
lockfile = self.lock.name
self.lock.close()
self.lock = None
while not self.lock:
with bb.utils.timeout(3):
self.lock = bb.utils.lockfile(lockfile, shared=False, retry=False, block=True)
if not self.lock:
# Some systems may not have lsof available
procs = None
try:
procs = subprocess.check_output(["lsof", '-w', lockfile], stderr=subprocess.STDOUT)
except OSError as e:
if e.errno != errno.ENOENT:
raise
if procs is None:
# Fall back to fuser if lsof is unavailable
try:
procs = subprocess.check_output(["fuser", '-v', lockfile], stderr=subprocess.STDOUT)
except OSError as e:
if e.errno != errno.ENOENT:
raise
msg = "Delaying shutdown due to active processes which appear to be holding bitbake.lock"
if procs:
msg += ":\n%s" % str(procs)
print(msg)
def shutdown(self, force = False):
if force:
@@ -1481,9 +1346,12 @@ class BBCooker:
def finishcommand(self):
self.state = state.initial
def reset(self):
def initialize(self):
self.initConfigurationData()
def reset(self):
self.loadConfigurationData()
def server_main(cooker, func, *args):
cooker.pre_serve()
@@ -1519,8 +1387,6 @@ class CookerExit(bb.event.Event):
class CookerCollectFiles(object):
def __init__(self, priorities):
self.appendlist = {}
self.bbappends = []
self.appliedappendlist = []
self.bbfile_config_priorities = priorities
def calc_bbfile_priority( self, filename, matched = None ):
@@ -1549,7 +1415,7 @@ class CookerCollectFiles(object):
for ignored in ('SCCS', 'CVS', '.svn'):
if ignored in dirs:
dirs.remove(ignored)
found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
found += [os.path.join(dir, f) for f in files if (f.endswith('.bb') or f.endswith('.bbappend'))]
return found
@@ -1614,7 +1480,6 @@ class CookerCollectFiles(object):
# Build a list of .bbappend files for each .bb file
for f in bbappend:
base = os.path.basename(f).replace('.bbappend', '.bb')
self.bbappends.append((base, f))
if not base in self.appendlist:
self.appendlist[base] = []
if f not in self.appendlist[base]:
@@ -1638,14 +1503,10 @@ class CookerCollectFiles(object):
"""
Returns a list of .bbappend files to apply to fn
"""
filelist = []
f = os.path.basename(fn)
for b in self.bbappends:
(bbappend, filename) = b
if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
self.appliedappendlist.append(bbappend)
filelist.append(filename)
return filelist
if f in self.appendlist:
return self.appendlist[f]
return []
def collection_priorities(self, pkgfns):
@@ -1664,10 +1525,10 @@ class CookerCollectFiles(object):
unmatched.add(regex)
def findmatch(regex):
for b in self.bbappends:
(bbfile, append) = b
if regex.match(append):
return True
for bbfile in self.appendlist:
for append in self.appendlist[bbfile]:
if regex.match(append):
return True
return False
for unmatch in unmatched.copy():
@@ -1976,7 +1837,7 @@ class CookerParser(object):
self.skipped += 1
self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
self.bb_cache.add_info(virtualfn, info_array, self.cooker.recipecache,
parsed=parsed, watcher = self.cooker.add_filewatch)
parsed=parsed)
return True
def reparse(self, filename):

View File

@@ -69,17 +69,6 @@ class ConfigParameters(object):
if bbpkgs:
self.options.pkgs_to_build.extend(bbpkgs.split())
def updateToServer(self, server):
options = {}
for o in ["abort", "tryaltconfigs", "force", "invalidate_stamp",
"verbose", "debug", "dry_run", "dump_signatures",
"debug_domains", "extra_assume_provided", "profile"]:
options[o] = getattr(self.options, o)
ret, error = server.runCommand(["updateConfig", options])
if error:
raise Exception("Unable to update the server configuration with local parameters: %s" % error)
def parseActions(self):
# Parse any commandline into actions
action = {'action':None, 'msg':None}
@@ -135,10 +124,9 @@ class CookerConfiguration(object):
self.profile = False
self.nosetscene = False
self.invalidate_stamp = False
self.dump_signatures = []
self.dump_signatures = False
self.dry_run = False
self.tracking = False
self.interface = []
self.env = {}
@@ -238,13 +226,10 @@ class CookerDataBuilder(object):
try:
self.parseConfigurationFiles(self.prefiles, self.postfiles)
except SyntaxError:
raise bb.BBHandledException
except bb.data_smart.ExpansionError as e:
logger.error(str(e))
raise bb.BBHandledException
sys.exit(1)
except Exception:
logger.exception("Error parsing configuration files")
raise bb.BBHandledException
sys.exit(1)
def _findLayerConf(self, data):
return findConfigFile("bblayers.conf", data)

View File

@@ -1,5 +1,5 @@
"""
Python Daemonizing helper
Python Deamonizing helper
Configurable daemon behaviors:
@@ -12,11 +12,8 @@ A failed call to fork() now raises an exception.
References:
1) Advanced Programming in the Unix Environment: W. Richard Stevens
http://www.apuebook.com/apue3e.html
2) The Linux Programming Interface: Michael Kerrisk
http://man7.org/tlpi/index.html
3) Unix Programming Frequently Asked Questions:
http://www.faqs.org/faqs/unix-faq/programmer/faq/
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
@@ -28,7 +25,7 @@ __version__ = "0.2"
# Standard Python modules.
import os # Miscellaneous OS interfaces.
import sys # System-specific parameters and functions.
import sys # System-specific parameters and functions.
# Default daemon parameters.
# File mode creation mask of the daemon.
@@ -131,7 +128,7 @@ def createDaemon(function, logfile):
# 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 exist, use
# number of open file descriptors to close. If it doesn't exists, use
# the default value (configurable).
#
# try:
@@ -149,7 +146,7 @@ def createDaemon(function, logfile):
# OR
#
# Use the getrlimit method to retrieve the maximum file descriptor number
# that can be opened by this process. If there is no limit on the
# that can be opened by this process. If there is not limit on the
# resource, use the default value.
#
import resource # Resource usage information.

View File

@@ -6,7 +6,7 @@ BitBake 'Data' implementations
Functions for interacting with the data structure used by the
BitBake build tools.
The expandKeys and update_data are the most expensive
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
@@ -15,7 +15,7 @@ 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 trade-off between speed and memory again but
This is a treade-off between speed and memory again but
the speed is more critical here.
"""
@@ -35,7 +35,7 @@ the speed is more critical here.
# 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
#Based on functions from the base bb module, Copyright 2003 Holger Schurig
import sys, os, re
if sys.argv[0][-5:] == "pydoc":
@@ -214,18 +214,11 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
o.write('unset %s\n' % varExpanded)
return 0
if val is None:
if not val:
return 0
val = str(val)
if varExpanded.startswith("BASH_FUNC_"):
varExpanded = varExpanded[10:-2]
val = val[3:] # Strip off "() "
o.write("%s() %s\n" % (varExpanded, val))
o.write("export -f %s\n" % (varExpanded))
return 1
if func:
# NOTE: should probably check for unbalanced {} within the var
o.write("%s() {\n%s\n}\n" % (varExpanded, val))
@@ -236,9 +229,8 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
# 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)
alter = re.sub('"', '\\"', val.strip())
alter = re.sub('\n', ' \\\n', alter)
alter = re.sub('\\$', '\\\\$', alter)
o.write('%s="%s"\n' % (varExpanded, alter))
return 0
@@ -283,47 +275,12 @@ def emit_func(func, o=sys.__stdout__, d = init()):
seen |= deps
newdeps = set()
for dep in deps:
if d.getVarFlag(dep, "func") and not d.getVarFlag(dep, "python"):
if d.getVarFlag(dep, "func"):
emit_var(dep, o, d, False) and o.write('\n')
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
newdeps -= seen
_functionfmt = """
def {function}(d):
{body}"""
def emit_func_python(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."""
def write_func(func, o, call = False):
body = d.getVar(func, True)
if not body.startswith("def"):
body = _functionfmt.format(function=func, body=body)
o.write(body.strip() + "\n\n")
if call:
o.write(func + "(d)" + "\n\n")
write_func(func, o, True)
pp = bb.codeparser.PythonParser(func, logger)
pp.parse_python(d.getVar(func, True))
newdeps = pp.execs
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
seen = set()
while newdeps:
deps = newdeps
seen |= deps
newdeps = set()
for dep in deps:
if d.getVarFlag(dep, "func") and d.getVarFlag(dep, "python"):
write_func(dep, o)
pp = bb.codeparser.PythonParser(dep, logger)
pp.parse_python(d.getVar(dep, True))
newdeps |= pp.execs
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
newdeps -= seen
def update_data(d):
"""Performs final steps upon the datastore, including application of overrides"""
d.finalize(parent = True)
@@ -338,25 +295,10 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
deps |= parser.references
deps = deps | (keys & parser.execs)
return deps, value
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "vardepvalueexclude", "postfuncs", "prefuncs"]) or {}
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude"]) or {}
vardeps = varflags.get("vardeps")
value = d.getVar(key, False)
def handle_contains(value, contains, d):
newvalue = ""
for k in sorted(contains):
l = (d.getVar(k, True) or "").split()
for word in sorted(contains[k]):
if word in l:
newvalue += "\n%s{%s} = Set" % (k, word)
else:
newvalue += "\n%s{%s} = Unset" % (k, word)
if not newvalue:
return value
if not value:
return newvalue
return value + newvalue
if "vardepvalue" in varflags:
value = varflags.get("vardepvalue")
elif varflags.get("func"):
@@ -367,7 +309,6 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
logger.warn("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE", True)))
parser.parse_python(parsedvar.value)
deps = deps | parser.references
value = handle_contains(value, parser.contains, d)
else:
parsedvar = d.expandWithRefs(value, key)
parser = bb.codeparser.ShellParser(key, logger)
@@ -375,24 +316,12 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
deps = deps | shelldeps
if vardeps is None:
parser.log.flush()
if "prefuncs" in varflags:
deps = deps | set(varflags["prefuncs"].split())
if "postfuncs" in varflags:
deps = deps | set(varflags["postfuncs"].split())
deps = deps | parsedvar.references
deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
value = handle_contains(value, parsedvar.contains, d)
else:
parser = d.expandWithRefs(value, key)
deps |= parser.references
deps = deps | (keys & parser.execs)
value = handle_contains(value, parser.contains, d)
if "vardepvalueexclude" in varflags:
exclude = varflags.get("vardepvalueexclude")
for excl in exclude.split('|'):
if excl:
value = value.replace(excl, '')
# Add varflags, assuming an exclusion list is set
if varflagsexcl:

View File

@@ -88,7 +88,6 @@ class VariableParse:
self.references = set()
self.execs = set()
self.contains = {}
def var_sub(self, match):
key = match.group()[2:-1]
@@ -99,7 +98,7 @@ class VariableParse:
varparse = self.d.expand_cache[key]
var = varparse.value
else:
var = self.d.getVarFlag(key, "_content", True)
var = self.d.getVar(key, True)
self.references.add(key)
if var is not None:
return var
@@ -121,11 +120,6 @@ class VariableParse:
self.references |= parser.references
self.execs |= parser.execs
for k in parser.contains:
if k not in self.contains:
self.contains[k] = parser.contains[k].copy()
else:
self.contains[k].update(parser.contains[k])
value = utils.better_eval(codeobj, DataContext(self.d))
return str(value)
@@ -263,7 +257,7 @@ class VariableHistory(object):
flag = ''
o.write("# %s %s:%s%s\n# %s\"%s\"\n" % (event['op'], event['file'], event['line'], display_func, flag, re.sub('\n', '\n# ', event['detail'])))
if len(history) > 1:
o.write("# pre-expansion value:\n")
o.write("# computed:\n")
o.write('# "%s"\n' % (commentVal))
else:
o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
@@ -334,7 +328,7 @@ class DataSmart(MutableMapping):
break
except ExpansionError:
raise
except bb.parse.SkipRecipe:
except bb.parse.SkipPackage:
raise
except Exception as exc:
raise ExpansionError(varname, s, exc)
@@ -513,15 +507,10 @@ class DataSmart(MutableMapping):
def _setvar_update_overrides(self, var):
# aka pay the cookie monster
override = var[var.rfind('_')+1:]
shortvar = var[:var.rfind('_')]
while override:
if len(override) > 0:
if override not in self._seen_overrides:
self._seen_overrides[override] = set()
self._seen_overrides[override].add( var )
override = None
if "_" in shortvar:
override = var[shortvar.rfind('_')+1:]
shortvar = var[:shortvar.rfind('_')]
def getVar(self, var, expand=False, noweakdefault=False):
return self.getVarFlag(var, "_content", expand, noweakdefault)
@@ -615,16 +604,10 @@ class DataSmart(MutableMapping):
else:
cachename = var + "[" + flag + "]"
value = self.expand(value, cachename)
if value and flag == "_content" and local_var is not None and "_removeactive" in local_var:
removes = [self.expand(r).split() for r in local_var["_removeactive"]]
removes = reduce(lambda a, b: a+b, removes, [])
filtered = filter(lambda v: v not in removes,
value.split())
if value is not None and flag == "_content" and local_var is not None and "_removeactive" in local_var:
filtered = filter(lambda v: v not in local_var["_removeactive"],
value.split(" "))
value = " ".join(filtered)
if expand:
# We need to ensure the expand cache has the correct value
# flag == "_content" here
self.expand_cache[var].value = value
return value
def delVarFlag(self, var, flag, **loginfo):

View File

@@ -55,7 +55,6 @@ def get_class_handlers():
return _handlers
def set_class_handlers(h):
global _handlers
_handlers = h
def clean_class_handlers():
@@ -73,7 +72,7 @@ def execute_handler(name, handler, event, d):
event.data = d
try:
ret = handler(event)
except (bb.parse.SkipRecipe, bb.BBHandledException):
except bb.parse.SkipPackage:
raise
except Exception:
etype, value, tb = sys.exc_info()
@@ -95,7 +94,10 @@ def fire_class_handlers(event, d):
evt_hmap = _event_handler_map.get(eid, {})
for name, handler in _handlers.iteritems():
if name in _catchall_handlers or name in evt_hmap:
execute_handler(name, handler, event, d)
try:
execute_handler(name, handler, event, d)
except Exception:
continue
ui_queue = []
@atexit.register
@@ -595,19 +597,16 @@ class MetadataEvent(Event):
def __init__(self, eventtype, eventdata):
Event.__init__(self)
self.type = eventtype
self._localdata = eventdata
self.data = eventdata
class SanityCheck(Event):
"""
Event to run sanity checks, either raise errors or generate events as return status.
Event to issue sanity check
"""
def __init__(self, generateevents = True):
Event.__init__(self)
self.generateevents = generateevents
class SanityCheckPassed(Event):
"""
Event to indicate sanity check has passed
Event to indicate sanity check is passed
"""
class SanityCheckFailed(Event):
@@ -621,11 +620,8 @@ class SanityCheckFailed(Event):
class NetworkTest(Event):
"""
Event to run network connectivity tests, either raise errors or generate events as return status.
Event to start network test
"""
def __init__(self, generateevents = True):
Event.__init__(self)
self.generateevents = generateevents
class NetworkTestPassed(Event):
"""

View File

@@ -33,6 +33,9 @@ import glob
import logging
import urllib
import urlparse
if 'git' not in urlparse.uses_netloc:
urlparse.uses_netloc.append('git')
from urlparse import urlparse
import operator
import bb.persist_data, bb.utils
import bb.checksum
@@ -56,11 +59,8 @@ class BBFetchException(Exception):
class MalformedUrl(BBFetchException):
"""Exception raised when encountering an invalid url"""
def __init__(self, url, message=''):
if message:
msg = message
else:
msg = "The URL: '%s' is invalid and cannot be interpreted" % url
def __init__(self, url):
msg = "The URL: '%s' is invalid and cannot be interpreted" % url
self.url = url
BBFetchException.__init__(self, msg)
self.args = (url,)
@@ -165,7 +165,6 @@ class URI(object):
* path_quoted (read/write)
A URI quoted version of path
* params (dict) (read/write)
* query (dict) (read/write)
* relative (bool) (read only)
True if this is a "relative URI", (e.g. file:foo.diff)
@@ -205,35 +204,18 @@ class URI(object):
self.port = None
self._path = ''
self.params = {}
self.query = {}
self.relative = False
if not uri:
return
# We hijack the URL parameters, since the way bitbake uses
# them are not quite RFC compliant.
uri, param_str = (uri.split(";", 1) + [None])[:2]
urlp = urlparse.urlparse(uri)
urlp = urlparse(uri)
self.scheme = urlp.scheme
reparse = 0
# Coerce urlparse to make URI scheme use netloc
if not self.scheme in urlparse.uses_netloc:
urlparse.uses_params.append(self.scheme)
reparse = 1
# Make urlparse happy(/ier) by converting local resources
# to RFC compliant URL format. E.g.:
# file://foo.diff -> file:foo.diff
# Convert URI to be relative
if urlp.scheme in self._netloc_forbidden:
uri = re.sub("(?<=:)//(?!/)", "", uri, 1)
reparse = 1
if reparse:
urlp = urlparse.urlparse(uri)
urlp = urlparse(uri)
# Identify if the URI is relative or not
if urlp.scheme in self._relative_schemes and \
@@ -249,45 +231,49 @@ class URI(object):
if urlp.password:
self.userinfo += ':%s' % urlp.password
self.path = urllib.unquote(urlp.path)
# Do support params even for URI schemes that Python's
# urlparse doesn't support params for.
path = ''
param_str = ''
if not urlp.params:
path, param_str = (list(urlp.path.split(";", 1)) + [None])[:2]
else:
path = urlp.path
param_str = urlp.params
self.path = urllib.unquote(path)
if param_str:
self.params = self._param_str_split(param_str, ";")
if urlp.query:
self.query = self._param_str_split(urlp.query, "&")
self.params = self._param_dict(param_str)
def __str__(self):
userinfo = self.userinfo
if userinfo:
userinfo += '@'
return "%s:%s%s%s%s%s%s" % (
return "%s:%s%s%s%s%s" % (
self.scheme,
'' if self.relative else '//',
userinfo,
self.hostport,
self.path_quoted,
self._query_str(),
self._param_str())
self._param_str)
@property
def _param_str(self):
return (
''.join([';', self._param_str_join(self.params, ";")])
if self.params else '')
def _query_str(self):
return (
''.join(['?', self._param_str_join(self.query, "&")])
if self.query else '')
def _param_str_split(self, string, elmdelim, kvdelim="="):
ret = {}
for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
ret[k] = v
ret = ''
for key, val in self.params.items():
ret += ";%s=%s" % (key, val)
return ret
def _param_str_join(self, dict_, elmdelim, kvdelim="="):
return elmdelim.join([kvdelim.join([k, v]) for k, v in dict_.items()])
def _param_dict(self, param_str):
parm = {}
for keyval in param_str.split(";"):
key, val = keyval.split("=", 1)
parm[key] = val
return parm
@property
def hostport(self):
@@ -324,10 +310,9 @@ class URI(object):
@username.setter
def username(self, username):
password = self.password
self.userinfo = username
if password:
self.userinfo += ":%s" % password
if self.password:
self.userinfo += ":%s" % self.password
@property
def password(self):
@@ -344,7 +329,7 @@ def decodeurl(url):
user, password, parameters).
"""
m = re.compile('(?P<type>[^:]*)://((?P<user>[^/]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
if not m:
raise MalformedUrl(url)
@@ -374,11 +359,8 @@ def decodeurl(url):
p = {}
if parm:
for s in parm.split(';'):
if s:
if not '=' in s:
raise MalformedUrl(url, "The URL: '%s' is invalid: parameter %s does not specify a value (missing '=')" % (url, s))
s1, s2 = s.split('=')
p[s1] = s2
s1, s2 = s.split('=')
p[s1] = s2
return type, host, urllib.unquote(path), user, pswd, p
@@ -525,7 +507,7 @@ def fetcher_compare_revisions(d):
def mirror_from_string(data):
return [ i.split() for i in (data or "").replace('\\n','\n').split('\n') if i ]
def verify_checksum(ud, d):
def verify_checksum(u, ud, d):
"""
verify the MD5 and SHA256 checksum for downloaded src
@@ -543,21 +525,20 @@ def verify_checksum(ud, d):
if ud.method.recommends_checksum(ud):
# If strict checking enabled and neither sum defined, raise error
strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
if (strict == "1") and not (ud.md5_expected or ud.sha256_expected):
logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
strict = d.getVar("BB_STRICT_CHECKSUM", True) or None
if (strict and ud.md5_expected == None and ud.sha256_expected == None):
raise NoChecksumError('No checksum specified for %s, please add at least one to the recipe:\n'
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
(ud.localpath, ud.md5_name, md5data,
ud.sha256_name, sha256data))
raise NoChecksumError('Missing SRC_URI checksum', ud.url)
ud.sha256_name, sha256data), u)
# Log missing sums so user can more easily add them
if not ud.md5_expected:
if ud.md5_expected == None:
logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.md5_name, md5data)
if not ud.sha256_expected:
if ud.sha256_expected == None:
logger.warn('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.sha256_name, sha256data)
@@ -587,10 +568,10 @@ def verify_checksum(ud, d):
msg = msg + '\nIf this change is expected (e.g. you have upgraded to a new version without updating the checksums) then you can use these lines within the recipe:\nSRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"\nOtherwise you should retry the download and/or check with upstream to determine if the file has become corrupted or otherwise unexpectedly modified.\n' % (ud.md5_name, md5data, ud.sha256_name, sha256data)
if len(msg):
raise ChecksumError('Checksum mismatch!%s' % msg, ud.url, md5data)
raise ChecksumError('Checksum mismatch!%s' % msg, u, md5data)
def update_stamp(ud, d):
def update_stamp(u, ud, d):
"""
donestamp is file stamp indicating the whole fetching is done
this function update the stamp after verifying the checksum
@@ -603,7 +584,7 @@ def update_stamp(ud, d):
# Errors aren't fatal here
pass
else:
verify_checksum(ud, d)
verify_checksum(u, ud, d)
open(ud.donestamp, 'w').close()
def subprocess_setup():
@@ -638,7 +619,7 @@ def get_srcrev(d):
raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
autoinc, rev = urldata[scms[0]].method.sortable_revision(urldata[scms[0]], d, urldata[scms[0]].names[0])
autoinc, rev = urldata[scms[0]].method.sortable_revision(scms[0], urldata[scms[0]], d, urldata[scms[0]].names[0])
if len(rev) > 10:
rev = rev[:10]
if autoinc:
@@ -656,7 +637,7 @@ def get_srcrev(d):
for scm in scms:
ud = urldata[scm]
for name in ud.names:
autoinc, rev = ud.method.sortable_revision(ud, d, name)
autoinc, rev = ud.method.sortable_revision(scm, ud, d, name)
seenautoinc = seenautoinc or autoinc
if len(rev) > 10:
rev = rev[:10]
@@ -749,7 +730,7 @@ def build_mirroruris(origud, mirrors, ld):
replacements["BASENAME"] = origud.path.split("/")[-1]
replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
def adduri(ud, uris, uds):
def adduri(uri, ud, uris, uds):
for line in mirrors:
try:
(find, replace) = line
@@ -772,9 +753,9 @@ def build_mirroruris(origud, mirrors, ld):
uris.append(newuri)
uds.append(newud)
adduri(newud, uris, uds)
adduri(newuri, newud, uris, uds)
adduri(origud, uris, uds)
adduri(None, origud, uris, uds)
return uris, uds
@@ -791,22 +772,22 @@ def rename_bad_checksum(ud, suffix):
bb.utils.movefile(ud.localpath, new_localpath)
def try_mirror_url(origud, ud, ld, check = False):
def try_mirror_url(newuri, origud, ud, ld, check = False):
# Return of None or a value means we're finished
# False means try another url
try:
if check:
found = ud.method.checkstatus(ud, ld)
found = ud.method.checkstatus(newuri, ud, ld)
if found:
return found
return False
os.chdir(ld.getVar("DL_DIR", True))
if not os.path.exists(ud.donestamp) or ud.method.need_update(ud, ld):
ud.method.download(ud, ld)
if not os.path.exists(ud.donestamp) or ud.method.need_update(newuri, ud, ld):
ud.method.download(newuri, ud, ld)
if hasattr(ud.method,"build_mirror_data"):
ud.method.build_mirror_data(ud, ld)
ud.method.build_mirror_data(newuri, ud, ld)
if not ud.localpath or not os.path.exists(ud.localpath):
return False
@@ -824,11 +805,7 @@ def try_mirror_url(origud, ud, ld, check = False):
dest = os.path.join(dldir, os.path.basename(ud.localpath))
if not os.path.exists(dest):
os.symlink(ud.localpath, dest)
if not os.path.exists(origud.donestamp) or origud.method.need_update(origud, ld):
origud.method.download(origud, ld)
if hasattr(origud.method,"build_mirror_data"):
origud.method.build_mirror_data(origud, ld)
return ud.localpath
return None
# Otherwise the result is a local file:// and we symlink to it
if not os.path.exists(origud.localpath):
if os.path.islink(origud.localpath):
@@ -836,7 +813,7 @@ def try_mirror_url(origud, ud, ld, check = False):
os.unlink(origud.localpath)
os.symlink(ud.localpath, origud.localpath)
update_stamp(origud, ld)
update_stamp(newuri, origud, ld)
return ud.localpath
except bb.fetch2.NetworkAccess:
@@ -844,13 +821,13 @@ def try_mirror_url(origud, ud, ld, check = False):
except bb.fetch2.BBFetchException as e:
if isinstance(e, ChecksumError):
logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (newuri, origud.url))
logger.warn(str(e))
rename_bad_checksum(ud, e.checksum)
elif isinstance(e, NoChecksumError):
raise
else:
logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url))
logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
logger.debug(1, str(e))
try:
ud.method.clean(ud, ld)
@@ -872,7 +849,7 @@ def try_mirrors(d, origud, mirrors, check = False):
uris, uds = build_mirroruris(origud, mirrors, ld)
for index, uri in enumerate(uris):
ret = try_mirror_url(origud, uds[index], ld, check)
ret = try_mirror_url(uri, origud, uds[index], ld, check)
if ret != False:
return ret
return None
@@ -885,42 +862,32 @@ def srcrev_internal_helper(ud, d, name):
c) None if not specified
"""
srcrev = None
if 'rev' in ud.parm:
return ud.parm['rev']
if 'tag' in ud.parm:
return ud.parm['tag']
rev = None
pn = d.getVar("PN", True)
attempts = []
if name != '' and pn:
attempts.append("SRCREV_%s_pn-%s" % (name, pn))
if name != '':
attempts.append("SRCREV_%s" % name)
if pn:
attempts.append("SRCREV_pn-%s" % pn)
attempts.append("SRCREV")
rev = d.getVar("SRCREV_%s_pn-%s" % (name, pn), True)
if not rev:
rev = d.getVar("SRCREV_%s" % name, True)
if not rev:
rev = d.getVar("SRCREV_pn-%s" % pn, True)
if not rev:
rev = d.getVar("SRCREV", True)
if rev == "INVALID":
var = "SRCREV_pn-%s" % pn
if name != '':
var = "SRCREV_%s_pn-%s" % (name, pn)
raise FetchError("Please set %s to a valid value" % var, ud.url)
if rev == "AUTOINC":
rev = ud.method.latest_revision(ud.url, ud, d, name)
for a in attempts:
srcrev = d.getVar(a, True)
if srcrev and srcrev != "INVALID":
break
return rev
if 'rev' in ud.parm and 'tag' in ud.parm:
raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
if 'rev' in ud.parm or 'tag' in ud.parm:
if 'rev' in ud.parm:
parmrev = ud.parm['rev']
else:
parmrev = ud.parm['tag']
if srcrev == "INVALID" or not srcrev:
return parmrev
if srcrev != parmrev:
raise FetchError("Conflicting revisions (%s from SRCREV and %s from the url) found, please spcify one valid value" % (srcrev, parmrev))
return parmrev
if srcrev == "INVALID" or not srcrev:
raise FetchError("Please set a valid SRCREV for url %s (possible key names are %s, or use a ;rev=X URL parameter)" % (str(attempts), ud.url), ud.url)
if srcrev == "AUTOINC":
srcrev = ud.method.latest_revision(ud, d, name)
return srcrev
def get_checksum_file_list(d):
""" Get a list of files checksum in SRC_URI
@@ -936,21 +903,20 @@ def get_checksum_file_list(d):
ud = fetch.ud[u]
if ud and isinstance(ud.method, local.Local):
paths = ud.method.localpaths(ud, d)
for f in paths:
pth = ud.decodedurl
if '*' in pth:
f = os.path.join(os.path.abspath(f), pth)
if f.startswith(dl_dir):
# The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
if os.path.exists(f):
bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
else:
bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
filelist.append(f + ":" + str(os.path.exists(f)))
ud.setup_localpath(d)
f = ud.localpath
if f.startswith(dl_dir):
# The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
if os.path.exists(f):
bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
else:
bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
continue
filelist.append(f)
return " ".join(filelist)
def get_file_checksums(filelist, pn):
"""Get a list of the checksums for a list of local files
@@ -967,35 +933,27 @@ def get_file_checksums(filelist, pn):
return None
return checksum
def checksum_dir(pth):
# Handle directories recursively
dirchecksums = []
for root, dirs, files in os.walk(pth):
for name in files:
fullpth = os.path.join(root, name)
checksum = checksum_file(fullpth)
if checksum:
dirchecksums.append((fullpth, checksum))
return dirchecksums
checksums = []
for pth in filelist.split():
exist = pth.split(":")[1]
if exist == "False":
continue
pth = pth.split(":")[0]
checksum = None
if '*' in pth:
# Handle globs
for f in glob.glob(pth):
if os.path.isdir(f):
checksums.extend(checksum_dir(f))
else:
checksum = checksum_file(f)
checksum = checksum_file(f)
if checksum:
checksums.append((f, checksum))
elif os.path.isdir(pth):
checksums.extend(checksum_dir(pth))
# Handle directories
for root, dirs, files in os.walk(pth):
for name in files:
fullpth = os.path.join(root, name)
checksum = checksum_file(fullpth)
if checksum:
checksums.append((fullpth, checksum))
else:
checksum = checksum_file(pth)
if checksum:
checksums.append((pth, checksum))
checksums.sort(key=operator.itemgetter(1))
@@ -1047,7 +1005,7 @@ class FetchData(object):
self.method = None
for m in methods:
if m.supports(self, d):
if m.supports(url, self, d):
self.method = m
break
@@ -1069,7 +1027,7 @@ class FetchData(object):
self.localpath = self.parm["localpath"]
self.basename = os.path.basename(self.localpath)
elif self.localfile:
self.localpath = self.method.localpath(self, d)
self.localpath = self.method.localpath(self.url, self, d)
dldir = d.getVar("DL_DIR", True)
# Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
@@ -1093,7 +1051,7 @@ class FetchData(object):
def setup_localpath(self, d):
if not self.localpath:
self.localpath = self.method.localpath(self, d)
self.localpath = self.method.localpath(self.url, self, d)
def getSRCDate(self, d):
"""
@@ -1117,13 +1075,13 @@ class FetchMethod(object):
def __init__(self, urls = []):
self.urls = []
def supports(self, urldata, d):
def supports(self, url, urldata, d):
"""
Check to see if this fetch class supports a given url.
"""
return 0
def localpath(self, urldata, d):
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
@@ -1167,7 +1125,7 @@ class FetchMethod(object):
urls = property(getUrls, setUrls, None, "Urls property")
def need_update(self, ud, d):
def need_update(self, url, ud, d):
"""
Force a fetch, even if localpath exists?
"""
@@ -1181,7 +1139,7 @@ class FetchMethod(object):
"""
return False
def download(self, urldata, d):
def download(self, url, urldata, d):
"""
Fetch urls
Assumes localpath was called first
@@ -1258,22 +1216,17 @@ class FetchMethod(object):
destdir = destdir.strip('/')
if destdir != "." and not os.access("%s/%s" % (rootdir, destdir), os.F_OK):
os.makedirs("%s/%s" % (rootdir, destdir))
cmd = 'cp -fpPR %s %s/%s/' % (file, rootdir, destdir)
cmd = 'cp -pPR %s %s/%s/' % (file, rootdir, destdir)
#cmd = 'tar -cf - -C "%d" -ps . | tar -xf - -C "%s/%s/"' % (file, rootdir, destdir)
else:
# The "destdir" handling was specifically done for FILESPATH
# items. So, only do so for file:// entries.
if urldata.type == "file" and urldata.path.find("/") != -1:
destdir = urldata.path.rsplit("/", 1)[0]
if urldata.parm.get('subdir') != None:
destdir = urldata.parm.get('subdir') + "/" + destdir
else:
if urldata.parm.get('subdir') != None:
destdir = urldata.parm.get('subdir')
else:
destdir = "."
destdir = "."
bb.utils.mkdirhier("%s/%s" % (rootdir, destdir))
cmd = 'cp -f %s %s/%s/' % (file, rootdir, destdir)
cmd = 'cp %s %s/%s/' % (file, rootdir, destdir)
if not cmd:
return
@@ -1305,18 +1258,18 @@ class FetchMethod(object):
return
def clean(self, urldata, d):
"""
Clean any existing full or partial download
"""
bb.utils.remove(urldata.localpath)
"""
Clean any existing full or partial download
"""
bb.utils.remove(urldata.localpath)
def try_premirror(self, urldata, d):
def try_premirror(self, url, urldata, d):
"""
Should premirrors be used?
"""
return True
def checkstatus(self, urldata, d):
def checkstatus(self, url, urldata, d):
"""
Check the status of a URL
Assumes localpath was called first
@@ -1324,7 +1277,7 @@ class FetchMethod(object):
logger.info("URL %s could not be checked for status since no method exists.", url)
return True
def latest_revision(self, ud, d, name):
def latest_revision(self, url, ud, d, name):
"""
Look in the cache for the latest revision, if not present ask the SCM.
"""
@@ -1332,19 +1285,19 @@ class FetchMethod(object):
raise ParameterError("The fetcher for this URL does not support _latest_revision", url)
revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
key = self.generate_revision_key(ud, d, name)
key = self.generate_revision_key(url, ud, d, name)
try:
return revs[key]
except KeyError:
revs[key] = rev = self._latest_revision(ud, d, name)
revs[key] = rev = self._latest_revision(url, ud, d, name)
return rev
def sortable_revision(self, ud, d, name):
latest_rev = self._build_revision(ud, d, name)
def sortable_revision(self, url, ud, d, name):
latest_rev = self._build_revision(url, ud, d, name)
return True, str(latest_rev)
def generate_revision_key(self, ud, d, name):
key = self._revision_key(ud, d, name)
def generate_revision_key(self, url, ud, d, name):
key = self._revision_key(url, ud, d, name)
return "%s-%s" % (key, d.getVar("PN", True) or "")
class Fetch(object):
@@ -1415,9 +1368,9 @@ class Fetch(object):
try:
self.d.setVar("BB_NO_NETWORK", network)
if os.path.exists(ud.donestamp) and not m.need_update(ud, self.d):
if os.path.exists(ud.donestamp) and not m.need_update(u, ud, self.d):
localpath = ud.localpath
elif m.try_premirror(ud, self.d):
elif m.try_premirror(u, ud, self.d):
logger.debug(1, "Trying PREMIRRORS")
mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
localpath = try_mirrors(self.d, ud, mirrors, False)
@@ -1428,16 +1381,16 @@ class Fetch(object):
os.chdir(self.d.getVar("DL_DIR", True))
firsterr = None
if not localpath and ((not os.path.exists(ud.donestamp)) or m.need_update(ud, self.d)):
if not localpath and ((not os.path.exists(ud.donestamp)) or m.need_update(u, ud, self.d)):
try:
logger.debug(1, "Trying Upstream")
m.download(ud, self.d)
m.download(u, ud, self.d)
if hasattr(m, "build_mirror_data"):
m.build_mirror_data(ud, self.d)
m.build_mirror_data(u, ud, self.d)
localpath = ud.localpath
# early checksum verify, so that if checksum mismatched,
# fetcher still have chance to fetch from mirror
update_stamp(ud, self.d)
update_stamp(u, ud, self.d)
except bb.fetch2.NetworkAccess:
raise
@@ -1464,10 +1417,12 @@ class Fetch(object):
logger.error(str(firsterr))
raise FetchError("Unable to fetch URL from any source.", u)
update_stamp(ud, self.d)
update_stamp(u, ud, self.d)
except BBFetchException as e:
if isinstance(e, ChecksumError):
if isinstance(e, NoChecksumError):
logger.error("%s" % str(e))
elif isinstance(e, ChecksumError):
logger.error("Checksum failure fetching %s" % u)
raise
@@ -1493,7 +1448,7 @@ class Fetch(object):
if not ret:
# Next try checking from the original uri, u
try:
ret = m.checkstatus(ud, self.d)
ret = m.checkstatus(u, ud, self.d)
except:
# Finally, try checking uri, u, from MIRRORS
mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
@@ -1539,7 +1494,7 @@ class Fetch(object):
ud = self.ud[url]
ud.setup_localpath(self.d)
if not ud.localfile and ud.localpath is None:
if not ud.localfile or self.localpath is None:
continue
if ud.lockfile:
@@ -1555,10 +1510,10 @@ class Fetch(object):
from . import cvs
from . import git
from . import gitsm
from . import gitannex
from . import local
from . import svn
from . import wget
from . import svk
from . import ssh
from . import sftp
from . import perforce
@@ -1566,15 +1521,14 @@ from . import bzr
from . import hg
from . import osc
from . import repo
from . import clearcase
methods.append(local.Local())
methods.append(wget.Wget())
methods.append(svn.Svn())
methods.append(git.Git())
methods.append(gitsm.GitSM())
methods.append(gitannex.GitANNEX())
methods.append(cvs.Cvs())
methods.append(svk.Svk())
methods.append(ssh.SSH())
methods.append(sftp.SFTP())
methods.append(perforce.Perforce())
@@ -1582,4 +1536,3 @@ methods.append(bzr.Bzr())
methods.append(hg.Hg())
methods.append(osc.Osc())
methods.append(repo.Repo())
methods.append(clearcase.ClearCase())

View File

@@ -34,7 +34,7 @@ from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
class Bzr(FetchMethod):
def supports(self, ud, d):
def supports(self, url, ud, d):
return ud.type in ['bzr']
def urldata_init(self, ud, d):
@@ -48,7 +48,7 @@ class Bzr(FetchMethod):
ud.setup_revisons(d)
if not ud.revision:
ud.revision = self.latest_revision(ud, d)
ud.revision = self.latest_revision(ud.url, ud, d)
ud.localfile = data.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision), d)
@@ -81,12 +81,12 @@ class Bzr(FetchMethod):
return bzrcmd
def download(self, ud, d):
def download(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")
logger.debug(1, "BZR Update %s", ud.url)
logger.debug(1, "BZR Update %s", loc)
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
os.chdir(os.path.join (ud.pkgdir, os.path.basename(ud.path)))
runfetchcmd(bzrcmd, d)
@@ -94,7 +94,7 @@ class Bzr(FetchMethod):
bb.utils.remove(os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir)), True)
bzrcmd = self._buildbzrcommand(ud, d, "fetch")
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
logger.debug(1, "BZR Checkout %s", ud.url)
logger.debug(1, "BZR Checkout %s", loc)
bb.utils.mkdirhier(ud.pkgdir)
os.chdir(ud.pkgdir)
logger.debug(1, "Running %s", bzrcmd)
@@ -114,17 +114,17 @@ class Bzr(FetchMethod):
def supports_srcrev(self):
return True
def _revision_key(self, ud, d, name):
def _revision_key(self, url, ud, d, name):
"""
Return a unique key for the url
"""
return "bzr:" + ud.pkgdir
def _latest_revision(self, ud, d, name):
def _latest_revision(self, url, ud, d, name):
"""
Return the latest upstream revision number
"""
logger.debug(2, "BZR fetcher hitting network for %s", ud.url)
logger.debug(2, "BZR fetcher hitting network for %s", url)
bb.fetch2.check_network_access(d, self._buildbzrcommand(ud, d, "revno"), ud.url)
@@ -132,12 +132,12 @@ class Bzr(FetchMethod):
return output.strip()
def sortable_revision(self, ud, d, name):
def sortable_revision(self, url, ud, d, name):
"""
Return a sortable revision number which in our case is the revision number
"""
return False, self._build_revision(ud, d)
return False, self._build_revision(url, ud, d)
def _build_revision(self, ud, d):
def _build_revision(self, url, ud, d):
return ud.revision

View File

@@ -1,263 +0,0 @@
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
"""
BitBake 'Fetch' clearcase implementation
The clearcase fetcher is used to retrieve files from a ClearCase repository.
Usage in the recipe:
SRC_URI = "ccrc://cc.example.org/ccrc;vob=/example_vob;module=/example_module"
SRCREV = "EXAMPLE_CLEARCASE_TAG"
PV = "${@d.getVar("SRCREV").replace("/", "+")}"
The fetcher uses the rcleartool or cleartool remote client, depending on which one is available.
Supported SRC_URI options are:
- vob
(required) The name of the clearcase VOB (with prepending "/")
- module
The module in the selected VOB (with prepending "/")
The module and vob parameters are combined to create
the following load rule in the view config spec:
load <vob><module>
- proto
http or https
Related variables:
CCASE_CUSTOM_CONFIG_SPEC
Write a config spec to this variable in your recipe to use it instead
of the default config spec generated by this fetcher.
Please note that the SRCREV loses its functionality if you specify
this variable. SRCREV is still used to label the archive after a fetch,
but it doesn't define what's fetched.
User credentials:
cleartool:
The login of cleartool is handled by the system. No special steps needed.
rcleartool:
In order to use rcleartool with authenticated users an `rcleartool login` is
necessary before using the fetcher.
"""
# Copyright (C) 2014 Siemens AG
#
# 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 shutil
import bb
from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
from distutils import spawn
class ClearCase(FetchMethod):
"""Class to fetch urls via 'clearcase'"""
def init(self, d):
pass
def supports(self, ud, d):
"""
Check to see if a given url can be fetched with Clearcase.
"""
return ud.type in ['ccrc']
def debug(self, msg):
logger.debug(1, "ClearCase: %s", msg)
def urldata_init(self, ud, d):
"""
init ClearCase specific variable within url data
"""
ud.proto = "https"
if 'protocol' in ud.parm:
ud.proto = ud.parm['protocol']
if not ud.proto in ('http', 'https'):
raise fetch2.ParameterError("Invalid protocol type", ud.url)
ud.vob = ''
if 'vob' in ud.parm:
ud.vob = ud.parm['vob']
else:
msg = ud.url+": vob must be defined so the fetcher knows what to get."
raise MissingParameterError('vob', msg)
if 'module' in ud.parm:
ud.module = ud.parm['module']
else:
ud.module = ""
ud.basecmd = d.getVar("FETCHCMD_ccrc", True) or spawn.find_executable("cleartool") or spawn.find_executable("rcleartool")
if data.getVar("SRCREV", d, True) == "INVALID":
raise FetchError("Set a valid SRCREV for the clearcase fetcher in your recipe, e.g. SRCREV = \"/main/LATEST\" or any other label of your choice.")
ud.label = d.getVar("SRCREV")
ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC", True)
ud.server = "%s://%s%s" % (ud.proto, ud.host, ud.path)
ud.identifier = "clearcase-%s%s-%s" % ( ud.vob.replace("/", ""),
ud.module.replace("/", "."),
ud.label.replace("/", "."))
ud.viewname = "%s-view%s" % (ud.identifier, d.getVar("DATETIME", d, True))
ud.csname = "%s-config-spec" % (ud.identifier)
ud.ccasedir = os.path.join(data.getVar("DL_DIR", d, True), ud.type)
ud.viewdir = os.path.join(ud.ccasedir, ud.viewname)
ud.configspecfile = os.path.join(ud.ccasedir, ud.csname)
ud.localfile = "%s.tar.gz" % (ud.identifier)
self.debug("host = %s" % ud.host)
self.debug("path = %s" % ud.path)
self.debug("server = %s" % ud.server)
self.debug("proto = %s" % ud.proto)
self.debug("type = %s" % ud.type)
self.debug("vob = %s" % ud.vob)
self.debug("module = %s" % ud.module)
self.debug("basecmd = %s" % ud.basecmd)
self.debug("label = %s" % ud.label)
self.debug("ccasedir = %s" % ud.ccasedir)
self.debug("viewdir = %s" % ud.viewdir)
self.debug("viewname = %s" % ud.viewname)
self.debug("configspecfile = %s" % ud.configspecfile)
self.debug("localfile = %s" % ud.localfile)
ud.localfile = os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
def _build_ccase_command(self, ud, command):
"""
Build up a commandline based on ud
command is: mkview, setcs, rmview
"""
options = []
if "rcleartool" in ud.basecmd:
options.append("-server %s" % ud.server)
basecmd = "%s %s" % (ud.basecmd, command)
if command is 'mkview':
if not "rcleartool" in ud.basecmd:
# Cleartool needs a -snapshot view
options.append("-snapshot")
options.append("-tag %s" % ud.viewname)
options.append(ud.viewdir)
elif command is 'rmview':
options.append("-force")
options.append("%s" % ud.viewdir)
elif command is 'setcs':
options.append("-overwrite")
options.append(ud.configspecfile)
else:
raise FetchError("Invalid ccase command %s" % command)
ccasecmd = "%s %s" % (basecmd, " ".join(options))
self.debug("ccasecmd = %s" % ccasecmd)
return ccasecmd
def _write_configspec(self, ud, d):
"""
Create config spec file (ud.configspecfile) for ccase view
"""
config_spec = ""
custom_config_spec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC", d)
if custom_config_spec is not None:
for line in custom_config_spec.split("\\n"):
config_spec += line+"\n"
bb.warn("A custom config spec has been set, SRCREV is only relevant for the tarball name.")
else:
config_spec += "element * CHECKEDOUT\n"
config_spec += "element * %s\n" % ud.label
config_spec += "load %s%s\n" % (ud.vob, ud.module)
logger.info("Using config spec: \n%s" % config_spec)
with open(ud.configspecfile, 'w') as f:
f.write(config_spec)
def _remove_view(self, ud, d):
if os.path.exists(ud.viewdir):
os.chdir(ud.ccasedir)
cmd = self._build_ccase_command(ud, 'rmview');
logger.info("cleaning up [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
bb.fetch2.check_network_access(d, cmd, ud.url)
output = runfetchcmd(cmd, d)
logger.info("rmview output: %s", output)
def need_update(self, ud, d):
if ("LATEST" in ud.label) or (ud.customspec and "LATEST" in ud.customspec):
ud.identifier += "-%s" % d.getVar("DATETIME",d, True)
return True
if os.path.exists(ud.localpath):
return False
return True
def supports_srcrev(self):
return True
def sortable_revision(self, ud, d, name):
return False, ud.identifier
def download(self, ud, d):
"""Fetch url"""
# Make a fresh view
bb.utils.mkdirhier(ud.ccasedir)
self._write_configspec(ud, d)
cmd = self._build_ccase_command(ud, 'mkview')
logger.info("creating view [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
bb.fetch2.check_network_access(d, cmd, ud.url)
try:
runfetchcmd(cmd, d)
except FetchError as e:
if "CRCLI2008E" in e.msg:
raise FetchError("%s\n%s\n" % (e.msg, "Call `rcleartool login` in your console to authenticate to the clearcase server before running bitbake."))
else:
raise e
# Set configspec: Setting the configspec effectively fetches the files as defined in the configspec
os.chdir(ud.viewdir)
cmd = self._build_ccase_command(ud, 'setcs');
logger.info("fetching data [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
bb.fetch2.check_network_access(d, cmd, ud.url)
output = runfetchcmd(cmd, d)
logger.info("%s", output)
# Copy the configspec to the viewdir so we have it in our source tarball later
shutil.copyfile(ud.configspecfile, os.path.join(ud.viewdir, ud.csname))
# Clean clearcase meta-data before tar
runfetchcmd('tar -czf "%s" .' % (ud.localpath), d, cleanup = [ud.localpath])
# Clean up so we can create a new view next time
self.clean(ud, d);
def clean(self, ud, d):
self._remove_view(ud, d)
bb.utils.remove(ud.configspecfile)

View File

@@ -36,7 +36,7 @@ class Cvs(FetchMethod):
"""
Class to fetch a module or modules from cvs repositories
"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with cvs.
"""
@@ -65,14 +65,14 @@ class Cvs(FetchMethod):
ud.localfile = bb.data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d)
def need_update(self, ud, d):
def need_update(self, url, ud, d):
if (ud.date == "now"):
return True
if not os.path.exists(ud.localpath):
return True
return False
def download(self, ud, d):
def download(self, loc, ud, d):
method = ud.parm.get('method', 'pserver')
localdir = ud.parm.get('localdir', ud.module)
@@ -124,13 +124,13 @@ class Cvs(FetchMethod):
pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
moddir = os.path.join(pkgdir, localdir)
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
logger.info("Update " + ud.url)
logger.info("Update " + loc)
bb.fetch2.check_network_access(d, cvsupdatecmd, ud.url)
# update sources there
os.chdir(moddir)
cmd = cvsupdatecmd
else:
logger.info("Fetch " + ud.url)
logger.info("Fetch " + loc)
# check out sources there
bb.utils.mkdirhier(pkgdir)
os.chdir(pkgdir)

View File

@@ -44,11 +44,6 @@ Supported SRC_URI options are:
checkout code and tracking branch requirements.
The default is "0", set bareclone=1 if needed.
- nobranch
Don't check the SHA validation for branch. set this option for the recipe
referring to commit which is valid in tag instead of branch.
The default is "0", set nobranch=1 if needed.
"""
#Copyright (C) 2005 Richard Purdie
@@ -78,7 +73,7 @@ class Git(FetchMethod):
def init(self, d):
pass
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with git.
"""
@@ -106,14 +101,11 @@ class Git(FetchMethod):
ud.rebaseable = ud.parm.get("rebaseable","0") == "1"
ud.nobranch = ud.parm.get("nobranch","0") == "1"
# bareclone implies nocheckout
ud.bareclone = ud.parm.get("bareclone","0") == "1"
if ud.bareclone:
ud.nocheckout = 1
ud.unresolvedrev = {}
branches = ud.parm.get("branch", "master").split(',')
if len(branches) != len(ud.names):
raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
@@ -121,9 +113,8 @@ class Git(FetchMethod):
for name in ud.names:
branch = branches[ud.names.index(name)]
ud.branches[name] = branch
ud.unresolvedrev[name] = branch
ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git -c core.fsyncobjectfiles=0"
ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0") or ud.rebaseable
@@ -133,8 +124,8 @@ class Git(FetchMethod):
# Ensure anything that doesn't look like a sha256 checksum/revision is translated into one
if not ud.revisions[name] or len(ud.revisions[name]) != 40 or (False in [c in "abcdef0123456789" for c in ud.revisions[name]]):
if ud.revisions[name]:
ud.unresolvedrev[name] = ud.revisions[name]
ud.revisions[name] = self.latest_revision(ud, d, name)
ud.branches[name] = ud.revisions[name]
ud.revisions[name] = self.latest_revision(ud.url, ud, d, name)
gitsrcname = '%s%s' % (ud.host.replace(':','.'), ud.path.replace('/', '.').replace('*', '.'))
# for rebaseable git repo, it is necessary to keep mirror tar ball
@@ -151,21 +142,21 @@ class Git(FetchMethod):
ud.localfile = ud.clonedir
def localpath(self, ud, d):
def localpath(self, url, ud, d):
return ud.clonedir
def need_update(self, ud, d):
def need_update(self, u, ud, d):
if not os.path.exists(ud.clonedir):
return True
os.chdir(ud.clonedir)
for name in ud.names:
if not self._contains_ref(ud, d, name):
if not self._contains_ref(ud.revisions[name], d):
return True
if ud.write_tarballs and not os.path.exists(ud.fullmirror):
return True
return False
def try_premirror(self, ud, d):
def try_premirror(self, u, ud, d):
# If we don't do this, updating an existing checkout with only premirrors
# is not possible
if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
@@ -174,7 +165,7 @@ class Git(FetchMethod):
return False
return True
def download(self, ud, d):
def download(self, loc, ud, d):
"""Fetch url"""
if ud.user:
@@ -206,7 +197,7 @@ class Git(FetchMethod):
# Update the checkout if needed
needupdate = False
for name in ud.names:
if not self._contains_ref(ud, d, name):
if not self._contains_ref(ud.revisions[name], d):
needupdate = True
if needupdate:
try:
@@ -222,12 +213,8 @@ class Git(FetchMethod):
runfetchcmd("%s prune-packed" % ud.basecmd, d)
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
ud.repochanged = True
os.chdir(ud.clonedir)
for name in ud.names:
if not self._contains_ref(ud, d, name):
raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
def build_mirror_data(self, ud, d):
def build_mirror_data(self, url, ud, d):
# Generate a mirror tarball if needed
if ud.write_tarballs and (ud.repochanged or not os.path.exists(ud.fullmirror)):
# it's possible that this symlink points to read-only filesystem with PREMIRROR
@@ -245,7 +232,7 @@ class Git(FetchMethod):
subdir = ud.parm.get("subpath", "")
if subdir != "":
readpathspec = ":%s" % (subdir)
def_destsuffix = "%s/" % os.path.basename(subdir.rstrip('/'))
def_destsuffix = "%s/" % os.path.basename(subdir)
else:
readpathspec = ""
def_destsuffix = "git/"
@@ -275,7 +262,7 @@ class Git(FetchMethod):
os.symlink(ud.clonedir, indirectiondir)
clonedir = indirectiondir
runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, cloneflags, clonedir, destdir), d)
runfetchcmd("git clone %s %s/ %s" % (cloneflags, clonedir, destdir), d)
if not ud.nocheckout:
os.chdir(destdir)
if subdir != "":
@@ -290,67 +277,48 @@ class Git(FetchMethod):
bb.utils.remove(ud.localpath, True)
bb.utils.remove(ud.fullmirror)
bb.utils.remove(ud.fullmirror + ".done")
def supports_srcrev(self):
return True
def _contains_ref(self, ud, d, name):
cmd = ""
if ud.nobranch:
cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
ud.basecmd, ud.revisions[name])
else:
cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
ud.basecmd, ud.revisions[name], ud.branches[name])
try:
output = runfetchcmd(cmd, d, quiet=True)
except bb.fetch2.FetchError:
return False
def _contains_ref(self, tag, d):
basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (basecmd, tag)
output = runfetchcmd(cmd, d, quiet=True)
if len(output.split()) > 1:
raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output))
return output.split()[0] != "0"
def _revision_key(self, ud, d, name):
def _revision_key(self, url, ud, d, name):
"""
Return a unique key for the url
"""
return "git:" + ud.host + ud.path.replace('/', '.') + ud.unresolvedrev[name]
return "git:" + ud.host + ud.path.replace('/', '.') + ud.branches[name]
def _lsremote(self, ud, d, search):
def _latest_revision(self, url, ud, d, name):
"""
Run git ls-remote with the specified search string
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" % \
(ud.basecmd, ud.proto, username, ud.host, ud.path, search)
(basecmd, ud.proto, username, ud.host, ud.path, ud.branches[name])
if ud.proto.lower() != 'file':
bb.fetch2.check_network_access(d, cmd)
output = runfetchcmd(cmd, d, True)
if not output:
raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
return output
def _latest_revision(self, ud, d, name):
"""
Compute the HEAD revision for the url
"""
if ud.unresolvedrev[name][:5] == "refs/":
search = "%s %s^{}" % (ud.unresolvedrev[name], ud.unresolvedrev[name])
else:
search = "refs/heads/%s refs/tags/%s^{}" % (ud.unresolvedrev[name], ud.unresolvedrev[name])
output = self._lsremote(ud, d, search)
raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, url)
return output.split()[0]
def _build_revision(self, ud, d, name):
def _build_revision(self, url, ud, d, name):
return ud.revisions[name]
def checkstatus(self, ud, d):
fetchcmd = "%s ls-remote %s" % (ud.basecmd, ud.url)
def checkstatus(self, uri, ud, d):
fetchcmd = "%s ls-remote %s" % (ud.basecmd, uri)
try:
runfetchcmd(fetchcmd, d, quiet=True)
return True

View File

@@ -1,76 +0,0 @@
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
"""
BitBake 'Fetch' git annex implementation
"""
# Copyright (C) 2014 Otavio Salvador
# Copyright (C) 2014 O.S. Systems Software LTDA.
#
# 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.fetch2.git import Git
from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
class GitANNEX(Git):
def supports(self, ud, d):
"""
Check to see if a given url can be fetched with git.
"""
return ud.type in ['gitannex']
def uses_annex(self, ud, d):
for name in ud.names:
try:
runfetchcmd("%s rev-list git-annex" % (ud.basecmd), d, quiet=True)
return True
except bb.fetch.FetchError:
pass
return False
def update_annex(self, ud, d):
try:
runfetchcmd("%s annex get --all" % (ud.basecmd), d, quiet=True)
except bb.fetch.FetchError:
return False
runfetchcmd("chmod u+w -R %s/annex" % (ud.clonedir), d, quiet=True)
return True
def download(self, ud, d):
Git.download(self, ud, d)
os.chdir(ud.clonedir)
annex = self.uses_annex(ud, d)
if annex:
self.update_annex(ud, d)
def unpack(self, ud, destdir, d):
Git.unpack(self, ud, destdir, d)
os.chdir(ud.destdir)
try:
runfetchcmd("%s annex sync" % (ud.basecmd), d)
except bb.fetch.FetchError:
pass
annex = self.uses_annex(ud, d)
if annex:
runfetchcmd("%s annex get" % (ud.basecmd), d)
runfetchcmd("chmod u+w -R %s/.git/annex" % (ud.destdir), d, quiet=True)

View File

@@ -2,16 +2,6 @@
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
"""
BitBake 'Fetch' git submodules implementation
Inherits from and extends the Git fetcher to retrieve submodules of a git repository
after cloning.
SRC_URI = "gitsm://<see Git fetcher for syntax>"
See the Git fetcher, git://, for usage documentation.
NOTE: Switching a SRC_URI from "git://" to "gitsm://" requires a clean of your recipe.
"""
# Copyright (C) 2013 Richard Purdie
@@ -37,7 +27,7 @@ from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
class GitSM(Git):
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with git.
"""
@@ -52,54 +42,7 @@ class GitSM(Git):
pass
return False
def _set_relative_paths(self, repopath):
"""
Fix submodule paths to be relative instead of absolute,
so that when we move the repo it doesn't break
(In Git 1.7.10+ this is done automatically)
"""
submodules = []
with open(os.path.join(repopath, '.gitmodules'), 'r') as f:
for line in f.readlines():
if line.startswith('[submodule'):
submodules.append(line.split('"')[1])
for module in submodules:
repo_conf = os.path.join(repopath, module, '.git')
if os.path.exists(repo_conf):
with open(repo_conf, 'r') as f:
lines = f.readlines()
newpath = ''
for i, line in enumerate(lines):
if line.startswith('gitdir:'):
oldpath = line.split(': ')[-1].rstrip()
if oldpath.startswith('/'):
newpath = '../' * (module.count('/') + 1) + '.git/modules/' + module
lines[i] = 'gitdir: %s\n' % newpath
break
if newpath:
with open(repo_conf, 'w') as f:
for line in lines:
f.write(line)
repo_conf2 = os.path.join(repopath, '.git', 'modules', module, 'config')
if os.path.exists(repo_conf2):
with open(repo_conf2, 'r') as f:
lines = f.readlines()
newpath = ''
for i, line in enumerate(lines):
if line.lstrip().startswith('worktree = '):
oldpath = line.split(' = ')[-1].rstrip()
if oldpath.startswith('/'):
newpath = '../' * (module.count('/') + 3) + module
lines[i] = '\tworktree = %s\n' % newpath
break
if newpath:
with open(repo_conf2, 'w') as f:
for line in lines:
f.write(line)
def update_submodules(self, ud, d):
def update_submodules(self, u, ud, d):
# We have to convert bare -> full repo, do the submodule bit, then convert back
tmpclonedir = ud.clonedir + ".tmp"
gitdir = tmpclonedir + os.sep + ".git"
@@ -108,21 +51,20 @@ class GitSM(Git):
os.rename(ud.clonedir, gitdir)
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
os.chdir(tmpclonedir)
runfetchcmd(ud.basecmd + " reset --hard", d)
runfetchcmd(ud.basecmd + " submodule init", d)
runfetchcmd(ud.basecmd + " submodule update", d)
self._set_relative_paths(tmpclonedir)
runfetchcmd("git reset --hard", d)
runfetchcmd("git submodule init", d)
runfetchcmd("git submodule update", d)
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*false/bare = true/'", d)
os.rename(gitdir, ud.clonedir,)
bb.utils.remove(tmpclonedir, True)
def download(self, ud, d):
Git.download(self, ud, d)
def download(self, loc, ud, d):
Git.download(self, loc, ud, d)
os.chdir(ud.clonedir)
submodules = self.uses_submodules(ud, d)
if submodules:
self.update_submodules(ud, d)
self.update_submodules(loc, ud, d)
def unpack(self, ud, destdir, d):
Git.unpack(self, ud, destdir, d)
@@ -131,6 +73,6 @@ class GitSM(Git):
submodules = self.uses_submodules(ud, d)
if submodules:
runfetchcmd("cp -r " + ud.clonedir + "/modules " + ud.destdir + "/.git/", d)
runfetchcmd(ud.basecmd + " submodule init", d)
runfetchcmd(ud.basecmd + " submodule update", d)
runfetchcmd("git submodule init", d)
runfetchcmd("git submodule update", d)

View File

@@ -37,7 +37,7 @@ from bb.fetch2 import logger
class Hg(FetchMethod):
"""Class to fetch from mercurial repositories"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with mercurial.
"""
@@ -62,11 +62,11 @@ class Hg(FetchMethod):
if 'rev' in ud.parm:
ud.revision = ud.parm['rev']
elif not ud.revision:
ud.revision = self.latest_revision(ud, d)
ud.revision = self.latest_revision(ud.url, ud, d)
ud.localfile = data.expand('%s_%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision), d)
def need_update(self, ud, d):
def need_update(self, url, ud, d):
revTag = ud.parm.get('rev', 'tip')
if revTag == "tip":
return True
@@ -92,10 +92,7 @@ class Hg(FetchMethod):
if not ud.user:
hgroot = host + ud.path
else:
if ud.pswd:
hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path
else:
hgroot = ud.user + "@" + host + ud.path
hgroot = ud.user + "@" + host + ud.path
if command == "info":
return "%s identify -i %s://%s/%s" % (basecmd, proto, hgroot, ud.module)
@@ -110,36 +107,27 @@ class Hg(FetchMethod):
options.append("-r %s" % ud.revision)
if command == "fetch":
if ud.user and ud.pswd:
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" clone %s %s://%s/%s %s" % (basecmd, ud.user, ud.pswd, proto, " ".join(options), proto, hgroot, ud.module, ud.module)
else:
cmd = "%s clone %s %s://%s/%s %s" % (basecmd, " ".join(options), proto, hgroot, ud.module, ud.module)
cmd = "%s clone %s %s://%s/%s %s" % (basecmd, " ".join(options), proto, hgroot, ud.module, ud.module)
elif command == "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
if ud.user and ud.pswd:
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" pull" % (basecmd, ud.user, ud.pswd, proto)
else:
cmd = "%s pull" % (basecmd)
cmd = "%s pull" % (basecmd)
elif command == "update":
if ud.user and ud.pswd:
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" update -C %s" % (basecmd, ud.user, ud.pswd, proto, " ".join(options))
else:
cmd = "%s update -C %s" % (basecmd, " ".join(options))
cmd = "%s update -C %s" % (basecmd, " ".join(options))
else:
raise FetchError("Invalid hg command %s" % command, ud.url)
return cmd
def download(self, ud, d):
def download(self, loc, ud, d):
"""Fetch url"""
logger.debug(2, "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")
logger.info("Update " + ud.url)
logger.info("Update " + loc)
# update sources there
os.chdir(ud.moddir)
logger.debug(1, "Running %s", updatecmd)
@@ -148,7 +136,7 @@ class Hg(FetchMethod):
else:
fetchcmd = self._buildhgcommand(ud, d, "fetch")
logger.info("Fetch " + ud.url)
logger.info("Fetch " + loc)
# check out sources there
bb.utils.mkdirhier(ud.pkgdir)
os.chdir(ud.pkgdir)
@@ -175,7 +163,7 @@ class Hg(FetchMethod):
def supports_srcrev(self):
return True
def _latest_revision(self, ud, d, name):
def _latest_revision(self, url, ud, d, name):
"""
Compute tip revision for the url
"""
@@ -183,10 +171,10 @@ class Hg(FetchMethod):
output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
return output.strip()
def _build_revision(self, ud, d, name):
def _build_revision(self, url, ud, d, name):
return ud.revision
def _revision_key(self, ud, d, name):
def _revision_key(self, url, ud, d, name):
"""
Return a unique key for the url
"""

View File

@@ -34,7 +34,7 @@ from bb.fetch2 import FetchMethod, FetchError
from bb.fetch2 import logger
class Local(FetchMethod):
def supports(self, urldata, d):
def supports(self, url, urldata, d):
"""
Check to see if a given url represents a local fetch.
"""
@@ -47,54 +47,42 @@ class Local(FetchMethod):
ud.basepath = ud.decodedurl
return
def localpath(self, urldata, d):
def localpath(self, url, urldata, d):
"""
Return the local filename of a given url assuming a successful fetch.
"""
return self.localpaths(urldata, d)[-1]
def localpaths(self, urldata, d):
"""
Return the local filename of a given url assuming a successful fetch.
"""
searched = []
path = urldata.decodedurl
newpath = path
if path[0] == "/":
return [path]
filespath = data.getVar('FILESPATH', d, True)
if filespath:
logger.debug(2, "Searching for %s in paths:\n %s" % (path, "\n ".join(filespath.split(":"))))
newpath, hist = bb.utils.which(filespath, path, history=True)
searched.extend(hist)
if not newpath:
filesdir = data.getVar('FILESDIR', d, True)
if filesdir:
logger.debug(2, "Searching for %s in path: %s" % (path, filesdir))
newpath = os.path.join(filesdir, path)
searched.append(newpath)
if (not newpath or not os.path.exists(newpath)) and path.find("*") != -1:
# For expressions using '*', best we can do is take the first directory in FILESPATH that exists
newpath, hist = bb.utils.which(filespath, ".", history=True)
searched.extend(hist)
logger.debug(2, "Searching for %s in path: %s" % (path, newpath))
return searched
if not os.path.exists(newpath):
dldirfile = os.path.join(d.getVar("DL_DIR", True), path)
logger.debug(2, "Defaulting to %s for %s" % (dldirfile, path))
bb.utils.mkdirhier(os.path.dirname(dldirfile))
searched.append(dldirfile)
return searched
return searched
if path[0] != "/":
filespath = data.getVar('FILESPATH', d, True)
if filespath:
logger.debug(2, "Searching for %s in paths: \n%s" % (path, "\n ".join(filespath.split(":"))))
newpath = bb.utils.which(filespath, path)
if not newpath:
filesdir = data.getVar('FILESDIR', d, True)
if filesdir:
logger.debug(2, "Searching for %s in path: %s" % (path, filesdir))
newpath = os.path.join(filesdir, path)
if (not newpath or not os.path.exists(newpath)) and path.find("*") != -1:
# For expressions using '*', best we can do is take the first directory in FILESPATH that exists
newpath = bb.utils.which(filespath, ".")
logger.debug(2, "Searching for %s in path: %s" % (path, newpath))
return newpath
if not os.path.exists(newpath):
dldirfile = os.path.join(d.getVar("DL_DIR", True), path)
logger.debug(2, "Defaulting to %s for %s" % (dldirfile, path))
bb.utils.mkdirhier(os.path.dirname(dldirfile))
return dldirfile
return newpath
def need_update(self, ud, d):
if ud.url.find("*") != -1:
def need_update(self, url, ud, d):
if url.find("*") != -1:
return False
if os.path.exists(ud.localpath):
return False
return True
def download(self, urldata, d):
def download(self, url, urldata, d):
"""Fetch urls (no-op for Local method)"""
# no need to fetch local files, we'll deal with them in place.
if self.supports_checksum(urldata) and not os.path.exists(urldata.localpath):
@@ -107,17 +95,17 @@ class Local(FetchMethod):
locations.append(filesdir)
locations.append(d.getVar("DL_DIR", True))
msg = "Unable to find file " + urldata.url + " anywhere. The paths that were searched were:\n " + "\n ".join(locations)
msg = "Unable to find file " + url + " anywhere. The paths that were searched were:\n " + "\n ".join(locations)
raise FetchError(msg)
return True
def checkstatus(self, urldata, d):
def checkstatus(self, url, urldata, d):
"""
Check the status of the url
"""
if urldata.localpath.find("*") != -1:
logger.info("URL %s looks like a glob and was therefore not checked.", urldata.url)
logger.info("URL %s looks like a glob and was therefore not checked.", url)
return True
if os.path.exists(urldata.localpath):
return True

View File

@@ -20,7 +20,7 @@ class Osc(FetchMethod):
"""Class to fetch a module or modules from Opensuse build server
repositories."""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with osc.
"""
@@ -77,7 +77,7 @@ class Osc(FetchMethod):
return osccmd
def download(self, ud, d):
def download(self, loc, ud, d):
"""
Fetch url
"""
@@ -86,7 +86,7 @@ class Osc(FetchMethod):
if os.access(os.path.join(data.expand('${OSCDIR}', d), ud.path, ud.module), os.R_OK):
oscupdatecmd = self._buildosccommand(ud, d, "update")
logger.info("Update "+ ud.url)
logger.info("Update "+ loc)
# update sources there
os.chdir(ud.moddir)
logger.debug(1, "Running %s", oscupdatecmd)
@@ -94,7 +94,7 @@ class Osc(FetchMethod):
runfetchcmd(oscupdatecmd, d)
else:
oscfetchcmd = self._buildosccommand(ud, d, "fetch")
logger.info("Fetch " + ud.url)
logger.info("Fetch " + loc)
# check out sources there
bb.utils.mkdirhier(ud.pkgdir)
os.chdir(ud.pkgdir)

View File

@@ -37,7 +37,7 @@ from bb.fetch2 import logger
from bb.fetch2 import runfetchcmd
class Perforce(FetchMethod):
def supports(self, ud, d):
def supports(self, url, ud, d):
return ud.type in ['p4']
def doparse(url, d):
@@ -48,7 +48,7 @@ class Perforce(FetchMethod):
(user, pswd, host, port) = path.split('@')[0].split(":")
path = path.split('@')[1]
else:
(host, port) = d.getVar('P4PORT').split(':')
(host, port) = data.getVar('P4PORT', d).split(':')
user = ""
pswd = ""
@@ -81,7 +81,7 @@ class Perforce(FetchMethod):
if host:
p4opt += " -p %s" % (host)
p4date = d.getVar("P4DATE", True)
p4date = data.getVar("P4DATE", d, True)
if "revision" in parm:
depot += "#%s" % (parm["revision"])
elif "label" in parm:
@@ -89,7 +89,7 @@ class Perforce(FetchMethod):
elif p4date:
depot += "@%s" % (p4date)
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
p4cmd = data.getVar('FETCHCOMMAND_p4', d, True)
logger.debug(1, "Running %s%s changes -m 1 %s", p4cmd, p4opt, depot)
p4file, errors = bb.process.run("%s%s changes -m 1 %s" % (p4cmd, p4opt, depot))
cset = p4file.strip()
@@ -103,22 +103,29 @@ class Perforce(FetchMethod):
def urldata_init(self, ud, d):
(host, path, user, pswd, parm) = Perforce.doparse(ud.url, d)
base_path = path.replace('/...', '')
base_path = self._strip_leading_slashes(base_path)
# If a label is specified, we use that as our filename
if "label" in parm:
version = parm["label"]
else:
version = Perforce.getcset(d, path, host, user, pswd, parm)
ud.localfile = "%s.tar.gz" % (parm["label"])
return
ud.localfile = data.expand('%s+%s+%s.tar.gz' % (host, base_path.replace('/', '.'), version), d)
base = path
which = path.find('/...')
if which != -1:
base = path[:which]
def download(self, ud, d):
base = self._strip_leading_slashes(base)
cset = Perforce.getcset(d, path, host, user, pswd, parm)
ud.localfile = data.expand('%s+%s+%s.tar.gz' % (host, base.replace('/', '.'), cset), d)
def download(self, loc, ud, d):
"""
Fetch urls
"""
(host, depot, user, pswd, parm) = Perforce.doparse(ud.url, d)
(host, depot, user, pswd, parm) = Perforce.doparse(loc, d)
if depot.find('/...') != -1:
path = depot[:depot.find('/...')]
@@ -127,6 +134,10 @@ class Perforce(FetchMethod):
module = parm.get('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:
@@ -138,16 +149,16 @@ class Perforce(FetchMethod):
if host:
p4opt += " -p %s" % (host)
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
p4cmd = data.getVar('FETCHCOMMAND', localdata, True)
# create temp directory
logger.debug(2, "Fetch: creating temporary directory")
bb.utils.mkdirhier(d.expand('${WORKDIR}'))
mktemp = d.getVar("FETCHCMD_p4mktemp", True) or d.expand("mktemp -d -q '${WORKDIR}/oep4.XXXXXX'")
tmpfile, errors = bb.process.run(mktemp)
bb.utils.mkdirhier(data.expand('${WORKDIR}', localdata))
data.setVar('TMPBASE', data.expand('${WORKDIR}/oep4.XXXXXX', localdata), localdata)
tmpfile, errors = bb.process.run(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmpfile.strip()
if not tmpfile:
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", ud.url)
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", loc)
if "label" in parm:
depot = "%s@%s" % (depot, parm["label"])
@@ -156,13 +167,13 @@ class Perforce(FetchMethod):
depot = "%s@%s" % (depot, cset)
os.chdir(tmpfile)
logger.info("Fetch " + ud.url)
logger.info("Fetch " + loc)
logger.info("%s%s files %s", p4cmd, p4opt, depot)
p4file, errors = bb.process.run("%s%s files %s" % (p4cmd, p4opt, depot))
p4file = [f.rstrip() for f in p4file.splitlines()]
if not p4file:
raise FetchError("Fetch: unable to get the P4 files from %s" % depot, ud.url)
raise FetchError("Fetch: unable to get the P4 files from %s" % depot, loc)
count = 0
@@ -180,7 +191,7 @@ class Perforce(FetchMethod):
if count == 0:
logger.error()
raise FetchError("Fetch: No files gathered from the P4 fetch", ud.url)
raise FetchError("Fetch: No files gathered from the P4 fetch", loc)
runfetchcmd("tar -czf %s %s" % (ud.localpath, module), d, cleanup = [ud.localpath])
# cleanup

View File

@@ -31,7 +31,7 @@ from bb.fetch2 import runfetchcmd
class Repo(FetchMethod):
"""Class to fetch a module or modules from repo (git) repositories"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with repo.
"""
@@ -53,7 +53,7 @@ class Repo(FetchMethod):
ud.localfile = data.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch), d)
def download(self, ud, d):
def download(self, loc, ud, d):
"""Fetch url"""
if os.access(os.path.join(data.getVar("DL_DIR", d, True), ud.localfile), os.R_OK):
@@ -91,8 +91,8 @@ class Repo(FetchMethod):
def supports_srcrev(self):
return False
def _build_revision(self, ud, d):
def _build_revision(self, url, ud, d):
return ud.manifest
def _want_sortable_revision(self, ud, d):
def _want_sortable_revision(self, url, ud, d):
return False

View File

@@ -72,7 +72,7 @@ from bb.fetch2 import runfetchcmd
class SFTP(FetchMethod):
"""Class to fetch urls via 'sftp'"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with sftp.
"""
@@ -95,10 +95,10 @@ class SFTP(FetchMethod):
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
def download(self, ud, d):
def download(self, uri, ud, d):
"""Fetch urls"""
urlo = URI(ud.url)
urlo = URI(uri)
basecmd = 'sftp -oPasswordAuthentication=no'
port = ''
if urlo.port:
@@ -124,6 +124,6 @@ class SFTP(FetchMethod):
cmd = '%s %s %s %s' % (basecmd, port, commands.mkarg(remote),
commands.mkarg(lpath))
bb.fetch2.check_network_access(d, cmd, ud.url)
bb.fetch2.check_network_access(d, cmd, uri)
runfetchcmd(cmd, d)
return True

View File

@@ -72,8 +72,8 @@ __pattern__ = re.compile(r'''
class SSH(FetchMethod):
'''Class to fetch a module or modules via Secure Shell'''
def supports(self, urldata, d):
return __pattern__.match(urldata.url) != None
def supports(self, url, urldata, d):
return __pattern__.match(url) != None
def supports_checksum(self, urldata):
return False
@@ -89,10 +89,10 @@ class SSH(FetchMethod):
host = m.group('host')
urldata.localpath = os.path.join(d.getVar('DL_DIR', True), os.path.basename(path))
def download(self, urldata, d):
def download(self, url, urldata, d):
dldir = d.getVar('DL_DIR', True)
m = __pattern__.match(urldata.url)
m = __pattern__.match(url)
path = m.group('path')
host = m.group('host')
port = m.group('port')

View File

@@ -0,0 +1,97 @@
# 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 logging
import bb
from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import MissingParameterError
from bb.fetch2 import logger
from bb.fetch2 import runfetchcmd
class Svk(FetchMethod):
"""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 urldata_init(self, ud, d):
if not "module" in ud.parm:
raise MissingParameterError('module', ud.url)
else:
ud.module = ud.parm["module"]
ud.revision = ud.parm.get('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)
def need_update(self, url, ud, d):
if ud.date == "now":
return True
if not os.path.exists(ud.localpath):
return True
return False
def download(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)
logger.debug(2, "Fetch: creating temporary directory")
bb.utils.mkdirhier(data.expand('${WORKDIR}', localdata))
data.setVar('TMPBASE', data.expand('${WORKDIR}/oesvk.XXXXXX', localdata), localdata)
tmpfile, errors = bb.process.run(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmpfile.strip()
if not tmpfile:
logger.error()
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", loc)
# check out sources there
os.chdir(tmpfile)
logger.info("Fetch " + loc)
logger.debug(1, "Running %s", svkcmd)
runfetchcmd(svkcmd, d, cleanup = [tmpfile])
os.chdir(os.path.join(tmpfile, os.path.dirname(ud.module)))
# tar them up to a defined filename
runfetchcmd("tar -czf %s %s" % (ud.localpath, os.path.basename(ud.module)), d, cleanup = [ud.localpath])
# cleanup
bb.utils.prunedir(tmpfile)

View File

@@ -27,7 +27,6 @@ import os
import sys
import logging
import bb
import re
from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
@@ -37,7 +36,7 @@ from bb.fetch2 import logger
class Svn(FetchMethod):
"""Class to fetch a module or modules from svn repositories"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with svn.
"""
@@ -92,8 +91,6 @@ class Svn(FetchMethod):
if command == "info":
svncmd = "%s info %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module)
elif command == "log1":
svncmd = "%s log --limit 1 %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module)
else:
suffix = ""
if ud.revision:
@@ -101,8 +98,7 @@ class Svn(FetchMethod):
suffix = "@%s" % (ud.revision)
if command == "fetch":
transportuser = ud.parm.get("transportuser", "")
svncmd = "%s co %s %s://%s%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, transportuser, svnroot, ud.module, suffix, ud.module)
svncmd = "%s co %s %s://%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module, suffix, ud.module)
elif command == "update":
svncmd = "%s update %s" % (ud.basecmd, " ".join(options))
else:
@@ -113,14 +109,14 @@ class Svn(FetchMethod):
return svncmd
def download(self, ud, d):
def download(self, loc, ud, d):
"""Fetch url"""
logger.debug(2, "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")
logger.info("Update " + ud.url)
logger.info("Update " + loc)
# update sources there
os.chdir(ud.moddir)
# We need to attempt to run svn upgrade first in case its an older working format
@@ -133,7 +129,7 @@ class Svn(FetchMethod):
runfetchcmd(svnupdatecmd, d)
else:
svnfetchcmd = self._buildsvncommand(ud, d, "fetch")
logger.info("Fetch " + ud.url)
logger.info("Fetch " + loc)
# check out sources there
bb.utils.mkdirhier(ud.pkgdir)
os.chdir(ud.pkgdir)
@@ -161,32 +157,33 @@ class Svn(FetchMethod):
def supports_srcrev(self):
return True
def _revision_key(self, ud, d, name):
def _revision_key(self, url, ud, d, name):
"""
Return a unique key for the url
"""
return "svn:" + ud.moddir
def _latest_revision(self, ud, d, name):
def _latest_revision(self, url, ud, d, name):
"""
Return the latest upstream revision number
"""
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"))
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "info"))
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True)
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "info"), d, True)
# skip the first line, as per output of svn log
# then we expect the revision on the 2nd line
revision = re.search('^r([0-9]*)', output.splitlines()[1]).group(1)
revision = None
for line in output.splitlines():
if "Last Changed Rev" in line:
revision = line.split(":")[1].strip()
return revision
def sortable_revision(self, ud, d, name):
def sortable_revision(self, url, ud, d, name):
"""
Return a sortable revision number which in our case is the revision number
"""
return False, self._build_revision(ud, d)
return False, self._build_revision(url, ud, d)
def _build_revision(self, ud, d):
def _build_revision(self, url, ud, d):
return ud.revision

View File

@@ -37,7 +37,7 @@ from bb.fetch2 import runfetchcmd
class Wget(FetchMethod):
"""Class to fetch urls via 'wget'"""
def supports(self, ud, d):
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with wget.
"""
@@ -58,49 +58,40 @@ class Wget(FetchMethod):
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
def _runwget(self, ud, d, command, quiet):
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
bb.fetch2.check_network_access(d, command)
runfetchcmd(command, d, quiet)
def download(self, ud, d):
def download(self, uri, ud, d, checkonly = False):
"""Fetch urls"""
fetchcmd = self.basecmd
basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
if 'downloadfilename' in ud.parm:
if not checkonly and 'downloadfilename' in ud.parm:
dldir = d.getVar("DL_DIR", True)
bb.utils.mkdirhier(os.path.dirname(dldir + os.sep + ud.localfile))
fetchcmd += " -O " + dldir + os.sep + ud.localfile
basecmd += " -O " + dldir + os.sep + ud.localfile
uri = ud.url.split(";")[0]
if os.path.exists(ud.localpath):
if checkonly:
fetchcmd = d.getVar("CHECKCOMMAND_wget", True) or d.expand(basecmd + " --spider '${URI}'")
elif os.path.exists(ud.localpath):
# file exists, but we didnt complete it.. trying again..
fetchcmd += d.expand(" -c -P ${DL_DIR} '%s'" % uri)
fetchcmd = d.getVar("RESUMECOMMAND_wget", True) or d.expand(basecmd + " -c -P ${DL_DIR} '${URI}'")
else:
fetchcmd += d.expand(" -P ${DL_DIR} '%s'" % uri)
fetchcmd = d.getVar("FETCHCOMMAND_wget", True) or d.expand(basecmd + " -P ${DL_DIR} '${URI}'")
self._runwget(ud, d, fetchcmd, False)
uri = uri.split(";")[0]
fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0])
fetchcmd = fetchcmd.replace("${FILE}", ud.basename)
if not checkonly:
logger.info("fetch " + uri)
logger.debug(2, "executing " + fetchcmd)
bb.fetch2.check_network_access(d, fetchcmd)
runfetchcmd(fetchcmd, d, quiet=checkonly)
# 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):
if not os.path.exists(ud.localpath) and not checkonly:
raise FetchError("The fetch command returned success for url %s but %s doesn't exist?!" % (uri, ud.localpath), uri)
if os.path.getsize(ud.localpath) == 0:
os.remove(ud.localpath)
raise FetchError("The fetch of %s resulted in a zero size file?! Deleting and failing since this isn't right." % (uri), uri)
return True
def checkstatus(self, ud, d):
uri = ud.url.split(";")[0]
fetchcmd = self.basecmd + " --spider '%s'" % uri
self._runwget(ud, d, fetchcmd, True)
return True
def checkstatus(self, uri, ud, d):
return self.download(uri, ud, d, True)

View File

@@ -52,10 +52,10 @@ def getMountedDev(path):
parentDev = os.stat(path).st_dev
currentDev = parentDev
# When the current directory's device is different from the
# parent's, then the current directory is a mount point
# parrent's, then the current directory is a mount point
while parentDev == currentDev:
mountPoint = path
# Use dirname to get the parent's directory
# Use dirname to get the parrent's directory
path = os.path.dirname(path)
# Reach the "/"
if path == mountPoint:
@@ -77,7 +77,7 @@ def getDiskData(BBDirs, configuration):
"""Prepare disk data for disk space monitor"""
# Save the device IDs, need the ID to be unique (the dictionary's key is
# unique), so that when more than one directory is located on the same
# unique), so that when more than one directories are located in the same
# device, we just monitor it once
devDict = {}
for pathSpaceInode in BBDirs.split():
@@ -187,11 +187,11 @@ class diskMonitor:
if self.spaceInterval and self.inodeInterval:
self.enableMonitor = True
# These are for saving the previous disk free space and inode, we
# use them to avoid printing too many warning messages
# use them to avoid print too many warning messages
self.preFreeS = {}
self.preFreeI = {}
# This is for STOPTASKS and ABORT, to avoid printing the message
# repeatedly while waiting for the tasks to finish
# This is for STOPTASKS and ABORT, to avoid print the message repeatly
# during waiting the tasks to finish
self.checked = {}
for k in self.devDict:
self.preFreeS[k] = 0
@@ -239,9 +239,11 @@ class diskMonitor:
freeInode = st.f_favail
if minInode and freeInode < minInode:
# Some filesystems use dynamic inodes so can't run out
# (e.g. btrfs). This is reported by the inode count being 0.
# Some fs formats' (e.g., btrfs) statvfs.f_files (inodes) is
# zero, this is a feature of the fs, we disable the inode
# checking for such a fs.
if st.f_files == 0:
logger.warn("Inode check for %s is unavaliable, will remove it from disk monitor" % path)
self.devDict[k][2] = None
continue
# Always show warning, the self.checked would always be False if the action is WARN

View File

@@ -126,21 +126,7 @@ class BBLogFilter(object):
return True
return False
class BBLogFilterStdErr(BBLogFilter):
def filter(self, record):
if not BBLogFilter.filter(self, record):
return False
if record.levelno >= logging.ERROR:
return True
return False
class BBLogFilterStdOut(BBLogFilter):
def filter(self, record):
if not BBLogFilter.filter(self, record):
return False
if record.levelno < logging.ERROR:
return True
return False
# Message control functions
#
@@ -178,10 +164,10 @@ def constructLogOptions():
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
return level, debug_domains
def addDefaultlogFilter(handler, cls = BBLogFilter):
def addDefaultlogFilter(handler):
level, debug_domains = constructLogOptions()
cls(handler, level, debug_domains)
BBLogFilter(handler, level, debug_domains)
#
# Message handling functions

View File

@@ -202,8 +202,8 @@ if __name__ == '__main__':
print(rec5._replace(k=222)._my_custom_method()) # MyMixIn's
print(rec5._replace(k=222).count(2)) # MyMixIn's
# Note that behavior: the standard namedtuple methods cannot be
# overridden by a foreign mix-in -- even if the mix-in is declared
# None that behavior: the standard namedtuple methods cannot be
# overriden by a foreign mix-in -- even if the mix-in is declared
# as the leftmost base class (but, obviously, you can override them
# in the defined class or its subclasses):

View File

@@ -49,11 +49,8 @@ class ParseError(Exception):
else:
return "ParseError in %s: %s" % (self.filename, self.msg)
class SkipRecipe(Exception):
"""Exception raised to skip this recipe"""
class SkipPackage(SkipRecipe):
"""Exception raised to skip this recipe (use SkipRecipe in new code)"""
class SkipPackage(Exception):
"""Exception raised to skip this package"""
__mtime_cache = {}
def cached_mtime(f):
@@ -73,25 +70,12 @@ def update_mtime(f):
__mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
return __mtime_cache[f]
def update_cache(f):
if f in __mtime_cache:
logger.debug(1, "Updating mtime cache for %s" % f)
update_mtime(f)
def mark_dependency(d, f):
if f.startswith('./'):
f = "%s/%s" % (os.getcwd(), f[2:])
deps = (d.getVar('__depends') or [])
s = (f, cached_mtime_noerror(f))
if s not in deps:
deps.append(s)
d.setVar('__depends', deps)
deps = (d.getVar('__depends') or []) + [(f, cached_mtime(f))]
d.setVar('__depends', deps)
def check_dependency(d, f):
s = (f, cached_mtime_noerror(f))
deps = (d.getVar('__depends') or [])
return s in deps
def supports(fn, data):
"""Returns true if we have a handler for this file, false otherwise"""
for h in handlers:
@@ -118,23 +102,21 @@ def init_parser(d):
def resolve_file(fn, d):
if not os.path.isabs(fn):
bbpath = d.getVar("BBPATH", True)
newfn, attempts = bb.utils.which(bbpath, fn, history=True)
for af in attempts:
mark_dependency(d, af)
newfn = bb.utils.which(bbpath, fn)
if not newfn:
raise IOError("file %s not found in %s" % (fn, bbpath))
fn = newfn
mark_dependency(d, fn)
if not os.path.isfile(fn):
raise IOError("file %s not found" % fn)
logger.debug(2, "LOAD %s", fn)
return fn
# Used by OpenEmbedded metadata
__pkgsplit_cache__={}
def vars_from_file(mypkg, d):
if not mypkg or not mypkg.endswith((".bb", ".bbappend")):
if not mypkg:
return (None, None, None)
if mypkg in __pkgsplit_cache__:
return __pkgsplit_cache__[mypkg]

View File

@@ -139,8 +139,6 @@ class DataNode(AstNode):
data.setVar(key, val, **loginfo)
class MethodNode(AstNode):
tr_tbl = string.maketrans('/.+-@%', '______')
def __init__(self, filename, lineno, func_name, body):
AstNode.__init__(self, filename, lineno)
self.func_name = func_name
@@ -149,7 +147,7 @@ class MethodNode(AstNode):
def eval(self, data):
text = '\n'.join(self.body)
if self.func_name == "__anonymous":
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(MethodNode.tr_tbl)))
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(string.maketrans('/.+-@', '_____'))))
text = "def %s(d):\n" % (funcname) + text
bb.methodpool.insert_method(funcname, text, self.filename)
anonfuncs = data.getVar('__BBANONFUNCS') or []
@@ -226,8 +224,6 @@ class ExportFuncsNode(AstNode):
if data.getVarFlag(calledfunc, "python"):
data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n")
else:
if "-" in self.classname:
bb.fatal("The classname %s contains a dash character and is calling an sh function %s using EXPORT_FUNCTIONS. Since a dash is illegal in sh function names, this cannot work, please rename the class or don't use EXPORT_FUNCTIONS." % (self.classname, calledfunc))
data.setVar(func, " " + calledfunc + "\n")
data.setVarFlag(func, 'export_func', '1')
@@ -239,15 +235,29 @@ class AddTaskNode(AstNode):
self.after = after
def eval(self, data):
bb.build.addtask(self.func, self.before, self.after, data)
var = self.func
if self.func[:3] != "do_":
var = "do_" + self.func
class DelTaskNode(AstNode):
def __init__(self, filename, lineno, func):
AstNode.__init__(self, filename, lineno)
self.func = func
data.setVarFlag(var, "task", 1)
bbtasks = data.getVar('__BBTASKS') or []
if not var in bbtasks:
bbtasks.append(var)
data.setVar('__BBTASKS', bbtasks)
def eval(self, data):
bb.build.deltask(self.func, data)
existing = data.getVarFlag(var, "deps") 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)
data.setVarFlag(var, "deps", existing)
if self.before is not None:
# set up things that depend on this func
for entry in self.before.split():
existing = data.getVarFlag(entry, "deps") or []
if var not in existing:
data.setVarFlag(entry, "deps", [var] + existing)
class BBHandlerNode(AstNode):
def __init__(self, filename, lineno, fns):
@@ -299,13 +309,6 @@ def handleAddTask(statements, filename, lineno, m):
statements.append(AddTaskNode(filename, lineno, func, before, after))
def handleDelTask(statements, filename, lineno, m):
func = m.group("func")
if func is None:
return
statements.append(DelTaskNode(filename, lineno, func))
def handleBBHandlers(statements, filename, lineno, m):
statements.append(BBHandlerNode(filename, lineno, m.group(1)))
@@ -330,8 +333,7 @@ def finalize(fn, d, variant = None):
bb.data.update_data(d)
tasklist = d.getVar('__BBTASKS') or []
deltasklist = d.getVar('__BBDELTASKS') or []
bb.build.add_tasks(tasklist, deltasklist, d)
bb.build.add_tasks(tasklist, d)
bb.parse.siggen.finalise(fn, d, variant)
@@ -339,10 +341,8 @@ def finalize(fn, d, variant = None):
bb.event.fire(bb.event.RecipeParsed(fn), d)
def _create_variants(datastores, names, function, onlyfinalise):
def _create_variants(datastores, names, function):
def create_variant(name, orig_d, arg = None):
if onlyfinalise and name not in onlyfinalise:
return
new_d = bb.data.createCopy(orig_d)
function(arg or name, new_d)
datastores[name] = new_d
@@ -379,7 +379,7 @@ def _expand_versions(versions):
def multi_finalize(fn, d):
appends = (d.getVar("__BBAPPEND", True) or "").split()
for append in appends:
logger.debug(1, "Appending .bbappend file %s to %s", append, fn)
logger.debug(2, "Appending .bbappend file %s to %s", append, fn)
bb.parse.BBHandler.handle(append, d, True)
onlyfinalise = d.getVar("__ONLYFINALISE", False)
@@ -388,7 +388,7 @@ def multi_finalize(fn, d):
d = bb.data.createCopy(safe_d)
try:
finalize(fn, d)
except bb.parse.SkipRecipe as e:
except bb.parse.SkipPackage as e:
d.setVar("__SKIPPED", e.args[0])
datastores = {"": safe_d}
@@ -431,10 +431,10 @@ def multi_finalize(fn, d):
verfunc(pv, d, safe_d)
try:
finalize(fn, d)
except bb.parse.SkipRecipe as e:
except bb.parse.SkipPackage as e:
d.setVar("__SKIPPED", e.args[0])
_create_variants(datastores, versions, verfunc, onlyfinalise)
_create_variants(datastores, versions, verfunc)
extended = d.getVar("BBCLASSEXTEND", True) or ""
if extended:
@@ -464,14 +464,14 @@ def multi_finalize(fn, d):
bb.parse.BBHandler.inherit(extendedmap[name], fn, 0, d)
safe_d.setVar("BBCLASSEXTEND", extended)
_create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
_create_variants(datastores, extendedmap.keys(), extendfunc)
for variant, variant_d in datastores.iteritems():
if variant:
try:
if not onlyfinalise or variant in onlyfinalise:
finalize(fn, variant_d, variant)
except bb.parse.SkipRecipe as e:
except bb.parse.SkipPackage as e:
variant_d.setVar("__SKIPPED", e.args[0])
if len(datastores) > 1:

View File

@@ -42,7 +42,6 @@ __func_start_regexp__ = re.compile( r"(((?P<py>python)|(?P<fr>fakeroot))\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))|(.*)))))*")
__deltask_regexp__ = re.compile("deltask\s+(?P<func>\w+)")
__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
@@ -78,15 +77,12 @@ def inherit(files, fn, lineno, d):
if not os.path.isabs(file):
dname = os.path.dirname(fn)
bbpath = "%s:%s" % (dname, d.getVar("BBPATH", True))
abs_fn, attempts = bb.utils.which(bbpath, file, history=True)
for af in attempts:
if af != abs_fn:
bb.parse.mark_dependency(d, af)
abs_fn = bb.utils.which(bbpath, file)
if abs_fn:
file = abs_fn
if not file in __inherit_cache:
logger.debug(1, "Inheriting %s (from %s:%d)" % (file, fn, lineno))
logger.log(logging.DEBUG -1, "BB %s:%d: inheriting %s", fn, lineno, file)
__inherit_cache.append( file )
d.setVar('__inherit_cache', __inherit_cache)
include(fn, file, lineno, d, "inherit")
@@ -124,6 +120,12 @@ def handle(fn, d, include):
__classname__ = ""
__residue__ = []
if include == 0:
logger.debug(2, "BB %s: handle(data)", fn)
else:
logger.debug(2, "BB %s: handle(data, include)", fn)
base_name = os.path.basename(fn)
(root, ext) = os.path.splitext(base_name)
init(d)
@@ -154,7 +156,7 @@ def handle(fn, d, include):
try:
statements.eval(d)
except bb.parse.SkipRecipe:
except bb.parse.SkipPackage:
bb.data.setVar("__SKIPPED", True, d)
if include == 0:
return { "" : d }
@@ -238,11 +240,6 @@ def feeder(lineno, s, fn, root, statements):
ast.handleAddTask(statements, fn, lineno, m)
return
m = __deltask_regexp__.match(s)
if m:
ast.handleDelTask(statements, fn, lineno, m)
return
m = __addhandler_regexp__.match(s)
if m:
ast.handleBBHandlers(statements, fn, lineno, m)

View File

@@ -82,15 +82,9 @@ def include(oldfn, fn, lineno, data, error_out):
if not os.path.isabs(fn):
dname = os.path.dirname(oldfn)
bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
if abs_fn and bb.parse.check_dependency(data, abs_fn):
bb.warn("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
for af in attempts:
bb.parse.mark_dependency(data, af)
abs_fn = bb.utils.which(bbpath, fn)
if abs_fn:
fn = abs_fn
elif bb.parse.check_dependency(data, fn):
bb.warn("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
from bb.parse import handle
try:
@@ -99,7 +93,6 @@ def include(oldfn, fn, lineno, data, error_out):
if error_out:
raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
logger.debug(2, "CONF file '%s' not found", fn)
bb.parse.mark_dependency(data, fn)
# We have an issue where a UI might want to enforce particular settings such as
# an empty DISTRO variable. If configuration files do something like assigning
@@ -144,7 +137,7 @@ def handle(fn, data, include):
# skip comments
if s[0] == '#':
continue
feeder(lineno, s, abs_fn, statements)
feeder(lineno, s, fn, statements)
# DONE WITH PARSING... time to evaluate
data.setVar('FILE', abs_fn)

View File

@@ -199,9 +199,7 @@ class PersistData(object):
del self.data[domain][key]
def connect(database):
connection = sqlite3.connect(database, timeout=5, isolation_level=None)
connection.execute("pragma synchronous = off;")
return connection
return sqlite3.connect(database, timeout=5, isolation_level=None)
def persist(domain, d):
"""Convenience factory for SQLTable objects based upon metadata"""

View File

@@ -84,7 +84,7 @@ def _logged_communicate(pipe, log, input):
while pipe.poll() is None:
rlist = rin
try:
r,w,e = select.select (rlist, [], [], 1)
r,w,e = select.select (rlist, [], [])
except OSError as e:
if e.errno != errno.EINTR:
raise

View File

@@ -325,7 +325,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
provides = dataCache.pn_provides[pn]
for provide in provides:
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
#logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
if prefervar in pns and pns[prefervar] not in preferred:
var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)

View File

@@ -38,4 +38,4 @@ class ExitSignal(ShellError):
class ReturnSignal(ShellError):
"""Exit signal."""
pass
pass

View File

@@ -30,7 +30,6 @@ import stat
import fcntl
import errno
import logging
import re
import bb
from bb import msg, data, event
from bb import monitordisk
@@ -44,8 +43,6 @@ except ImportError:
bblogger = logging.getLogger("BitBake")
logger = logging.getLogger("BitBake.RunQueue")
__find_md5__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{32}(?![a-z0-9])' )
class RunQueueStats:
"""
Holds statistics on the tasks handled by the associated runQueue
@@ -97,56 +94,30 @@ class RunQueueScheduler(object):
def __init__(self, runqueue, rqdata):
"""
The default scheduler just returns the first buildable task (the
priority map is sorted by task number)
priority map is sorted by task numer)
"""
self.rq = runqueue
self.rqdata = rqdata
self.numTasks = len(self.rqdata.runq_fnid)
numTasks = len(self.rqdata.runq_fnid)
self.prio_map = []
self.prio_map.extend(range(self.numTasks))
self.buildable = []
self.stamps = {}
for taskid in xrange(self.numTasks):
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[taskid]]
taskname = self.rqdata.runq_task[taskid]
self.stamps[taskid] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
if self.rq.runq_buildable[taskid] == 1:
self.buildable.append(taskid)
self.rev_prio_map = None
self.prio_map.extend(range(numTasks))
def next_buildable_task(self):
"""
Return the id of the first task we find that is buildable
"""
self.buildable = [x for x in self.buildable if not self.rq.runq_running[x] == 1]
if not self.buildable:
return None
if len(self.buildable) == 1:
taskid = self.buildable[0]
stamp = self.stamps[taskid]
if stamp not in self.rq.build_stamps.itervalues():
return taskid
if not self.rev_prio_map:
self.rev_prio_map = range(self.numTasks)
for taskid in xrange(self.numTasks):
self.rev_prio_map[self.prio_map[taskid]] = taskid
best = None
bestprio = None
for taskid in self.buildable:
prio = self.rev_prio_map[taskid]
if bestprio is None or bestprio > prio:
stamp = self.stamps[taskid]
if stamp in self.rq.build_stamps.itervalues():
for tasknum in xrange(len(self.rqdata.runq_fnid)):
taskid = self.prio_map[tasknum]
if self.rq.runq_running[taskid] == 1:
continue
if self.rq.runq_buildable[taskid] == 1:
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[taskid]]
taskname = self.rqdata.runq_task[taskid]
stamp = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
if stamp in self.rq.build_stamps.values():
continue
bestprio = prio
best = taskid
return best
return taskid
def next(self):
"""
@@ -155,9 +126,6 @@ class RunQueueScheduler(object):
if self.rq.stats.active < self.rq.number_tasks:
return self.next_buildable_task()
def newbuilable(self, task):
self.buildable.append(task)
class RunQueueSchedulerSpeed(RunQueueScheduler):
"""
A scheduler optimised for speed. The priority map is sorted by task weight,
@@ -169,7 +137,9 @@ class RunQueueSchedulerSpeed(RunQueueScheduler):
"""
The priority map is sorted by task weight.
"""
RunQueueScheduler.__init__(self, runqueue, rqdata)
self.rq = runqueue
self.rqdata = rqdata
sortweight = sorted(copy.deepcopy(self.rqdata.runq_weight))
copyweight = copy.deepcopy(self.rqdata.runq_weight)
@@ -186,7 +156,7 @@ class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
"""
A scheduler optimised to complete .bb files are quickly as possible. The
priority map is sorted by task weight, but then reordered so once a given
.bb file starts to build, it's completed as quickly as possible. This works
.bb file starts to build, its completed as quickly as possible. This works
well where disk space is at a premium and classes like OE's rm_work are in
force.
"""
@@ -370,11 +340,11 @@ class RunQueueData:
for listid in xrange(numTasks):
task_done.append(False)
weight.append(1)
weight.append(0)
deps_left.append(len(self.runq_revdeps[listid]))
for listid in endpoints:
weight[listid] = 10
weight[listid] = 1
task_done[listid] = True
while True:
@@ -481,7 +451,7 @@ class RunQueueData:
fn = taskData.fn_index[fnid]
task_deps = self.dataCache.task_deps[fn]
#logger.debug(2, "Processing %s:%s", fn, taskData.tasks_name[task])
logger.debug(2, "Processing %s:%s", fn, taskData.tasks_name[task])
if fnid not in taskData.failed_fnids:
@@ -795,7 +765,7 @@ class RunQueueData:
for st in self.cooker.configuration.invalidate_stamp.split(','):
invalidate_task(fn, "do_%s" % st, True)
# Iterate over the task list and call into the siggen code
# Interate over the task list and call into the siggen code
dealtwith = set()
todeal = set(range(len(self.runq_fnid)))
while len(todeal) > 0:
@@ -808,6 +778,19 @@ class RunQueueData:
procdep.append(self.taskData.fn_index[self.runq_fnid[dep]] + "." + self.runq_task[dep])
self.runq_hash[task] = bb.parse.siggen.get_taskhash(self.taskData.fn_index[self.runq_fnid[task]], self.runq_task[task], procdep, self.dataCache)
self.hashes = {}
self.hash_deps = {}
for task in xrange(len(self.runq_fnid)):
identifier = '%s.%s' % (self.taskData.fn_index[self.runq_fnid[task]],
self.runq_task[task])
self.hashes[identifier] = self.runq_hash[task]
deps = []
for dep in self.runq_depends[task]:
depidentifier = '%s.%s' % (self.taskData.fn_index[self.runq_fnid[dep]],
self.runq_task[dep])
deps.append(depidentifier)
self.hash_deps[identifier] = deps
return len(self.runq_fnid)
def dump_data(self, taskQueue):
@@ -859,27 +842,26 @@ class RunQueue:
def _start_worker(self, fakeroot = False, rqexec = None):
logger.debug(1, "Starting bitbake-worker")
magic = "decafbad"
if self.cooker.configuration.profile:
magic = "decafbadbad"
if fakeroot:
fakerootcmd = self.cfgData.getVar("FAKEROOTCMD", True)
fakerootenv = (self.cfgData.getVar("FAKEROOTBASEENV", True) or "").split()
env = os.environ.copy()
for key, value in (var.split('=') for var in fakerootenv):
env[key] = value
worker = subprocess.Popen([fakerootcmd, "bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
worker = subprocess.Popen([fakerootcmd, "bitbake-worker", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
else:
worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
worker = subprocess.Popen(["bitbake-worker", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
bb.utils.nonblockingfd(worker.stdout)
workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec)
workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, rqexec)
workerdata = {
"taskdeps" : self.rqdata.dataCache.task_deps,
"fakerootenv" : self.rqdata.dataCache.fakerootenv,
"fakerootdirs" : self.rqdata.dataCache.fakerootdirs,
"fakerootnoenv" : self.rqdata.dataCache.fakerootnoenv,
"sigdata" : bb.parse.siggen.get_taskdata(),
"hashes" : self.rqdata.hashes,
"hash_deps" : self.rqdata.hash_deps,
"sigchecksums" : bb.parse.siggen.file_checksum_values,
"runq_hash" : self.rqdata.runq_hash,
"logdefaultdebug" : bb.msg.loggerDefaultDebugLevel,
"logdefaultverbose" : bb.msg.loggerDefaultVerbose,
@@ -901,11 +883,8 @@ class RunQueue:
if not worker:
return
logger.debug(1, "Teardown for bitbake-worker")
try:
worker.stdin.write("<quit></quit>")
worker.stdin.flush()
except IOError:
pass
worker.stdin.write("<quit></quit>")
worker.stdin.flush()
while worker.returncode is None:
workerpipe.read()
worker.poll()
@@ -916,7 +895,6 @@ class RunQueue:
def start_worker(self):
if self.worker:
self.teardown_workers()
self.teardown = False
self.worker, self.workerpipe = self._start_worker()
def start_fakeworker(self, rqexec):
@@ -924,7 +902,6 @@ class RunQueue:
self.fakeworker, self.fakeworkerpipe = self._start_worker(True, rqexec)
def teardown_workers(self):
self.teardown = True
self._teardown_worker(self.worker, self.workerpipe)
self.worker = None
self.workerpipe = None
@@ -968,11 +945,11 @@ class RunQueue:
stampfile = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
# If the stamp is missing, it's not current
# If the stamp is missing its not current
if not os.access(stampfile, os.F_OK):
logger.debug(2, "Stampfile %s not available", stampfile)
return False
# If it's a 'nostamp' task, it's not current
# If its a 'nostamp' task, it's not current
taskdep = self.rqdata.dataCache.task_deps[fn]
if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
logger.debug(2, "%s.%s is nostamp\n", fn, taskname)
@@ -1037,14 +1014,8 @@ class RunQueue:
bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
if self.state is runQueueSceneInit:
dump = self.cooker.configuration.dump_signatures
if dump:
if 'printdiff' in dump:
invalidtasks = self.print_diffscenetasks()
self.dump_signatures(dump)
if 'printdiff' in dump:
self.write_diffscenetasks(invalidtasks)
self.state = runQueueComplete
if self.cooker.configuration.dump_signatures:
self.dump_signatures()
else:
self.start_worker()
self.rqexe = RunQueueExecuteScenequeue(self)
@@ -1064,9 +1035,9 @@ class RunQueue:
retval = self.rqexe.execute()
if self.state is runQueueCleanUp:
retval = self.rqexe.finish()
self.rqexe.finish()
if (self.state is runQueueComplete or self.state is runQueueFailed) and self.rqexe:
if self.state is runQueueComplete or self.state is runQueueFailed:
self.teardown_workers()
if self.rqexe.stats.failed:
logger.info("Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed.", self.rqexe.stats.completed + self.rqexe.stats.failed, self.rqexe.stats.skipped, self.rqexe.stats.failed)
@@ -1107,7 +1078,6 @@ class RunQueue:
def finish_runqueue(self, now = False):
if not self.rqexe:
self.state = runQueueComplete
return
if now:
@@ -1115,7 +1085,8 @@ class RunQueue:
else:
self.rqexe.finish()
def dump_signatures(self, options):
def dump_signatures(self):
self.state = runQueueComplete
done = set()
bb.note("Reparsing files to collect dependency data")
for task in range(len(self.rqdata.runq_fnid)):
@@ -1124,126 +1095,10 @@ class RunQueue:
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.collection.get_file_appends(fn), self.cooker.data)
done.add(self.rqdata.runq_fnid[task])
bb.parse.siggen.dump_sigs(self.rqdata.dataCache, options)
bb.parse.siggen.dump_sigs(self.rqdata.dataCache)
return
def print_diffscenetasks(self):
valid = []
sq_hash = []
sq_hashfn = []
sq_fn = []
sq_taskname = []
sq_task = []
noexec = []
stamppresent = []
valid_new = set()
for task in xrange(len(self.rqdata.runq_fnid)):
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
taskname = self.rqdata.runq_task[task]
taskdep = self.rqdata.dataCache.task_deps[fn]
if 'noexec' in taskdep and taskname in taskdep['noexec']:
noexec.append(task)
continue
sq_fn.append(fn)
sq_hashfn.append(self.rqdata.dataCache.hashfn[fn])
sq_hash.append(self.rqdata.runq_hash[task])
sq_taskname.append(taskname)
sq_task.append(task)
call = self.hashvalidate + "(sq_fn, sq_task, sq_hash, sq_hashfn, d)"
locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.data }
valid = bb.utils.better_eval(call, locs)
for v in valid:
valid_new.add(sq_task[v])
# Tasks which are both setscene and noexec never care about dependencies
# We therefore find tasks which are setscene and noexec and mark their
# unique dependencies as valid.
for task in noexec:
if task not in self.rqdata.runq_setscene:
continue
for dep in self.rqdata.runq_depends[task]:
hasnoexecparents = True
for dep2 in self.rqdata.runq_revdeps[dep]:
if dep2 in self.rqdata.runq_setscene and dep2 in noexec:
continue
hasnoexecparents = False
break
if hasnoexecparents:
valid_new.add(dep)
invalidtasks = set()
for task in xrange(len(self.rqdata.runq_fnid)):
if task not in valid_new and task not in noexec:
invalidtasks.add(task)
found = set()
processed = set()
for task in invalidtasks:
toprocess = set([task])
while toprocess:
next = set()
for t in toprocess:
for dep in self.rqdata.runq_depends[t]:
if dep in invalidtasks:
found.add(task)
if dep not in processed:
processed.add(dep)
next.add(dep)
toprocess = next
if task in found:
toprocess = set()
tasklist = []
for task in invalidtasks.difference(found):
tasklist.append(self.rqdata.get_user_idstring(task))
if tasklist:
bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
return invalidtasks.difference(found)
def write_diffscenetasks(self, invalidtasks):
# Define recursion callback
def recursecb(key, hash1, hash2):
hashes = [hash1, hash2]
hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
recout = []
if len(hashfiles) == 2:
out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
recout.extend(list(' ' + l for l in out2))
else:
recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
return recout
for task in invalidtasks:
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
pn = self.rqdata.dataCache.pkg_fn[fn]
taskname = self.rqdata.runq_task[task]
h = self.rqdata.runq_hash[task]
matches = bb.siggen.find_siginfo(pn, taskname, [], self.cfgData)
match = None
for m in matches:
if h in m:
match = m
if match is None:
bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
matches = {k : v for k, v in matches.iteritems() if h not in k}
if matches:
latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
prevh = __find_md5__.search(latestmatch).group(0)
output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, closest matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
else:
bb.plain("Error, can't find multiple tasks at divergence point? Was there a previously run task?")
class RunQueueExecute:
@@ -1261,7 +1116,6 @@ class RunQueueExecute:
self.runq_complete = []
self.build_stamps = {}
self.build_stamps2 = []
self.failed_fnids = []
self.stampcache = {}
@@ -1274,7 +1128,6 @@ class RunQueueExecute:
# self.build_stamps[pid] may not exist when use shared work directory.
if task in self.build_stamps:
self.build_stamps2.remove(self.build_stamps[task])
del self.build_stamps[task]
if status != 0:
@@ -1285,15 +1138,11 @@ class RunQueueExecute:
def finish_now(self):
for worker in [self.rq.worker, self.rq.fakeworker]:
if not worker:
continue
try:
worker.stdin.write("<finishnow></finishnow>")
worker.stdin.flush()
except IOError:
# worker must have died?
pass
self.rq.worker.stdin.write("<finishnow></finishnow>")
self.rq.worker.stdin.flush()
if self.rq.fakeworker:
self.rq.fakeworker.stdin.write("<finishnow></finishnow>")
self.rq.fakeworker.stdin.flush()
if len(self.failed_fnids) != 0:
self.rq.state = runQueueFailed
@@ -1308,14 +1157,15 @@ class RunQueueExecute:
if self.stats.active > 0:
bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData)
self.rq.read_workers()
return self.rq.active_fds()
return
if len(self.failed_fnids) != 0:
self.rq.state = runQueueFailed
return True
return
self.rq.state = runQueueComplete
return True
return
def check_dependencies(self, task, taskdeps, setscene = False):
if not self.rq.depvalidate:
@@ -1354,8 +1204,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
self.stampcache = {}
initial_covered = self.rq.scenequeue_covered.copy()
# Mark initial buildable tasks
for task in xrange(self.stats.total):
self.runq_running.append(0)
@@ -1409,27 +1257,12 @@ class RunQueueExecuteTasks(RunQueueExecute):
except TypeError:
covered_remove = bb.utils.better_eval(call2, locs)
def removecoveredtask(task):
for task in covered_remove:
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
taskname = self.rqdata.runq_task[task] + '_setscene'
bb.build.del_stamp(taskname, self.rqdata.dataCache, fn)
self.rq.scenequeue_covered.remove(task)
toremove = covered_remove
for task in toremove:
logger.debug(1, 'Not skipping task %s due to setsceneverify', task)
while toremove:
covered_remove = []
for task in toremove:
removecoveredtask(task)
for deptask in self.rqdata.runq_depends[task]:
if deptask not in self.rq.scenequeue_covered:
continue
if deptask in toremove or deptask in covered_remove or deptask in initial_covered:
continue
logger.debug(1, 'Task %s depends on task %s so not skipping' % (task, deptask))
covered_remove.append(deptask)
toremove = covered_remove
self.rq.scenequeue_covered.remove(task)
logger.debug(1, 'Full skip list %s', self.rq.scenequeue_covered)
@@ -1467,10 +1300,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
schedulers.add(getattr(module, name))
return schedulers
def setbuildable(self, task):
self.runq_buildable[task] = 1
self.sched.newbuilable(task)
def task_completeoutright(self, task):
"""
Mark a task as completed
@@ -1488,7 +1317,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
if self.runq_complete[dep] != 1:
alldeps = 0
if alldeps == 1:
self.setbuildable(revdep)
self.runq_buildable[revdep] = 1
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[revdep]]
taskname = self.rqdata.runq_task[revdep]
logger.debug(1, "Marking task %s (%s, %s) as buildable", revdep, fn, taskname)
@@ -1512,7 +1341,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
def task_skip(self, task, reason):
self.runq_running[task] = 1
self.setbuildable(task)
self.runq_buildable[task] = 1
bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
self.task_completeoutright(task)
self.stats.taskCompleted()
@@ -1554,28 +1383,24 @@ class RunQueueExecuteTasks(RunQueueExecute):
bb.event.fire(startevent, self.cfgData)
self.runq_running[task] = 1
self.stats.taskActive()
if not self.cooker.configuration.dry_run:
bb.build.make_stamp(taskname, self.rqdata.dataCache, fn)
bb.build.make_stamp(taskname, self.rqdata.dataCache, fn)
self.task_complete(task)
return True
else:
startevent = runQueueTaskStarted(task, self.stats, self.rq)
bb.event.fire(startevent, self.cfgData)
taskdepdata = self.build_taskdepdata(task)
taskdep = self.rqdata.dataCache.task_deps[fn]
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
if not self.rq.fakeworker:
self.rq.start_fakeworker(self)
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + "</runtask>")
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
self.rq.fakeworker.stdin.flush()
else:
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + "</runtask>")
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
self.rq.worker.stdin.flush()
self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
self.build_stamps2.append(self.build_stamps[task])
self.runq_running[task] = 1
self.stats.taskActive()
if self.stats.active < self.number_tasks:
@@ -1601,26 +1426,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
return True
def build_taskdepdata(self, task):
taskdepdata = {}
next = self.rqdata.runq_depends[task]
next.add(task)
while next:
additional = []
for revdep in next:
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[revdep]]
pn = self.rqdata.dataCache.pkg_fn[fn]
taskname = self.rqdata.runq_task[revdep]
deps = self.rqdata.runq_depends[revdep]
taskdepdata[revdep] = [pn, taskname, fn, deps]
for revdep2 in deps:
if revdep2 not in taskdepdata:
additional.append(revdep2)
next = additional
#bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
return taskdepdata
class RunQueueExecuteScenequeue(RunQueueExecute):
def __init__(self, rq):
RunQueueExecute.__init__(self, rq)
@@ -1640,7 +1445,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
sq_revdeps = []
sq_revdeps_new = []
sq_revdeps_squash = []
self.sq_harddeps = {}
self.sq_harddeps = []
# We need to construct a dependency graph for the setscene functions. Intermediate
# dependencies between the setscene tasks only complicate the code. This code
@@ -1690,7 +1495,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
process_endpoints(endpoints)
# Build a list of setscene tasks which are "unskippable"
# Build a list of setscene tasks which as "unskippable"
# These are direct endpoints referenced by the build
endpoints2 = {}
sq_revdeps2 = []
@@ -1751,23 +1556,15 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
dep = self.rqdata.taskData.fn_index[depdata]
taskid = self.rqdata.get_task_id(self.rqdata.taskData.getfn_id(dep), idependtask.replace("_setscene", ""))
if taskid is None:
bb.msg.fatal("RunQueue", "Task %s_setscene depends upon non-existent task %s:%s" % (self.rqdata.get_user_idstring(task), dep, idependtask))
if not self.rqdata.runq_setscene.index(taskid) in self.sq_harddeps:
self.sq_harddeps[self.rqdata.runq_setscene.index(taskid)] = set()
self.sq_harddeps[self.rqdata.runq_setscene.index(taskid)].add(self.rqdata.runq_setscene.index(task))
bb.msg.fatal("RunQueue", "Task %s:%s depends upon non-existent task %s:%s" % (self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realid]], self.rqdata.taskData.tasks_name[realid], dep, idependtask))
self.sq_harddeps.append(self.rqdata.runq_setscene.index(taskid))
sq_revdeps_squash[self.rqdata.runq_setscene.index(task)].add(self.rqdata.runq_setscene.index(taskid))
# Have to zero this to avoid circular dependencies
sq_revdeps_squash[self.rqdata.runq_setscene.index(taskid)] = set()
for task in self.sq_harddeps:
for dep in self.sq_harddeps[task]:
sq_revdeps_squash[dep].add(task)
#for task in xrange(len(sq_revdeps_squash)):
# realtask = self.rqdata.runq_setscene[task]
# bb.warn("Task %s: %s_setscene is %s " % (task, self.rqdata.get_user_idstring(realtask) , sq_revdeps_squash[task]))
# print "Task %s: %s.%s is %s " % (task, self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[self.rqdata.runq_setscene[task]]], self.rqdata.runq_task[self.rqdata.runq_setscene[task]] + "_setscene", sq_revdeps_squash[task])
self.sq_deps = []
self.sq_revdeps = sq_revdeps_squash
@@ -1783,7 +1580,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
if len(self.sq_revdeps[task]) == 0:
self.runq_buildable[task] = 1
self.outrightfail = []
if self.rq.hashvalidate:
sq_hash = []
sq_hashfn = []
@@ -1834,7 +1630,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
realtask = self.rqdata.runq_setscene[task]
logger.debug(2, 'No package found, so skipping setscene task %s',
self.rqdata.get_user_idstring(realtask))
self.outrightfail.append(task)
self.task_failoutright(task)
logger.info('Executing SetScene Tasks')
@@ -1842,14 +1638,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
def scenequeue_updatecounters(self, task, fail = False):
for dep in self.sq_deps[task]:
if fail and task in self.sq_harddeps and dep in self.sq_harddeps[task]:
realtask = self.rqdata.runq_setscene[task]
realdep = self.rqdata.runq_setscene[dep]
logger.debug(2, "%s was unavailable and is a hard dependency of %s so skipping" % (self.rqdata.get_user_idstring(realtask), self.rqdata.get_user_idstring(realdep)))
self.scenequeue_updatecounters(dep, fail)
continue
if task not in self.sq_revdeps2[dep]:
# May already have been removed by the fail case above
if fail and task in self.sq_harddeps:
continue
self.sq_revdeps2[dep].remove(task)
if len(self.sq_revdeps2[dep]) == 0:
@@ -1911,20 +1700,9 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
if nexttask in self.unskippable:
logger.debug(2, "Setscene task %s is unskippable" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))
if nexttask not in self.unskippable and len(self.sq_revdeps[nexttask]) > 0 and self.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sq_revdeps[nexttask], True):
realtask = self.rqdata.runq_setscene[nexttask]
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realtask]]
foundtarget = False
for target in self.rqdata.target_pairs:
if target[0] == fn and target[1] == self.rqdata.runq_task[realtask]:
foundtarget = True
break
if not foundtarget:
logger.debug(2, "Skipping setscene for task %s" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))
self.task_skip(nexttask)
self.scenequeue_notneeded.add(nexttask)
return True
if nexttask in self.outrightfail:
self.task_failoutright(nexttask)
logger.debug(2, "Skipping setscene for task %s" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))
self.task_skip(nexttask)
self.scenequeue_notneeded.add(nexttask)
return True
task = nexttask
break
@@ -1958,10 +1736,10 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
if not self.rq.fakeworker:
self.rq.start_fakeworker(self)
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + "</runtask>")
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
self.rq.fakeworker.stdin.flush()
else:
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + "</runtask>")
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
self.rq.worker.stdin.flush()
self.runq_running[task] = 1
@@ -1973,12 +1751,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.rq.read_workers()
return self.rq.active_fds()
#for task in xrange(self.stats.total):
# if self.runq_running[task] != 1:
# buildable = self.runq_buildable[task]
# revdeps = self.sq_revdeps[task]
# bb.warn("Found we didn't run %s %s %s %s" % (task, buildable, str(revdeps), self.rqdata.get_user_idstring(self.rqdata.runq_setscene[task])))
# Convert scenequeue_covered task numbers into full taskgraph ids
oldcovered = self.scenequeue_covered
self.rq.scenequeue_covered = set()
@@ -1991,10 +1763,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
logger.debug(1, 'We can skip tasks %s', sorted(self.rq.scenequeue_covered))
self.rq.state = runQueueRunInit
completeevent = sceneQueueComplete(self.stats, self.rq)
bb.event.fire(completeevent, self.cfgData)
return True
def runqueue_process_waitpid(self, task, status):
@@ -2043,11 +1811,11 @@ class sceneQueueEvent(runQueueEvent):
self.taskstring = rq.rqdata.get_user_idstring(realtask, "_setscene")
self.taskname = rq.rqdata.get_task_name(realtask) + "_setscene"
self.taskfile = rq.rqdata.get_task_file(realtask)
self.taskhash = rq.rqdata.get_task_hash(realtask)
self.taskhash = rq.rqdata.get_task_hash(task)
class runQueueTaskStarted(runQueueEvent):
"""
Event notifying a task was started
Event notifing a task was started
"""
def __init__(self, task, stats, rq, noexec=False):
runQueueEvent.__init__(self, task, stats, rq)
@@ -2055,7 +1823,7 @@ class runQueueTaskStarted(runQueueEvent):
class sceneQueueTaskStarted(sceneQueueEvent):
"""
Event notifying a setscene task was started
Event notifing a setscene task was started
"""
def __init__(self, task, stats, rq, noexec=False):
sceneQueueEvent.__init__(self, task, stats, rq)
@@ -2063,7 +1831,7 @@ class sceneQueueTaskStarted(sceneQueueEvent):
class runQueueTaskFailed(runQueueEvent):
"""
Event notifying a task failed
Event notifing a task failed
"""
def __init__(self, task, stats, exitcode, rq):
runQueueEvent.__init__(self, task, stats, rq)
@@ -2071,33 +1839,25 @@ class runQueueTaskFailed(runQueueEvent):
class sceneQueueTaskFailed(sceneQueueEvent):
"""
Event notifying a setscene task failed
Event notifing a setscene task failed
"""
def __init__(self, task, stats, exitcode, rq):
sceneQueueEvent.__init__(self, task, stats, rq)
self.exitcode = exitcode
class sceneQueueComplete(sceneQueueEvent):
"""
Event when all the sceneQueue tasks are complete
"""
def __init__(self, stats, rq):
self.stats = stats.copy()
bb.event.Event.__init__(self)
class runQueueTaskCompleted(runQueueEvent):
"""
Event notifying a task completed
Event notifing a task completed
"""
class sceneQueueTaskCompleted(sceneQueueEvent):
"""
Event notifying a setscene task completed
Event notifing a setscene task completed
"""
class runQueueTaskSkipped(runQueueEvent):
"""
Event notifying a task was skipped
Event notifing a task was skipped
"""
def __init__(self, task, stats, rq, reason):
runQueueEvent.__init__(self, task, stats, rq)
@@ -2107,7 +1867,7 @@ class runQueuePipe():
"""
Abstraction for a pipe between a worker thread and the server
"""
def __init__(self, pipein, pipeout, d, rq, rqexec):
def __init__(self, pipein, pipeout, d, rq):
self.input = pipein
if pipeout:
pipeout.close()
@@ -2115,25 +1875,11 @@ class runQueuePipe():
self.queue = ""
self.d = d
self.rq = rq
self.rqexec = rqexec
def setrunqueueexec(self, rqexec):
self.rqexec = rqexec
def setrunqueueexec(self, rq):
self.rq = rq
def read(self):
for w in [self.rq.worker, self.rq.fakeworker]:
if not w:
continue
w.poll()
if w.returncode is not None and not self.rq.teardown:
name = None
if self.rq.worker and w.pid == self.rq.worker.pid:
name = "Worker"
elif self.rq.fakeworker and w.pid == self.rq.fakeworker.pid:
name = "Fakeroot"
bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, w.pid, str(w.returncode)))
self.rq.finish_runqueue(True)
start = len(self.queue)
try:
self.queue = self.queue + self.input.read(102400)
@@ -2146,21 +1892,15 @@ class runQueuePipe():
found = False
index = self.queue.find("</event>")
while index != -1 and self.queue.startswith("<event>"):
try:
event = pickle.loads(self.queue[7:index])
except ValueError as e:
bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
event = pickle.loads(self.queue[7:index])
bb.event.fire_from_worker(event, self.d)
found = True
self.queue = self.queue[index+8:]
index = self.queue.find("</event>")
index = self.queue.find("</exitcode>")
while index != -1 and self.queue.startswith("<exitcode>"):
try:
task, status = pickle.loads(self.queue[10:index])
except ValueError as e:
bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
self.rqexec.runqueue_process_waitpid(task, status)
task, status = pickle.loads(self.queue[10:index])
self.rq.runqueue_process_waitpid(task, status)
found = True
self.queue = self.queue[index+11:]
index = self.queue.find("</exitcode>")

View File

@@ -38,18 +38,14 @@ from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
logger = logging.getLogger('BitBake')
class ServerCommunicator():
def __init__(self, connection, event_handle, server):
def __init__(self, connection, event_handle):
self.connection = connection
self.event_handle = event_handle
self.server = server
def runCommand(self, command):
# @todo try/except
self.connection.send(command)
if not self.server.is_alive():
raise SystemExit
while True:
# don't let the user ctrl-c while we're waiting for a response
try:
@@ -84,14 +80,15 @@ class ProcessServer(Process, BaseImplServer):
def __init__(self, command_channel, event_queue, featurelist):
BaseImplServer.__init__(self)
Process.__init__(self)
Process.__init__(self, args=(featurelist))
self.command_channel = command_channel
self.event_queue = event_queue
self.event = EventAdapter(event_queue)
self.featurelist = featurelist
self.quit = False
self.quitin, self.quitout = Pipe()
self.keep_running = Event()
self.keep_running.set()
self.event_handle = multiprocessing.Value("i")
def run(self):
@@ -99,23 +96,26 @@ class ProcessServer(Process, BaseImplServer):
self.event_queue.put(event)
self.event_handle.value = bb.event.register_UIHhandler(self)
# process any feature changes based on what UI requested
original_featureset = list(self.cooker.featureset)
while len(self.featurelist)> 0:
self.cooker.featureset.setFeature(self.featurelist.pop())
if (original_featureset != list(self.cooker.featureset)):
self.cooker.reset()
bb.cooker.server_main(self.cooker, self.main)
def main(self):
# Ignore SIGINT within the server, as all SIGINT handling is done by
# the UI and communicated to us
self.quitin.close()
signal.signal(signal.SIGINT, signal.SIG_IGN)
while not self.quit:
while self.keep_running.is_set():
try:
if self.command_channel.poll():
command = self.command_channel.recv()
self.runCommand(command)
if self.quitout.poll():
self.quitout.recv()
self.quit = True
self.idle_commands(.1, [self.event_queue._reader, self.command_channel, self.quitout])
self.idle_commands(.1, [self.event_queue._reader, self.command_channel])
except Exception:
logger.exception('Running command %s', command)
@@ -123,6 +123,7 @@ class ProcessServer(Process, BaseImplServer):
bb.event.unregister_UIHhandler(self.event_handle.value)
self.command_channel.close()
self.cooker.shutdown(True)
self.idle_commands(.1)
def idle_commands(self, delay, fds = []):
nextsleep = delay
@@ -132,12 +133,8 @@ class ProcessServer(Process, BaseImplServer):
retval = function(self, data, False)
if retval is False:
del self._idlefuns[function]
nextsleep = None
elif retval is True:
nextsleep = None
elif isinstance(retval, float):
if (retval < nextsleep):
nextsleep = retval
elif nextsleep is None:
continue
else:
@@ -146,8 +143,6 @@ class ProcessServer(Process, BaseImplServer):
raise
except Exception:
logger.exception('Running idle function')
del self._idlefuns[function]
self.quit = True
if nextsleep is not None:
select.select(fds,[],[],nextsleep)
@@ -159,21 +154,16 @@ class ProcessServer(Process, BaseImplServer):
self.command_channel.send(self.cooker.command.runCommand(command))
def stop(self):
self.quitin.send("quit")
self.quitin.close()
self.keep_running.clear()
class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
def __init__(self, serverImpl, ui_channel, event_queue):
self.procserver = serverImpl
self.ui_channel = ui_channel
self.event_queue = event_queue
self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle, self.procserver)
self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle)
self.events = self.event_queue
def sigterm_terminate(self):
bb.error("UI received SIGTERM")
self.terminate()
def terminate(self):
def flushevents():
while True:
@@ -193,33 +183,17 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
self.ui_channel.close()
self.event_queue.close()
self.event_queue.setexit()
# Wrap Queue to provide API which isn't server implementation specific
class ProcessEventQueue(multiprocessing.queues.Queue):
def __init__(self, maxsize):
multiprocessing.queues.Queue.__init__(self, maxsize)
self.exit = False
def setexit(self):
self.exit = True
def waitEvent(self, timeout):
if self.exit:
sys.exit(1)
try:
if not self.server.is_alive():
self.setexit()
return None
return self.get(True, timeout)
except Empty:
return None
def getEvent(self):
try:
if not self.server.is_alive():
self.setexit()
return None
return self.get(False)
except Empty:
return None
@@ -233,20 +207,17 @@ class BitBakeServer(BitBakeBaseServer):
#
self.ui_channel, self.server_channel = Pipe()
self.event_queue = ProcessEventQueue(0)
self.serverImpl = ProcessServer(self.server_channel, self.event_queue, None)
self.event_queue.server = self.serverImpl
manager = Manager()
self.featurelist = manager.list()
self.serverImpl = ProcessServer(self.server_channel, self.event_queue, self.featurelist)
def detach(self):
self.serverImpl.start()
return
def establishConnection(self, featureset):
for f in featureset:
self.featurelist.append(f)
self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
_, error = self.connection.connection.runCommand(["setFeatures", featureset])
if error:
logger.error("Unable to set the cooker to the correct featureset: %s" % error)
raise BaseException(error)
signal.signal(signal.SIGTERM, lambda i, s: self.connection.sigterm_terminate())
signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate())
return self.connection

View File

@@ -80,7 +80,7 @@ class BBTransport(xmlrpclib.Transport):
def _create_server(host, port, timeout = 60):
t = BBTransport(timeout)
s = xmlrpclib.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True)
s = xmlrpclib.Server("http://%s:%d/" % (host, port), transport=t, allow_none=True)
return s, t
class BitBakeServerCommands():
@@ -89,7 +89,7 @@ class BitBakeServerCommands():
self.server = server
self.has_client = False
def registerEventHandler(self, host, port):
def registerEventHandler(self, host, port, featureset = []):
"""
Register a remote UI Event Handler
"""
@@ -99,6 +99,13 @@ class BitBakeServerCommands():
if (self.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]):
return None
original_featureset = list(self.cooker.featureset)
for f in featureset:
self.cooker.featureset.setFeature(f)
if (original_featureset != list(self.cooker.featureset)):
self.cooker.reset()
self.event_handle = bb.event.register_UIHhandler(s)
return self.event_handle
@@ -191,11 +198,6 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
Constructor
"""
BaseImplServer.__init__(self)
if (interface[1] == 0): # anonymous port, not getting reused
self.single_use = True
# Use auto port configuration
if (interface[1] == -1):
interface = (interface[0], 0)
SimpleXMLRPCServer.__init__(self, interface,
requestHandler=BitBakeXMLRPCRequestHandler,
logRequests=False, allow_none=True)
@@ -206,6 +208,8 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
self.autoregister_all_functions(self.commands, "")
self.interface = interface
self.single_use = False
if (interface[1] == 0): # anonymous port, not getting reused
self.single_use = True
def addcooker(self, cooker):
BaseImplServer.addcooker(self, cooker)
@@ -241,9 +245,6 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
del self._idlefuns[function]
elif retval is True:
nextsleep = 0
elif isinstance(retval, float):
if (retval < nextsleep):
nextsleep = retval
else:
fds = fds + retval
except SystemExit:
@@ -256,13 +257,9 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
socktimeout = self.socket.gettimeout() or nextsleep
socktimeout = min(socktimeout, nextsleep)
# Mirror what BaseServer handle_request would do
try:
fd_sets = select.select(fds, [], [], socktimeout)
if fd_sets[0] and self in fd_sets[0]:
self._handle_request_noblock()
except IOError:
# we ignore interrupted calls
pass
fd_sets = select.select(fds, [], [], socktimeout)
if fd_sets[0] and self in fd_sets[0]:
self._handle_request_noblock()
# Tell idle functions we're exiting
for function, data in self._idlefuns.items():
@@ -284,27 +281,18 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
self.observer_only = observer_only
self.featureset = featureset
def connect(self, token = None):
if token is None:
if self.observer_only:
token = "observer"
else:
token = self.connection.addClient()
def connect(self):
if not self.observer_only:
token = self.connection.addClient()
else:
token = "observer"
if token is None:
return None
self.transport.set_connection_token(token)
self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo)
self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo, self.featureset)
for event in bb.event.ui_queue:
self.events.queue_event(event)
_, error = self.connection.runCommand(["setFeatures", self.featureset])
if error:
# no need to log it here, the error shall be sent to the client
raise BaseException(error)
return self
def removeClient(self):
@@ -342,9 +330,7 @@ class BitBakeServer(BitBakeBaseServer):
class BitBakeXMLRPCClient(BitBakeBaseServer):
def __init__(self, observer_only = False, token = None):
self.token = token
def __init__(self, observer_only = False):
self.observer_only = observer_only
# if we need extra caches, just tell the server to load them all
pass
@@ -357,10 +343,8 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
try:
[host, port] = self.remote.split(":")
port = int(port)
except Exception as e:
bb.warn("Failed to read remote definition (%s)" % str(e))
raise e
except:
return None
# We need our IP for the server connection. We get the IP
# by trying to connect with the server
try:
@@ -368,16 +352,14 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
s.connect((host, port))
ip = s.getsockname()[0]
s.close()
except Exception as e:
bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
raise e
except:
return None
try:
self.serverImpl = XMLRPCProxyServer(host, port)
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only, featureset)
return self.connection.connect(self.token)
return self.connection.connect()
except Exception as e:
bb.warn("Could not connect to server at %s:%s (%s)" % (host, port, str(e)))
raise e
bb.fatal("Could not connect to server at %s:%s (%s)" % (host, port, str(e)))
def endSession(self):
self.connection.removeClient()

View File

@@ -34,9 +34,7 @@ class SignatureGenerator(object):
name = "noop"
def __init__(self, data):
self.taskhash = {}
self.runtaskdeps = {}
self.file_checksum_values = {}
return
def finalise(self, fn, d, varient):
return
@@ -44,7 +42,7 @@ class SignatureGenerator(object):
def get_taskhash(self, fn, task, deps, dataCache):
return "0"
def set_taskdata(self, hashes, deps, checksum):
def set_taskdata(self, hashes, deps):
return
def stampfile(self, stampbase, file_name, taskname, extrainfo):
@@ -59,15 +57,6 @@ class SignatureGenerator(object):
def invalidate_task(self, task, d, fn):
bb.build.del_stamp(task, d, fn)
def dump_sigs(self, dataCache, options):
return
def get_taskdata(self):
return (self.runtaskdeps, self.taskhash, self.file_checksum_values)
def set_taskdata(self, data):
self.runtaskdeps, self.taskhash, self.file_checksum_values = data
class SignatureGeneratorBasic(SignatureGenerator):
"""
@@ -192,19 +181,22 @@ class SignatureGeneratorBasic(SignatureGenerator):
checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
for (f,cs) in checksums:
self.file_checksum_values[k][f] = cs
if cs:
data = data + cs
data = data + cs
taint = self.read_taint(fn, task, dataCache.stamp[fn])
if taint:
data = data + taint
logger.warn("%s is tainted from a forced run" % k)
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, checksums):
self.runtaskdeps = deps
self.taskhash = hashes
self.file_checksum_values = checksums
def dump_sigtask(self, fn, task, stampbase, runtime):
k = fn + "." + task
if runtime == "customfile":
@@ -232,7 +224,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
if runtime and k in self.taskhash:
data['runtaskdeps'] = self.runtaskdeps[k]
data['file_checksum_values'] = [(os.path.basename(f), cs) for f,cs in self.file_checksum_values[k].items()]
data['file_checksum_values'] = self.file_checksum_values[k]
data['runtaskhashes'] = {}
for dep in data['runtaskdeps']:
data['runtaskhashes'][dep] = self.taskhash[dep]
@@ -246,6 +238,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
with os.fdopen(fd, "wb") as stream:
p = pickle.dump(data, stream, -1)
stream.flush()
os.fsync(fd)
os.chmod(tmpfile, 0664)
os.rename(tmpfile, sigfile)
except (OSError, IOError) as err:
@@ -255,7 +248,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
pass
raise err
def dump_sigs(self, dataCache, options):
def dump_sigs(self, dataCache):
for fn in self.taskdeps:
for task in self.taskdeps[fn]:
k = fn + "." + task
@@ -297,9 +290,10 @@ def dump_this_task(outfile, d):
bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile")
def clean_basepath(a):
b = a.rsplit("/", 2)[1] + a.rsplit("/", 2)[2]
if a.startswith("virtual:"):
b = b + ":" + a.rsplit(":", 1)[0]
b = a.rsplit(":", 1)[0] + ":" + a.rsplit("/", 1)[1]
else:
b = a.rsplit("/", 1)[1]
return b
def clean_basepaths(a):
@@ -324,41 +318,8 @@ def compare_sigfiles(a, b, recursecb = None):
for i in common:
if a[i] != b[i] and i not in whitelist:
changed.add(i)
added = sb - sa
removed = sa - sb
return changed, added, removed
def file_checksums_diff(a, b):
from collections import Counter
# Handle old siginfo format
if isinstance(a, dict):
a = [(os.path.basename(f), cs) for f, cs in a.items()]
if isinstance(b, dict):
b = [(os.path.basename(f), cs) for f, cs in b.items()]
# Compare lists, ensuring we can handle duplicate filenames if they exist
removedcount = Counter(a)
removedcount.subtract(b)
addedcount = Counter(b)
addedcount.subtract(a)
added = []
for x in b:
if addedcount[x] > 0:
addedcount[x] -= 1
added.append(x)
removed = []
changed = []
for x in a:
if removedcount[x] > 0:
removedcount[x] -= 1
for y in added:
if y[0] == x[0]:
changed.append((x[0], x[1], y[1]))
added.remove(y)
break
else:
removed.append(x)
added = [x[0] for x in added]
removed = [x[0] for x in removed]
added = sa - sb
removed = sb - sa
return changed, added, removed
if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
@@ -396,10 +357,10 @@ def compare_sigfiles(a, b, recursecb = None):
for dep in changed:
output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
changed, added, removed = dict_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
if changed:
for f, old, new in changed:
output.append("Checksum for file %s changed from %s to %s" % (f, old, new))
for f in changed:
output.append("Checksum for file %s changed from %s to %s" % (f, a_data['file_checksum_values'][f], b_data['file_checksum_values'][f]))
if added:
for f in added:
output.append("Dependency on checksum of file %s was added" % (f))
@@ -417,30 +378,28 @@ def compare_sigfiles(a, b, recursecb = None):
bdep_found = False
if removed:
for bdep in removed:
if b[dep] == a[bdep]:
if a[dep] == b[bdep]:
#output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
bdep_found = True
if not bdep_found:
output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), b[dep]))
output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), a[dep]))
if removed:
for dep in removed:
adep_found = False
if added:
for adep in added:
if b[adep] == a[dep]:
if a[adep] == b[dep]:
#output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
adep_found = True
if not adep_found:
output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), a[dep]))
output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), b[dep]))
if changed:
for dep in changed:
output.append("Hash for dependent task %s changed from %s to %s" % (clean_basepath(dep), a[dep], b[dep]))
if callable(recursecb):
# If a dependent hash changed, might as well print the line above and then defer to the changes in
# that hash since in all likelyhood, they're the same changes this task also saw.
recout = recursecb(dep, a[dep], b[dep])
if recout:
output = [output[-1]] + recout
output.extend(recout)
a_taint = a_data.get('taint', None)
b_taint = b_data.get('taint', None)

View File

@@ -207,29 +207,23 @@ class TaskData:
if not fnid in self.depids:
dependids = {}
for depend in dataCache.deps[fn]:
logger.debug(2, "Added dependency %s for %s", depend, fn)
dependids[self.getbuild_id(depend)] = None
self.depids[fnid] = dependids.keys()
logger.debug(2, "Added dependencies %s for %s", str(dataCache.deps[fn]), fn)
# Work out runtime dependencies
if not fnid in self.rdepids:
rdependids = {}
rdepends = dataCache.rundeps[fn]
rrecs = dataCache.runrecs[fn]
rdependlist = []
rreclist = []
for package in rdepends:
for rdepend in rdepends[package]:
rdependlist.append(rdepend)
logger.debug(2, "Added runtime dependency %s for %s", rdepend, fn)
rdependids[self.getrun_id(rdepend)] = None
for package in rrecs:
for rdepend in rrecs[package]:
rreclist.append(rdepend)
logger.debug(2, "Added runtime recommendation %s for %s", rdepend, fn)
rdependids[self.getrun_id(rdepend)] = None
if rdependlist:
logger.debug(2, "Added runtime dependencies %s for %s", str(rdependlist), fn)
if rreclist:
logger.debug(2, "Added runtime recommendations %s for %s", str(rreclist), fn)
self.rdepids[fnid] = rdependids.keys()
for dep in self.depids[fnid]:

View File

@@ -236,8 +236,7 @@ be. These unit tests are testing snippets."""
self.d.setVar("do_something", "echo 'hi mom! ${FOO}'")
self.d.setVarFlag("do_something", etype, True)
self.parseExpression("bb.build.exec_func('do_something', d)")
self.assertReferences(set([]))
self.assertExecs(set(["do_something"]))
self.assertReferences(set(["do_something"]))
def test_function_reference(self):
self.context["testfunc"] = lambda msg: bb.msg.note(1, None, msg)

View File

@@ -23,7 +23,6 @@
import unittest
import bb
import bb.data
import bb.parse
class DataExpansions(unittest.TestCase):
def setUp(self):
@@ -213,66 +212,6 @@ class TestConcat(unittest.TestCase):
self.d.appendVar("TEST", ":${BAR}")
self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
class TestConcatOverride(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d.setVar("FOO", "foo")
self.d.setVar("VAL", "val")
self.d.setVar("BAR", "bar")
def test_prepend(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_prepend", "${FOO}:")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "foo:val")
def test_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_append", ":${BAR}")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "val:bar")
def test_multiple_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_prepend", "${FOO}:")
self.d.setVar("TEST_append", ":val2")
self.d.setVar("TEST_append", ":${BAR}")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
def test_remove(self):
self.d.setVar("TEST", "${VAL} ${BAR}")
self.d.setVar("TEST_remove", "val")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "bar")
def test_doubleref_remove(self):
self.d.setVar("TEST", "${VAL} ${BAR}")
self.d.setVar("TEST_remove", "val")
self.d.setVar("TEST_TEST", "${TEST} ${TEST}")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST_TEST", True), "bar bar")
def test_empty_remove(self):
self.d.setVar("TEST", "")
self.d.setVar("TEST_remove", "val")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "")
def test_remove_expansion(self):
self.d.setVar("BAR", "Z")
self.d.setVar("TEST", "${BAR}/X Y")
self.d.setVar("TEST_remove", "${BAR}/X")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "Y")
def test_remove_expansion_items(self):
self.d.setVar("TEST", "A B C D")
self.d.setVar("BAR", "B D")
self.d.setVar("TEST_remove", "${BAR}")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "A C")
class TestOverrides(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
@@ -313,39 +252,3 @@ class TestFlags(unittest.TestCase):
self.assertEqual(self.d.getVarFlag("foo", "flag2"), None)
class Contains(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d.setVar("SOMEFLAG", "a b c")
def test_contains(self):
self.assertTrue(bb.utils.contains("SOMEFLAG", "a", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "b", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "c", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "a b", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "b c", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "c a", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "a b c", True, False, self.d))
self.assertTrue(bb.utils.contains("SOMEFLAG", "c b a", True, False, self.d))
self.assertFalse(bb.utils.contains("SOMEFLAG", "x", True, False, self.d))
self.assertFalse(bb.utils.contains("SOMEFLAG", "a x", True, False, self.d))
self.assertFalse(bb.utils.contains("SOMEFLAG", "x c b", True, False, self.d))
self.assertFalse(bb.utils.contains("SOMEFLAG", "x c b a", True, False, self.d))
def test_contains_any(self):
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "b", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "c", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a b", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "b c", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "c a", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a x", True, False, self.d))
self.assertTrue(bb.utils.contains_any("SOMEFLAG", "x c", True, False, self.d))
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x", True, False, self.d))
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x y z", True, False, self.d))

View File

@@ -39,7 +39,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': False
},
"http://www.google.com/index.html;param1=value1" : {
@@ -55,41 +54,6 @@ class URITest(unittest.TestCase):
'params': {
'param1': 'value1'
},
'query': {},
'relative': False
},
"http://www.example.org/index.html?param1=value1" : {
'uri': 'http://www.example.org/index.html?param1=value1',
'scheme': 'http',
'hostname': 'www.example.org',
'port': None,
'hostport': 'www.example.org',
'path': '/index.html',
'userinfo': '',
'username': '',
'password': '',
'params': {},
'query': {
'param1': 'value1'
},
'relative': False
},
"http://www.example.org/index.html?qparam1=qvalue1;param2=value2" : {
'uri': 'http://www.example.org/index.html?qparam1=qvalue1;param2=value2',
'scheme': 'http',
'hostname': 'www.example.org',
'port': None,
'hostport': 'www.example.org',
'path': '/index.html',
'userinfo': '',
'username': '',
'password': '',
'params': {
'param2': 'value2'
},
'query': {
'qparam1': 'qvalue1'
},
'relative': False
},
"http://www.example.com:8080/index.html" : {
@@ -103,7 +67,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': False
},
"cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : {
@@ -119,7 +82,6 @@ class URITest(unittest.TestCase):
'params': {
'module': 'familiar/dist/ipkg'
},
'query': {},
'relative': False
},
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg": {
@@ -136,7 +98,6 @@ class URITest(unittest.TestCase):
'tag': 'V0-99-81',
'module': 'familiar/dist/ipkg'
},
'query': {},
'relative': False
},
"file://example.diff": { # NOTE: Not RFC compliant!
@@ -150,7 +111,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': True
},
"file:example.diff": { # NOTE: RFC compliant version of the former
@@ -165,7 +125,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': True
},
"file:///tmp/example.diff": {
@@ -180,7 +139,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': False
},
"git:///path/example.git": {
@@ -195,7 +153,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': False
},
"git:path/example.git": {
@@ -210,7 +167,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': True
},
"git://example.net/path/example.git": {
@@ -225,7 +181,6 @@ class URITest(unittest.TestCase):
'username': '',
'password': '',
'params': {},
'query': {},
'relative': False
}
}
@@ -274,20 +229,6 @@ class URITest(unittest.TestCase):
self.assertEqual(uri.username, test['username'])
self.assertEqual(uri.password, test['password'])
# make sure changing the values doesn't do anything unexpected
uri.username = 'changeme'
self.assertEqual(uri.username, 'changeme')
self.assertEqual(uri.password, test['password'])
uri.password = 'insecure'
self.assertEqual(uri.username, 'changeme')
self.assertEqual(uri.password, 'insecure')
# reset back after our trickery
uri.userinfo = test['userinfo']
self.assertEqual(uri.userinfo, test['userinfo'])
self.assertEqual(uri.username, test['username'])
self.assertEqual(uri.password, test['password'])
uri.hostname = test['hostname']
self.assertEqual(uri.hostname, test['hostname'])
self.assertEqual(uri.hostport, test['hostname'])
@@ -302,8 +243,7 @@ class URITest(unittest.TestCase):
uri.params = test['params']
self.assertEqual(uri.params, test['params'])
uri.query = test['query']
self.assertEqual(uri.query, test['query'])
self.assertEqual(str(uri)+str(uri.relative), str(test['uri'])+str(test['relative']))
self.assertEqual(str(uri), test['uri'])
@@ -390,68 +330,6 @@ class MirrorUriTest(FetcherTest):
uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
self.assertEqual(uris, ['file:///someotherpath/downloads/bitbake-1.0.tar.gz'])
class FetcherLocalTest(FetcherTest):
def setUp(self):
def touch(fn):
with file(fn, 'a'):
os.utime(fn, None)
super(FetcherLocalTest, self).setUp()
self.localsrcdir = os.path.join(self.tempdir, 'localsrc')
os.makedirs(self.localsrcdir)
touch(os.path.join(self.localsrcdir, 'a'))
touch(os.path.join(self.localsrcdir, 'b'))
os.makedirs(os.path.join(self.localsrcdir, 'dir'))
touch(os.path.join(self.localsrcdir, 'dir', 'c'))
touch(os.path.join(self.localsrcdir, 'dir', 'd'))
os.makedirs(os.path.join(self.localsrcdir, 'dir', 'subdir'))
touch(os.path.join(self.localsrcdir, 'dir', 'subdir', 'e'))
self.d.setVar("FILESPATH", self.localsrcdir)
def fetchUnpack(self, uris):
fetcher = bb.fetch.Fetch(uris, self.d)
fetcher.download()
fetcher.unpack(self.unpackdir)
flst = []
for root, dirs, files in os.walk(self.unpackdir):
for f in files:
flst.append(os.path.relpath(os.path.join(root, f), self.unpackdir))
flst.sort()
return flst
def test_local(self):
tree = self.fetchUnpack(['file://a', 'file://dir/c'])
self.assertEqual(tree, ['a', 'dir/c'])
def test_local_wildcard(self):
tree = self.fetchUnpack(['file://a', 'file://dir/*'])
# FIXME: this is broken - it should return ['a', 'dir/c', 'dir/d', 'dir/subdir/e']
# see https://bugzilla.yoctoproject.org/show_bug.cgi?id=6128
self.assertEqual(tree, ['a', 'b', 'dir/c', 'dir/d', 'dir/subdir/e'])
def test_local_dir(self):
tree = self.fetchUnpack(['file://a', 'file://dir'])
self.assertEqual(tree, ['a', 'dir/c', 'dir/d', 'dir/subdir/e'])
def test_local_subdir(self):
tree = self.fetchUnpack(['file://dir/subdir'])
# FIXME: this is broken - it should return ['dir/subdir/e']
# see https://bugzilla.yoctoproject.org/show_bug.cgi?id=6129
self.assertEqual(tree, ['subdir/e'])
def test_local_subdir_file(self):
tree = self.fetchUnpack(['file://dir/subdir/e'])
self.assertEqual(tree, ['dir/subdir/e'])
def test_local_subdirparam(self):
tree = self.fetchUnpack(['file://a;subdir=bar'])
self.assertEqual(tree, ['bar/a'])
def test_local_deepsubdirparam(self):
tree = self.fetchUnpack(['file://dir/subdir/e;subdir=bar'])
self.assertEqual(tree, ['bar/dir/subdir/e'])
class FetcherNetworkTest(FetcherTest):
if os.environ.get("BB_SKIP_NETTESTS") == "yes":
@@ -504,21 +382,6 @@ class FetcherNetworkTest(FetcherTest):
url1 = url2 = "git://git.openembedded.org/bitbake"
self.gitfetcher(url1, url2)
def test_gitfetch_goodsrcrev(self):
# SRCREV is set but matches rev= parameter
url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
self.gitfetcher(url1, url2)
def test_gitfetch_badsrcrev(self):
# SRCREV is set but does not match rev= parameter
url1 = url2 = "git://git.openembedded.org/bitbake;rev=dead05b0b4ba0959fe0624d2a4885d7b70426da5"
self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
def test_gitfetch_tagandrev(self):
# SRCREV is set but does not match rev= parameter
url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;tag=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
def test_gitfetch_premirror(self):
url1 = "git://git.openembedded.org/bitbake"
url2 = "git://someserver.org/bitbake"
@@ -539,20 +402,12 @@ class FetcherNetworkTest(FetcherTest):
self.d.setVar("PREMIRRORS", "%s git://%s;protocol=file \n" % (dummyurl, self.sourcedir))
self.gitfetcher(dummyurl, dummyurl)
def test_git_submodule(self):
fetcher = bb.fetch.Fetch(["gitsm://git.yoctoproject.org/git-submodule-test;rev=f12e57f2edf0aa534cf1616fa983d165a92b0842"], self.d)
fetcher.download()
# Previous cwd has been deleted
os.chdir(os.path.dirname(self.unpackdir))
fetcher.unpack(self.unpackdir)
class URLHandle(unittest.TestCase):
datatable = {
"http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}),
"cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}),
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}),
"git://git.openembedded.org/bitbake;branch=@foo" : ('git', 'git.openembedded.org', '/bitbake', '', '', {'branch': '@foo'})
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
}
def test_decodeurl(self):

View File

@@ -21,7 +21,6 @@
import unittest
import bb
import os
class VerCmpString(unittest.TestCase):
@@ -52,52 +51,3 @@ class VerCmpString(unittest.TestCase):
result = bb.utils.explode_dep_versions2("foo ( =1.10 )")
self.assertEqual(result, correctresult)
def test_vercmp_string_op(self):
compareops = [('1', '1', '=', True),
('1', '1', '==', True),
('1', '1', '!=', False),
('1', '1', '>', False),
('1', '1', '<', False),
('1', '1', '>=', True),
('1', '1', '<=', True),
('1', '0', '=', False),
('1', '0', '==', False),
('1', '0', '!=', True),
('1', '0', '>', True),
('1', '0', '<', False),
('1', '0', '>>', True),
('1', '0', '<<', False),
('1', '0', '>=', True),
('1', '0', '<=', False),
('0', '1', '=', False),
('0', '1', '==', False),
('0', '1', '!=', True),
('0', '1', '>', False),
('0', '1', '<', True),
('0', '1', '>>', False),
('0', '1', '<<', True),
('0', '1', '>=', False),
('0', '1', '<=', True)]
for arg1, arg2, op, correctresult in compareops:
result = bb.utils.vercmp_string_op(arg1, arg2, op)
self.assertEqual(result, correctresult, 'vercmp_string_op("%s", "%s", "%s") != %s' % (arg1, arg2, op, correctresult))
# Check that clearly invalid operator raises an exception
self.assertRaises(bb.utils.VersionStringException, bb.utils.vercmp_string_op, '0', '0', '$')
class Path(unittest.TestCase):
def test_unsafe_delete_path(self):
checkitems = [('/', True),
('//', True),
('///', True),
(os.getcwd().count(os.sep) * ('..' + os.sep), True),
(os.environ.get('HOME', '/home/test'), True),
('/home/someone', True),
('/home/other/', True),
('/home/other/subdir', False),
('', False)]
for arg1, correctresult in checkitems:
result = bb.utils._check_unsafe_delete_path(arg1)
self.assertEqual(result, correctresult, '_check_unsafe_delete_path("%s") != %s' % (arg1, correctresult))

View File

@@ -25,12 +25,12 @@ import bb.cache
import bb.cooker
import bb.providers
import bb.utils
from bb.cooker import state, BBCooker, CookerFeatures
from bb.cooker import state, BBCooker
from bb.cookerdata import CookerConfiguration, ConfigParameters
import bb.fetch2
class Tinfoil:
def __init__(self, output=sys.stdout, tracking=False):
def __init__(self, output=sys.stdout):
# Needed to avoid deprecation warnings with python 2.6
warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -48,10 +48,7 @@ class Tinfoil:
configparams = TinfoilConfigParameters(parse_only=True)
self.config.setConfigParameters(configparams)
self.config.setServerRegIdleCallback(self.register_idle_function)
features = []
if tracking:
features.append(CookerFeatures.BASEDATASTORE_TRACKING)
self.cooker = BBCooker(self.config, features)
self.cooker = BBCooker(self.config)
self.config_data = self.cooker.data
bb.providers.logger.setLevel(logging.ERROR)
self.cooker_data = None
@@ -84,11 +81,6 @@ class Tinfoil:
else:
self.parseRecipes()
def shutdown(self):
self.cooker.shutdown(force=True)
self.cooker.post_serve()
self.cooker.unlockBitbake()
class TinfoilConfigParameters(ConfigParameters):
def __init__(self, **options):

File diff suppressed because it is too large Load Diff

View File

@@ -185,7 +185,6 @@ class BuildDetailsPage (HobPage):
def show_issues(self):
self.num_of_issues += 1
self.notebook.show_indicator_icon("Issues", self.num_of_issues)
self.notebook.queue_draw()
def reset_issues(self):
self.num_of_issues = 0
@@ -424,7 +423,7 @@ class BuildDetailsPage (HobPage):
if "recipes" in action:
self.builder.show_recipes()
elif "packages" in action:
self.builder.show_packages()
self.builder.show_packages(ask=False)
elif "image" in action:
self.builder.show_configuration()

View File

@@ -227,20 +227,14 @@ class Configuration:
handler.set_var_in_file("DEPENDS", self.selected_recipes, "local.conf")
handler.set_var_in_file("IMAGE_INSTALL", self.user_selected_packages, "local.conf")
# proxy
if self.enable_proxy == True:
handler.set_var_in_file("http_proxy", self.combine_proxy("http"), "local.conf")
handler.set_var_in_file("https_proxy", self.combine_proxy("https"), "local.conf")
handler.set_var_in_file("ftp_proxy", self.combine_proxy("ftp"), "local.conf")
handler.set_var_in_file("all_proxy", self.combine_proxy("socks"), "local.conf")
handler.set_var_in_file("CVS_PROXY_HOST", self.combine_host_only("cvs"), "local.conf")
handler.set_var_in_file("CVS_PROXY_PORT", self.combine_port_only("cvs"), "local.conf")
else:
handler.set_var_in_file("http_proxy", "", "local.conf")
handler.set_var_in_file("https_proxy", "", "local.conf")
handler.set_var_in_file("ftp_proxy", "", "local.conf")
handler.set_var_in_file("all_proxy", "", "local.conf")
handler.set_var_in_file("CVS_PROXY_HOST", "", "local.conf")
handler.set_var_in_file("CVS_PROXY_PORT", "", "local.conf")
handler.set_var_in_file("enable_proxy", self.enable_proxy, "local.conf")
handler.set_var_in_file("use_same_proxy", self.same_proxy, "local.conf")
handler.set_var_in_file("http_proxy", self.combine_proxy("http"), "local.conf")
handler.set_var_in_file("https_proxy", self.combine_proxy("https"), "local.conf")
handler.set_var_in_file("ftp_proxy", self.combine_proxy("ftp"), "local.conf")
handler.set_var_in_file("all_proxy", self.combine_proxy("socks"), "local.conf")
handler.set_var_in_file("CVS_PROXY_HOST", self.combine_host_only("cvs"), "local.conf")
handler.set_var_in_file("CVS_PROXY_PORT", self.combine_port_only("cvs"), "local.conf")
def __str__(self):
s = "VERSION: '%s', BBLAYERS: '%s', MACHINE: '%s', DISTRO: '%s', DL_DIR: '%s'," % \
@@ -380,6 +374,7 @@ class Builder(gtk.Window):
super(Builder, self).__init__()
self.hob_image = "hob-image"
self.hob_toolchain = "hob-toolchain"
# handler
self.handler = hobHandler
@@ -446,7 +441,6 @@ class Builder(gtk.Window):
self.handler.connect("recipe-populated", self.handler_recipe_populated_cb)
self.handler.connect("package-populated", self.handler_package_populated_cb)
self.handler.append_to_bbfiles("${TOPDIR}/recipes/images/custom/*.bb")
self.handler.append_to_bbfiles("${TOPDIR}/recipes/images/*.bb")
self.initiate_new_build_async()
@@ -534,9 +528,9 @@ class Builder(gtk.Window):
self.generate_configuration()
def update_config_async(self):
self.switch_page(self.MACHINE_SELECTION)
self.set_user_config()
self.generate_configuration()
self.switch_page(self.MACHINE_SELECTION)
def sanity_check(self):
self.handler.trigger_sanity_check()
@@ -597,6 +591,7 @@ class Builder(gtk.Window):
image = self.configuration.selected_image
self.handler.generate_image(image,
base_image,
self.hob_toolchain,
packages,
toolchain_packages,
self.configuration.default_task)
@@ -709,6 +704,7 @@ class Builder(gtk.Window):
self.set_user_config_proxies()
def set_user_config(self):
self.handler.reset_cooker()
# set bb layers
self.handler.set_bblayers(self.configuration.layers)
# set local configuration
@@ -738,11 +734,6 @@ class Builder(gtk.Window):
self.configuration.update(params)
self.parameters.update(params)
def set_base_image(self):
self.configuration.initial_selected_image = self.configuration.selected_image
if self.configuration.selected_image != self.recipe_model.__custom_image__:
self.hob_image = self.configuration.selected_image + "-edited"
def reset(self):
self.configuration.curr_mach = ""
self.configuration.clear_selection()
@@ -803,8 +794,8 @@ class Builder(gtk.Window):
self.generate_image_async(True)
def show_error_dialog(self, msg):
lbl = "<b>Hob found an error</b>"
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
lbl = "<b>Hob found an error</b>\n"
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR, msg)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
response = dialog.run()
@@ -820,9 +811,10 @@ class Builder(gtk.Window):
dialog.destroy()
def show_network_error_dialog(self):
lbl = "<b>Hob cannot connect to the network</b>"
msg = msg + "Please check your network connection. If you are using a proxy server, please make sure it is configured correctly."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
lbl = "<b>Hob cannot connect to the network</b>\n"
msg = "Please check your network connection. If you are using a proxy server, please make sure it is configured correctly."
lbl = lbl + "%s\n\n" % glib.markup_escape_text(msg)
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
button = dialog.add_button("Proxy settings", gtk.RESPONSE_CANCEL)
@@ -967,7 +959,7 @@ class Builder(gtk.Window):
if selected_image == self.recipe_model.__custom_image__:
if self.configuration.initial_selected_image != selected_image:
version = self.recipe_model.get_custom_image_version()
linkname = self.hob_image + version + "-" + self.configuration.curr_mach
linkname = 'hob-image' + version+ "-" + self.configuration.curr_mach
else:
linkname = selected_image + '-' + self.configuration.curr_mach
image_extension = self.get_image_extension()
@@ -1045,7 +1037,7 @@ class Builder(gtk.Window):
self.build_failed()
def handler_no_provider_cb(self, running_build, msg):
dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.MESSAGE_INFO)
dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1108,10 +1100,9 @@ class Builder(gtk.Window):
def build_packages(self):
_, all_recipes = self.recipe_model.get_selected_recipes()
if not all_recipes:
lbl = "<b>No selections made</b>"
msg = "You have not made any selections"
msg = msg + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
lbl = "<b>No selections made</b>\nYou have not made any selections"
lbl = lbl + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1122,10 +1113,9 @@ class Builder(gtk.Window):
def build_image(self):
selected_packages = self.package_model.get_selected_packages()
if not selected_packages:
lbl = "<b>No selections made</b>"
msg = "You have not made any selections"
msg = msg + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
lbl = "<b>No selections made</b>\nYou have not made any selections"
lbl = lbl + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1139,10 +1129,9 @@ class Builder(gtk.Window):
# If no base image and no selected packages don't build anything
if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
lbl = "<b>No selections made</b>"
msg = "You have not made any selections"
msg = msg + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
lbl = "<b>No selections made</b>\nYou have not made any selections"
lbl = lbl + " so there isn't anything to bake at this time."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1226,9 +1215,8 @@ class Builder(gtk.Window):
response = dialog.run()
if response == gtk.RESPONSE_YES:
if not dialog.image_names:
lbl = "<b>No selections made</b>"
msg = "You have not made any selections"
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
lbl = "<b>No selections made</b>\nYou have not made any selections"
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
crumbs_dialog.run()
@@ -1315,7 +1303,7 @@ class Builder(gtk.Window):
def deploy_image(self, image_name):
if not image_name:
lbl = "<b>Please select an image to deploy.</b>"
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO)
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1361,8 +1349,8 @@ class Builder(gtk.Window):
def runqemu_image(self, image_name, kernel_name):
if not image_name or not kernel_name:
lbl = "<b>Please select %s to launch in QEMU.</b>" % ("a kernel" if image_name else "an image")
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO)
lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image")
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
@@ -1383,23 +1371,38 @@ class Builder(gtk.Window):
cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
subprocess.Popen(shlex.split(cmdline))
else:
lbl = "<b>Path error</b>"
msg = "One of your paths is wrong,"
msg = msg + " please make sure the following paths exist:\n"
msg = msg + "image path:" + image_path + "\n"
msg = msg + "kernel path:" + kernel_path + "\n"
msg = msg + "source environment path:" + source_env_path + "\n"
msg = msg + "tmp path: " + tmp_path + "."
msg = msg + "You may be missing either xterm or vte for terminal services."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
lbl = "<b>Path error</b>\nOne of your paths is wrong,"
lbl = lbl + " please make sure the following paths exist:\n"
lbl = lbl + "image path:" + image_path + "\n"
lbl = lbl + "kernel path:" + kernel_path + "\n"
lbl = lbl + "source environment path:" + source_env_path + "\n"
lbl = lbl + "tmp path: " + tmp_path + "."
lbl = lbl + "You may be missing either xterm or vte for terminal services."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
dialog.run()
dialog.destroy()
def show_packages(self):
self.package_details_page.refresh_tables()
self.switch_page(self.PACKAGE_SELECTION)
def show_packages(self, ask=True):
_, selected_recipes = self.recipe_model.get_selected_recipes()
if selected_recipes and ask:
lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
lbl = lbl + " to get a full list or just view the existing packages?"
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = dialog.add_button("View packages", gtk.RESPONSE_NO)
HobAltButton.style_button(button)
button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
HobButton.style_button(button)
dialog.set_default_response(gtk.RESPONSE_YES)
response = dialog.run()
dialog.destroy()
if response == gtk.RESPONSE_YES:
self.generate_packages_async(True)
else:
self.switch_page(self.PACKAGE_SELECTION)
else:
self.switch_page(self.PACKAGE_SELECTION)
def show_recipes(self):
self.switch_page(self.RECIPE_SELECTION)
@@ -1412,28 +1415,26 @@ class Builder(gtk.Window):
def stop_build(self):
if self.stopping:
lbl = "<b>Force Stop build?</b>"
msg = "You've already selected Stop once,"
msg = msg + " would you like to 'Force Stop' the build?\n\n"
msg = msg + "This will stop the build as quickly as possible but may"
msg = msg + " well leave your build directory in an unusable state"
msg = msg + " that requires manual steps to fix."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
lbl = lbl + "This will stop the build as quickly as possible but may"
lbl = lbl + " well leave your build directory in an unusable state"
lbl = lbl + " that requires manual steps to fix.\n"
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
HobAltButton.style_button(button)
button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
HobButton.style_button(button)
else:
lbl = "<b>Stop build?</b>"
msg = "Are you sure you want to stop this"
msg = msg + " build?\n\n'Stop' will stop the build as soon as all in"
msg = msg + " progress build tasks are finished. However if a"
msg = msg + " lengthy compilation phase is in progress this may take"
msg = msg + " some time.\n\n"
msg = msg + "'Force Stop' will stop the build as quickly as"
msg = msg + " possible but may well leave your build directory in an"
msg = msg + " unusable state that requires manual steps to fix."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
lbl = lbl + " build?\n\n'Stop' will stop the build as soon as all in"
lbl = lbl + " progress build tasks are finished. However if a"
lbl = lbl + " lengthy compilation phase is in progress this may take"
lbl = lbl + " some time.\n\n"
lbl = lbl + "'Force Stop' will stop the build as quickly as"
lbl = lbl + " possible but may well leave your build directory in an"
lbl = lbl + " unusable state that requires manual steps to fix."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
HobAltButton.style_button(button)
button = dialog.add_button("Force stop", gtk.RESPONSE_YES)

View File

@@ -183,9 +183,8 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
self.set_save_button_state()
if self.get_num_checked_image_types() == 0:
# Show an error dialog
lbl = "<b>Select an image type</b>"
msg = "You need to select at least one image type."
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
lbl = "<b>Select an image type</b>\n\nYou need to select at least one image type."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
button = dialog.add_button("OK", gtk.RESPONSE_OK)
HobButton.style_button(button)
response = dialog.run()

View File

@@ -31,28 +31,51 @@ BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class CrumbsMessageDialog(gtk.MessageDialog):
class CrumbsMessageDialog(CrumbsDialog):
"""
A GNOME HIG compliant dialog widget.
Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
"""
def __init__(self, parent = None, label="", dialog_type = gtk.MESSAGE_QUESTION, msg=""):
super(CrumbsMessageDialog, self).__init__(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
dialog_type,
gtk.BUTTONS_NONE,
None)
def __init__(self, parent=None, label="", icon=gtk.STOCK_INFO, msg=""):
super(CrumbsMessageDialog, self).__init__("", parent, gtk.DIALOG_MODAL)
self.set_skip_taskbar_hint(False)
self.set_border_width(6)
self.vbox.set_property("spacing", 12)
self.action_area.set_property("spacing", 12)
self.action_area.set_property("border-width", 6)
self.set_markup(label)
first_column = gtk.HBox(spacing=12)
first_column.set_property("border-width", 6)
first_column.show()
self.vbox.add(first_column)
if 0 <= len(msg) < 300:
self.format_secondary_markup(msg)
self.icon = gtk.Image()
# We have our own Info icon which should be used in preference of the stock icon
self.icon_chk = HobIconChecker()
self.icon.set_from_stock(self.icon_chk.check_stock_icon(icon), gtk.ICON_SIZE_DIALOG)
self.icon.set_property("yalign", 0.00)
self.icon.show()
first_column.pack_start(self.icon, expand=False, fill=True, padding=0)
if 0 <= len(msg) < 200:
lbl = label + "%s" % glib.markup_escape_text(msg)
self.label_short = gtk.Label()
self.label_short.set_use_markup(True)
self.label_short.set_line_wrap(True)
self.label_short.set_markup(lbl)
self.label_short.set_property("yalign", 0.00)
self.label_short.show()
first_column.add(self.label_short)
else:
vbox = self.get_message_area()
vbox.set_border_width(1)
vbox.set_property("spacing", 12)
second_row = gtk.VBox(spacing=12)
second_row.set_property("border-width", 6)
self.label_long = gtk.Label()
self.label_long.set_use_markup(True)
self.label_long.set_line_wrap(True)
self.label_long.set_markup(label)
self.label_long.set_alignment(0.0, 0.0)
second_row.pack_start(self.label_long, expand=False, fill=False, padding=0)
self.label_long.show()
self.textWindow = gtk.ScrolledWindow()
self.textWindow.set_shadow_type(gtk.SHADOW_IN)
self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -66,5 +89,7 @@ class CrumbsMessageDialog(gtk.MessageDialog):
self.msgView.set_buffer(self.buf)
self.textWindow.add(self.msgView)
self.msgView.show()
vbox.add(self.textWindow)
second_row.add(self.textWindow)
self.textWindow.show()
first_column.add(second_row)
second_row.show()

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