mirror of
https://git.yoctoproject.org/poky
synced 2026-02-02 06:48:43 +01:00
Compare commits
5 Commits
dora
...
1.4_M3.rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ae1a4ba97 | ||
|
|
5e17deac79 | ||
|
|
b47d0b23d9 | ||
|
|
8617f7eb15 | ||
|
|
87017b554e |
101
README.hardware
101
README.hardware
@@ -1,34 +1,28 @@
|
||||
Poky Hardware README
|
||||
====================
|
||||
|
||||
This file gives details about using Poky with the reference machines
|
||||
supported out of the box. A full list of supported reference target machines
|
||||
can be found by looking in the following directories:
|
||||
|
||||
meta/conf/machine/
|
||||
meta-yocto-bsp/conf/machine/
|
||||
|
||||
If you are in doubt about using Poky/OpenEmbedded with your hardware, consult
|
||||
the documentation for your board/device.
|
||||
This file gives details about using Poky with different hardware reference
|
||||
boards and consumer devices. A full list of target machines can be found by
|
||||
looking in the meta/conf/machine/ directory. If in doubt about using Poky with
|
||||
your hardware, consult the documentation for your board/device.
|
||||
|
||||
Support for additional devices is normally added by creating BSP layers - for
|
||||
more information please see the Yocto Board Support Package (BSP) Developer's
|
||||
Guide - documentation source is in documentation/bspguide or download the PDF
|
||||
from:
|
||||
|
||||
http://yoctoproject.org/documentation
|
||||
http://yoctoproject.org/community/documentation
|
||||
|
||||
Support for physical reference hardware has now been split out into a
|
||||
meta-yocto-bsp layer which can be removed separately from other layers if not
|
||||
needed.
|
||||
Support for machines other than QEMU may be moved out to separate BSP layers in
|
||||
future versions.
|
||||
|
||||
|
||||
QEMU Emulation Targets
|
||||
======================
|
||||
|
||||
To simplify development, the build system supports building images to
|
||||
work with the QEMU emulator in system emulation mode. Several architectures
|
||||
are currently supported:
|
||||
To simplify development Poky supports building images to work with the QEMU
|
||||
emulator in system emulation mode. Several architectures are currently
|
||||
supported:
|
||||
|
||||
* ARM (qemuarm)
|
||||
* x86 (qemux86)
|
||||
@@ -36,33 +30,32 @@ are currently supported:
|
||||
* PowerPC (qemuppc)
|
||||
* MIPS (qemumips)
|
||||
|
||||
Use of the QEMU images is covered in the Yocto Project Reference Manual.
|
||||
The appropriate MACHINE variable value corresponding to the target is given
|
||||
in brackets.
|
||||
Use of the QEMU images is covered in the Poky Reference Manual. The Poky
|
||||
MACHINE setting corresponding to the target is given in brackets.
|
||||
|
||||
|
||||
Hardware Reference Boards
|
||||
=========================
|
||||
|
||||
The following boards are supported by the meta-yocto-bsp layer:
|
||||
The following boards are supported by Poky's core layer:
|
||||
|
||||
* 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.
|
||||
For more information see the board's section below. The Poky MACHINE setting
|
||||
corresponding to the board is given in brackets.
|
||||
|
||||
|
||||
Consumer Devices
|
||||
================
|
||||
|
||||
The following consumer devices are supported by the meta-yocto-bsp layer:
|
||||
The following consumer devices are supported by Poky's core layer:
|
||||
|
||||
* Intel x86 based PCs and devices (genericx86)
|
||||
* Intel Atom based PCs and devices (atom-pc)
|
||||
|
||||
For more information see the device's section below. The appropriate MACHINE
|
||||
variable value corresponding to the device is given in brackets.
|
||||
For more information see the device's section below. The Poky MACHINE setting
|
||||
corresponding to the device is given in brackets.
|
||||
|
||||
|
||||
|
||||
@@ -70,40 +63,22 @@ variable value corresponding to the device is given in brackets.
|
||||
===============================
|
||||
|
||||
|
||||
Intel x86 based PCs and devices (genericx86)
|
||||
Intel Atom based PCs and devices (atom-pc)
|
||||
==========================================
|
||||
|
||||
The genericx86 MACHINE is tested on the following platforms:
|
||||
The atom-pc MACHINE is tested on the following platforms:
|
||||
|
||||
Intel Xeon/Core i-Series:
|
||||
+ Intel Romley Server: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Canoe Pass CRB)
|
||||
+ Intel Romley Server: Ivy Bridge Xeon processor, C600 PCH (Patsburg), (Intel SDP S2R3)
|
||||
+ Intel Crystal Forest Server: Sandy Bridge Xeon processor, DH89xx PCH (Cave Creek), (Stargo CRB)
|
||||
+ Intel Chief River Mobile: Ivy Bridge Mobile processor, QM77 PCH (Panther Point-M), (Emerald Lake II CRB, Sabino Canyon CRB)
|
||||
+ Intel Huron River Mobile: Sandy Bridge processor, QM67 PCH (Cougar Point), (Emerald Lake CRB, EVOC EC7-1817LNAR board)
|
||||
+ Intel Calpella Platform: Core i7 processor, QM57 PCH (Ibex Peak-M), (Red Fort CRB, Emerson MATXM CORE-411-B)
|
||||
+ Intel Nehalem/Westmere-EP Server: Xeon 56xx/55xx processors, 5520 chipset, ICH10R IOH (82801), (Hanlan Creek CRB)
|
||||
+ Intel Nehalem Workstation: Xeon 56xx/55xx processors, System SC5650SCWS (Greencity CRB)
|
||||
+ Intel Picket Post Server: Xeon 56xx/55xx processors (Jasper Forest), 3420 chipset (Ibex Peak), (Osage CRB)
|
||||
+ Intel Storage Platform: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Oak Creek Canyon CRB)
|
||||
+ Intel Shark Bay Client Platform: Haswell processor, LynxPoint PCH, (Walnut Canyon CRB, Lava Canyon CRB, Basking Ridge CRB, Flathead Creek CRB)
|
||||
+ Intel Shark Bay Ultrabook Platform: Haswell ULT processor, Lynx Point-LP PCH, (WhiteTip Mountain 1 CRB)
|
||||
o Asus EeePC 901
|
||||
o Acer Aspire One
|
||||
o Toshiba NB305
|
||||
o Intel Embedded Development Board 1-N450 (Black Sand)
|
||||
|
||||
Intel Atom platforms:
|
||||
+ Intel embedded Menlow: Intel Atom Z510/530 CPU, System Controller Hub US15W (Portwell NANO-8044)
|
||||
+ Intel Luna Pier: Intel Atom N4xx/D5xx series CPU (aka: Pineview-D & -M), 82801HM I/O Hub (ICH8M), (Advantech AIMB-212, Moon Creek CRB)
|
||||
+ Intel Queens Bay platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Emerson NITX-315, Crown Bay CRB, Minnow Board)
|
||||
+ Intel Fish River Island platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Kontron KM2M806)
|
||||
+ Intel Cedar Trail platform: Intel Atom N2000 & D2000 series CPU (aka: Cedarview), NM10 Express Chipset (Norco kit BIS-6630, Cedar Rock CRB)
|
||||
|
||||
and is likely to work on many unlisted Atom/Core/Xeon based devices. The MACHINE
|
||||
type supports ethernet, wifi, sound, and Intel/vesa graphics by default in
|
||||
addition to common PC input devices, busses, and so on. Note that it does not
|
||||
included the binary-only graphic drivers used on some Atom platforms, for
|
||||
accelerated graphics on these machines please refer to meta-intel.
|
||||
and is likely to work on many unlisted Atom based devices. The MACHINE type
|
||||
supports ethernet, wifi, sound, and i915 graphics by default in addition to
|
||||
common PC input devices, busses, and so on.
|
||||
|
||||
Depending on the device, it can boot from a traditional hard-disk, a USB device,
|
||||
or over the network. Writing generated images to physical media is
|
||||
or over the network. Writing poky generated images to physical media is
|
||||
straightforward with a caveat for USB devices. The following examples assume the
|
||||
target boot device is /dev/sdb, be sure to verify this and use the correct
|
||||
device as the following commands are run as root and are not reversable.
|
||||
@@ -111,7 +86,7 @@ device as the following commands are run as root and are not reversable.
|
||||
USB Device:
|
||||
1. Build a live image. This image type consists of a simple filesystem
|
||||
without a partition table, which is suitable for USB keys, and with the
|
||||
default setup for the genericx86 machine, this image type is built
|
||||
default setup for the atom-pc machine, this image type is built
|
||||
automatically for any image you build. For example:
|
||||
|
||||
$ bitbake core-image-minimal
|
||||
@@ -119,7 +94,7 @@ USB Device:
|
||||
2. Use the "dd" utility to write the image to the raw block device. For
|
||||
example:
|
||||
|
||||
# dd if=core-image-minimal-genericx86.hddimg of=/dev/sdb
|
||||
# dd if=core-image-minimal-atom-pc.hddimg of=/dev/sdb
|
||||
|
||||
If the device fails to boot with "Boot error" displayed, or apparently
|
||||
stops just after the SYSLINUX version banner, it is likely the BIOS cannot
|
||||
@@ -156,11 +131,11 @@ USB Device:
|
||||
device stops flashing, remove and reinsert the device to allow the
|
||||
kernel to detect the new partition layout.
|
||||
|
||||
c. Copy the contents of the image to the USB-ZIP mode device:
|
||||
c. Copy the contents of the poky image to the USB-ZIP mode device:
|
||||
|
||||
# mkdir /tmp/image
|
||||
# mkdir /tmp/usbkey
|
||||
# mount -o loop core-image-minimal-genericx86.hddimg /tmp/image
|
||||
# mount -o loop core-image-minimal-atom-pc.hddimg /tmp/image
|
||||
# mount /dev/sdb4 /tmp/usbkey
|
||||
# cp -rf /tmp/image/* /tmp/usbkey
|
||||
|
||||
@@ -306,8 +281,8 @@ anything here.
|
||||
Load the kernel and dtb (device tree blob), and boot the system as follows:
|
||||
|
||||
1. Get the kernel (uImage-mpc8315e-rdb.bin) and dtb (uImage-mpc8315e-rdb.dtb)
|
||||
files from the tmp/deploy directory, and make them available on your TFTP
|
||||
server.
|
||||
files from the Poky build tmp/deploy directory, and make them available on
|
||||
your TFTP server.
|
||||
|
||||
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
|
||||
@@ -326,9 +301,9 @@ Load the kernel and dtb (device tree blob), and boot the system as follows:
|
||||
|
||||
5. Download the kernel and dtb, and boot:
|
||||
|
||||
=> tftp 1000000 uImage-mpc8315e-rdb.bin
|
||||
=> tftp 2000000 uImage-mpc8315e-rdb.dtb
|
||||
=> bootm 1000000 - 2000000
|
||||
=> tftp 800000 uImage-mpc8315e-rdb.bin
|
||||
=> tftp 780000 uImage-mpc8315e-rdb.dtb
|
||||
=> bootm 800000 - 780000
|
||||
|
||||
|
||||
Ubiquiti Networks RouterStation Pro (routerstationpro)
|
||||
|
||||
@@ -39,15 +39,10 @@ import bb.msg
|
||||
from bb import cooker
|
||||
from bb import ui
|
||||
from bb import server
|
||||
from bb import cookerdata
|
||||
|
||||
__version__ = "1.20.0"
|
||||
__version__ = "1.17.0"
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
# Python multiprocessing requires /dev/shm
|
||||
if not os.access('/dev/shm', os.W_OK | os.X_OK):
|
||||
sys.exit("FATAL: /dev/shm does not exist or is not writable")
|
||||
|
||||
# Unbuffer stdout to avoid log truncation in the event
|
||||
# of an unorderly exit as well as to provide timely
|
||||
# updates to log files for use with tail
|
||||
@@ -57,6 +52,16 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
class BBConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self, options):
|
||||
for key, val in options.__dict__.items():
|
||||
setattr(self, key, val)
|
||||
self.pkgs_to_build = []
|
||||
|
||||
|
||||
def get_ui(config):
|
||||
if not config.ui:
|
||||
@@ -70,7 +75,7 @@ def get_ui(config):
|
||||
# suggest a fixed set this allows you to have flexibility in which
|
||||
# ones are available.
|
||||
module = __import__("bb.ui", fromlist = [interface])
|
||||
return getattr(module, interface)
|
||||
return getattr(module, interface).main
|
||||
except AttributeError:
|
||||
sys.exit("FATAL: Invalid user interface '%s' specified.\n"
|
||||
"Valid interfaces: depexp, goggle, ncurses, hob, knotty [default]." % interface)
|
||||
@@ -95,240 +100,180 @@ warnings.filterwarnings("ignore", category=ImportWarning)
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning, module="<string>$")
|
||||
warnings.filterwarnings("ignore", message="With-statements now directly support multiple context managers")
|
||||
|
||||
class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||
|
||||
def parseCommandLine(self):
|
||||
parser = optparse.OptionParser(
|
||||
version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),
|
||||
usage = """%prog [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.""")
|
||||
|
||||
parser.add_option("-b", "--buildfile", help = "Execute tasks from a specific .bb recipe directly. WARNING: Does not handle any dependencies from other recipes.",
|
||||
action = "store", dest = "buildfile", default = None)
|
||||
|
||||
parser.add_option("-k", "--continue", help = "Continue as much as possible after an error. While the target that failed and anything depending on it cannot be built, as much as possible will be built before stopping.",
|
||||
action = "store_false", dest = "abort", default = True)
|
||||
|
||||
parser.add_option("-a", "--tryaltconfigs", help = "Continue with builds by trying to use alternative providers where possible.",
|
||||
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||
|
||||
parser.add_option("-f", "--force", help = "Force the specified targets/task to run (invalidating any existing stamp file).",
|
||||
action = "store_true", dest = "force", default = False)
|
||||
|
||||
parser.add_option("-c", "--cmd", help = "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.",
|
||||
action = "store", dest = "cmd")
|
||||
|
||||
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified task such as 'compile' and then run the default task for the specified target(s).",
|
||||
action = "store", dest = "invalidate_stamp")
|
||||
|
||||
parser.add_option("-r", "--read", help = "Read the specified file before bitbake.conf.",
|
||||
action = "append", dest = "prefile", default = [])
|
||||
|
||||
parser.add_option("-R", "--postread", help = "Read the specified file after bitbake.conf.",
|
||||
action = "append", dest = "postfile", default = [])
|
||||
|
||||
parser.add_option("-v", "--verbose", help = "Output more log message data to the terminal.",
|
||||
action = "store_true", dest = "verbose", default = False)
|
||||
|
||||
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||
action = "count", dest="debug", default = 0)
|
||||
|
||||
parser.add_option("-n", "--dry-run", help = "Don't execute, just go through the motions.",
|
||||
action = "store_true", dest = "dry_run", default = False)
|
||||
|
||||
parser.add_option("-S", "--dump-signatures", help = "Don't execute, just dump out the signature construction information.",
|
||||
action = "store_true", dest = "dump_signatures", default = False)
|
||||
|
||||
parser.add_option("-p", "--parse-only", help = "Quit after parsing the BB recipes.",
|
||||
action = "store_true", dest = "parse_only", default = False)
|
||||
|
||||
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-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.",
|
||||
action = "store_true", dest = "dot_graph", default = False)
|
||||
|
||||
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
|
||||
action = "append", dest = "extra_assume_provided", default = [])
|
||||
|
||||
parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
|
||||
action = "append", dest = "debug_domains", default = [])
|
||||
|
||||
parser.add_option("-P", "--profile", help = "Profile the command and save reports.",
|
||||
action = "store_true", dest = "profile", default = False)
|
||||
|
||||
parser.add_option("-u", "--ui", help = "The user interface to use (e.g. knotty, hob, depexp).",
|
||||
action = "store", dest = "ui")
|
||||
|
||||
parser.add_option("-t", "--servertype", help = "Choose which server to use, process or xmlrpc.",
|
||||
action = "store", dest = "servertype")
|
||||
|
||||
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)
|
||||
|
||||
parser.add_option("", "--server-only", help = "Run bitbake without a UI, only starting a server (cooker) process.",
|
||||
action = "store_true", dest = "server_only", default = False)
|
||||
|
||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to.",
|
||||
action = "store", dest = "bind", default = False)
|
||||
|
||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks. sstate will be ignored and everything needed, built.",
|
||||
action = "store_true", dest = "nosetscene", default = False)
|
||||
|
||||
parser.add_option("", "--remote-server", help = "Connect to the specified server.",
|
||||
action = "store", dest = "remote_server", default = False)
|
||||
|
||||
parser.add_option("-m", "--kill-server", help = "Terminate the remote server.",
|
||||
action = "store_true", dest = "kill_server", default = False)
|
||||
|
||||
parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
|
||||
action = "store_true", dest = "observe_only", default = False)
|
||||
|
||||
options, targets = parser.parse_args(sys.argv)
|
||||
|
||||
# some environmental variables set also configuration options
|
||||
if "BBSERVER" in os.environ:
|
||||
options.servertype = "xmlrpc"
|
||||
options.remote_server = os.environ["BBSERVER"]
|
||||
|
||||
return options, targets[1:]
|
||||
|
||||
|
||||
def start_server(servermodule, configParams, configuration):
|
||||
server = servermodule.BitBakeServer()
|
||||
if configParams.bind:
|
||||
(host, port) = configParams.bind.split(':')
|
||||
server.initServer((host, int(port)))
|
||||
else:
|
||||
server.initServer()
|
||||
|
||||
try:
|
||||
configuration.setServerRegIdleCallback(server.getServerIdleCB())
|
||||
|
||||
cooker = bb.cooker.BBCooker(configuration)
|
||||
|
||||
server.addcooker(cooker)
|
||||
server.saveConnectionDetails()
|
||||
except Exception as e:
|
||||
exc_info = sys.exc_info()
|
||||
while True:
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
try:
|
||||
event = server.event_queue.get(block=False)
|
||||
except (queue.Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
raise exc_info[1], None, exc_info[2]
|
||||
server.detach()
|
||||
return server
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(
|
||||
version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),
|
||||
usage = """%prog [options] [package ...]
|
||||
|
||||
configParams = BitBakeConfigParameters()
|
||||
configuration = cookerdata.CookerConfiguration()
|
||||
configuration.setConfigParameters(configParams)
|
||||
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.""")
|
||||
|
||||
ui_module = get_ui(configParams)
|
||||
parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.",
|
||||
action = "store", dest = "buildfile", default = None)
|
||||
|
||||
# Server type can be xmlrpc or process currently, if nothing is specified,
|
||||
parser.add_option("-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",
|
||||
action = "store_false", dest = "abort", default = True)
|
||||
|
||||
parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.",
|
||||
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||
|
||||
parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status",
|
||||
action = "store_true", dest = "force", default = False)
|
||||
|
||||
parser.add_option("-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks",
|
||||
action = "store", dest = "cmd")
|
||||
|
||||
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified cmd such as 'compile' and run the default task for the specified target(s)",
|
||||
action = "store", dest = "invalidate_stamp")
|
||||
|
||||
parser.add_option("-r", "--read", help = "read the specified file before bitbake.conf",
|
||||
action = "append", dest = "prefile", default = [])
|
||||
|
||||
parser.add_option("-R", "--postread", help = "read the specified file after bitbake.conf",
|
||||
action = "append", dest = "postfile", default = [])
|
||||
|
||||
parser.add_option("-v", "--verbose", help = "output more chit-chat to the terminal",
|
||||
action = "store_true", dest = "verbose", default = False)
|
||||
|
||||
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||
action = "count", dest="debug", default = 0)
|
||||
|
||||
parser.add_option("-n", "--dry-run", help = "don't execute, just go through the motions",
|
||||
action = "store_true", dest = "dry_run", default = False)
|
||||
|
||||
parser.add_option("-S", "--dump-signatures", help = "don't execute, just dump out the signature construction information",
|
||||
action = "store_true", dest = "dump_signatures", default = False)
|
||||
|
||||
parser.add_option("-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
|
||||
action = "store_true", dest = "parse_only", default = False)
|
||||
|
||||
parser.add_option("-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-package environment (this is what used to be bbread)",
|
||||
action = "store_true", dest = "show_environment", default = False)
|
||||
|
||||
parser.add_option("-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list",
|
||||
action = "store_true", dest = "dot_graph", default = False)
|
||||
|
||||
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
|
||||
action = "append", dest = "extra_assume_provided", default = [])
|
||||
|
||||
parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
|
||||
action = "append", dest = "debug_domains", default = [])
|
||||
|
||||
parser.add_option("-P", "--profile", help = "profile the command and print a report",
|
||||
action = "store_true", dest = "profile", default = False)
|
||||
|
||||
parser.add_option("-u", "--ui", help = "userinterface to use",
|
||||
action = "store", dest = "ui")
|
||||
|
||||
parser.add_option("-t", "--servertype", help = "Choose which server to use, none, process or xmlrpc",
|
||||
action = "store", dest = "servertype")
|
||||
|
||||
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)
|
||||
|
||||
parser.add_option("", "--server-only", help = "Run bitbake without UI, the frontend can connect with bitbake server itself",
|
||||
action = "store_true", dest = "server_only", default = False)
|
||||
|
||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
|
||||
action = "store", dest = "bind", default = False)
|
||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
|
||||
action = "store_true", dest = "nosetscene", default = False)
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
configuration = BBConfiguration(options)
|
||||
configuration.pkgs_to_build.extend(args[1:])
|
||||
|
||||
ui_main = get_ui(configuration)
|
||||
|
||||
# Server type can be xmlrpc, process or none currently, if nothing is specified,
|
||||
# the default server is process
|
||||
if configParams.servertype:
|
||||
server_type = configParams.servertype
|
||||
if configuration.servertype:
|
||||
server_type = configuration.servertype
|
||||
else:
|
||||
server_type = 'process'
|
||||
|
||||
try:
|
||||
module = __import__("bb.server", fromlist = [server_type])
|
||||
servermodule = getattr(module, server_type)
|
||||
server = getattr(module, server_type)
|
||||
except AttributeError:
|
||||
sys.exit("FATAL: Invalid server type '%s' specified.\n"
|
||||
"Valid interfaces: xmlrpc, process [default]." % servertype)
|
||||
"Valid interfaces: xmlrpc, process [default], none." % servertype)
|
||||
|
||||
if configParams.server_only:
|
||||
if configParams.servertype != "xmlrpc":
|
||||
if configuration.server_only:
|
||||
if configuration.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '--server-only' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
if not configParams.bind:
|
||||
if not configuration.bind:
|
||||
sys.exit("FATAL: The '--server-only' option requires a name/address to bind to with the -B option.\n")
|
||||
if configParams.remote_server:
|
||||
sys.exit("FATAL: The '--server-only' option conflicts with %s.\n" %
|
||||
("the BBSERVER environment variable" if "BBSERVER" in os.environ else "the '--remote-server' option" ))
|
||||
|
||||
if configParams.bind and configParams.servertype != "xmlrpc":
|
||||
if configuration.bind and configuration.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
|
||||
if configParams.remote_server and configParams.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
|
||||
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 "BBDEBUG" in os.environ:
|
||||
level = int(os.environ["BBDEBUG"])
|
||||
if level > configuration.debug:
|
||||
configuration.debug = level
|
||||
|
||||
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
|
||||
bb.msg.init_msgconfig(configuration.verbose, configuration.debug,
|
||||
configuration.debug_domains)
|
||||
|
||||
# Ensure logging messages get sent to the UI as events
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
# Clear away any spurious environment variables while we stoke up the cooker
|
||||
cleanedvars = bb.utils.clean_environment()
|
||||
# Before we start modifying the environment we should take a pristine
|
||||
# copy for possible later use
|
||||
initialenv = os.environ.copy()
|
||||
# Clear away any spurious environment variables. But don't wipe the
|
||||
# environment totally. This is necessary to ensure the correct operation
|
||||
# of the UIs (e.g. for DISPLAY, etc.)
|
||||
bb.utils.clean_environment()
|
||||
|
||||
if not configParams.remote_server:
|
||||
# we start a server with a given configuration
|
||||
server = start_server(servermodule, configParams, configuration)
|
||||
bb.event.ui_queue = []
|
||||
server = server.BitBakeServer()
|
||||
if configuration.bind:
|
||||
server.initServer((configuration.bind, 0))
|
||||
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)
|
||||
server.initServer()
|
||||
|
||||
if not configParams.server_only:
|
||||
# Collect the feature set for the UI
|
||||
featureset = getattr(ui_module, "featureSet", [])
|
||||
idle = server.getServerIdleCB()
|
||||
|
||||
cooker = bb.cooker.BBCooker(configuration, idle, initialenv)
|
||||
cooker.parseCommandLine()
|
||||
|
||||
server.addcooker(cooker)
|
||||
server.saveConnectionDetails()
|
||||
server.detach()
|
||||
|
||||
# Should no longer need to ever reference cooker
|
||||
del cooker
|
||||
|
||||
logger.removeHandler(handler)
|
||||
|
||||
if not configuration.server_only:
|
||||
# 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:
|
||||
os.environ[k] = cleanedvars[k]
|
||||
|
||||
logger.removeHandler(handler)
|
||||
server_connection = server.establishConnection()
|
||||
|
||||
try:
|
||||
return ui_module.main(server_connection.connection, server_connection.events, configParams)
|
||||
return server.launchUI(ui_main, server_connection.connection, server_connection.events)
|
||||
finally:
|
||||
bb.event.ui_queue = []
|
||||
server_connection.terminate()
|
||||
else:
|
||||
print("server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
|
||||
print("server address: %s, server port: %s" % (server.serverinfo.host, server.serverinfo.port))
|
||||
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
ret = main()
|
||||
except bb.BBHandledException:
|
||||
ret = 1
|
||||
except Exception:
|
||||
ret = 1
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
traceback.print_exc(5)
|
||||
sys.exit(ret)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# bitbake-diffsigs
|
||||
# BitBake task signature data comparison utility
|
||||
#
|
||||
# Copyright (C) 2012-2013 Intel Corporation
|
||||
# Copyright (C) 2012 Intel Corporation
|
||||
#
|
||||
# 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
|
||||
@@ -30,18 +30,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), '
|
||||
import bb.tinfoil
|
||||
import bb.siggen
|
||||
|
||||
def logger_create(name, output=sys.stderr):
|
||||
logger = logging.getLogger(name)
|
||||
console = logging.StreamHandler(output)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
logger.setLevel(logging.INFO)
|
||||
return logger
|
||||
|
||||
logger = logger_create('bitbake-diffsigs')
|
||||
logger = logging.getLogger('BitBake')
|
||||
|
||||
def find_compare_task(bbhandler, pn, taskname):
|
||||
""" Find the most recent signature files for the specified PN/task and compare them """
|
||||
@@ -50,9 +39,6 @@ def find_compare_task(bbhandler, pn, taskname):
|
||||
logger.error('Metadata does not support finding signature data files')
|
||||
sys.exit(1)
|
||||
|
||||
if not taskname.startswith('do_'):
|
||||
taskname = 'do_%s' % taskname
|
||||
|
||||
filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
|
||||
latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-2:]
|
||||
if not latestfiles:
|
||||
@@ -85,7 +71,6 @@ def find_compare_task(bbhandler, pn, taskname):
|
||||
|
||||
|
||||
parser = optparse.OptionParser(
|
||||
description = "Compares siginfo/sigdata files written out by BitBake",
|
||||
usage = """
|
||||
%prog -t recipename taskname
|
||||
%prog sigdatafile1 sigdatafile2
|
||||
@@ -93,30 +78,25 @@ parser = optparse.OptionParser(
|
||||
|
||||
parser.add_option("-t", "--task",
|
||||
help = "find the signature data files for last two runs of the specified task and compare them",
|
||||
action="store", dest="taskargs", nargs=2, metavar='recipename taskname')
|
||||
action="store_true", dest="taskmode")
|
||||
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
if options.taskargs:
|
||||
tinfoil = bb.tinfoil.Tinfoil()
|
||||
tinfoil.prepare(config_only = True)
|
||||
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
|
||||
if len(args) == 1:
|
||||
parser.print_help()
|
||||
else:
|
||||
if len(args) == 1:
|
||||
parser.print_help()
|
||||
tinfoil = bb.tinfoil.Tinfoil()
|
||||
if options.taskmode:
|
||||
if len(args) < 3:
|
||||
logger.error("Please specify a recipe and task name")
|
||||
sys.exit(1)
|
||||
tinfoil.prepare(config_only = True)
|
||||
find_compare_task(tinfoil, args[1], args[2])
|
||||
else:
|
||||
import cPickle
|
||||
try:
|
||||
if len(args) == 2:
|
||||
output = bb.siggen.dump_sigfile(sys.argv[1])
|
||||
else:
|
||||
output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except cPickle.UnpicklingError, EOFError:
|
||||
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
|
||||
sys.exit(1)
|
||||
if len(args) == 2:
|
||||
output = bb.siggen.dump_sigfile(sys.argv[1])
|
||||
else:
|
||||
output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
|
||||
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
|
||||
@@ -1,65 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# bitbake-dumpsig
|
||||
# BitBake task signature dump utility
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# 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 warnings
|
||||
import optparse
|
||||
import logging
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
|
||||
import bb.siggen
|
||||
|
||||
def logger_create(name, output=sys.stderr):
|
||||
logger = logging.getLogger(name)
|
||||
console = logging.StreamHandler(output)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
logger.setLevel(logging.INFO)
|
||||
return logger
|
||||
|
||||
logger = logger_create('bitbake-dumpsig')
|
||||
|
||||
parser = optparse.OptionParser(
|
||||
description = "Dumps siginfo/sigdata files written out by BitBake",
|
||||
usage = """
|
||||
%prog sigdatafile""")
|
||||
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
if len(args) == 1:
|
||||
parser.print_help()
|
||||
else:
|
||||
import cPickle
|
||||
try:
|
||||
output = bb.siggen.dump_sigfile(args[1])
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except cPickle.UnpicklingError, EOFError:
|
||||
logger.error('Invalid signature data - ensure you are specifying a sigdata/siginfo file')
|
||||
sys.exit(1)
|
||||
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
output = bb.siggen.dump_sigfile(sys.argv[1])
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
|
||||
@@ -26,7 +26,6 @@ import os
|
||||
import sys
|
||||
import fnmatch
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
bindir = os.path.dirname(__file__)
|
||||
topdir = os.path.dirname(bindir)
|
||||
@@ -73,7 +72,7 @@ class Commands(cmd.Cmd):
|
||||
else:
|
||||
sys.stdout.write("usage: bitbake-layers <command> [arguments]\n\n")
|
||||
sys.stdout.write("Available commands:\n")
|
||||
procnames = list(set(self.get_names()))
|
||||
procnames = self.get_names()
|
||||
for procname in procnames:
|
||||
if procname[:3] == 'do_':
|
||||
sys.stdout.write(" %s\n" % procname[3:].replace('_', '-'))
|
||||
@@ -89,7 +88,7 @@ class Commands(cmd.Cmd):
|
||||
for layerdir in self.bblayers:
|
||||
layername = self.get_layer_name(layerdir)
|
||||
layerpri = 0
|
||||
for layer, _, regex, pri in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layer, _, regex, pri in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(os.path.join(layerdir, 'test')):
|
||||
layerpri = pri
|
||||
break
|
||||
@@ -225,15 +224,15 @@ Options:
|
||||
|
||||
|
||||
def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_multi_provider_only):
|
||||
pkg_pn = self.bbhandler.cooker.recipecache.pkg_pn
|
||||
(latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.config_data, self.bbhandler.cooker.recipecache, pkg_pn)
|
||||
allproviders = bb.providers.allProviders(self.bbhandler.cooker.recipecache)
|
||||
pkg_pn = self.bbhandler.cooker.status.pkg_pn
|
||||
(latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.cooker.configuration.data, self.bbhandler.cooker.status, pkg_pn)
|
||||
allproviders = bb.providers.allProviders(self.bbhandler.cooker.status)
|
||||
|
||||
# Ensure we list skipped recipes
|
||||
# We are largely guessing about PN, PV and the preferred version here,
|
||||
# but we have no choice since skipped recipes are not fully parsed
|
||||
skiplist = self.bbhandler.cooker.skiplist.keys()
|
||||
skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.collection.calc_bbfile_priority(fileitem) )
|
||||
skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.calc_bbfile_priority(fileitem) )
|
||||
skiplist.reverse()
|
||||
for fn in skiplist:
|
||||
recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
|
||||
@@ -371,8 +370,8 @@ build results (as the layer priority order has effectively changed).
|
||||
appended_recipes = []
|
||||
for layer in layers:
|
||||
overlayed = []
|
||||
for f in self.bbhandler.cooker.collection.overlayed.iterkeys():
|
||||
for of in self.bbhandler.cooker.collection.overlayed[f]:
|
||||
for f in self.bbhandler.cooker.overlayed.iterkeys():
|
||||
for of in self.bbhandler.cooker.overlayed[f]:
|
||||
if of.startswith(layer):
|
||||
overlayed.append(of)
|
||||
|
||||
@@ -396,8 +395,8 @@ build results (as the layer priority order has effectively changed).
|
||||
logger.warn('Overwriting file %s', fdest)
|
||||
bb.utils.copyfile(f1full, fdest)
|
||||
if ext == '.bb':
|
||||
if f1 in self.bbhandler.cooker.collection.appendlist:
|
||||
appends = self.bbhandler.cooker.collection.appendlist[f1]
|
||||
if f1 in self.bbhandler.cooker.appendlist:
|
||||
appends = self.bbhandler.cooker.appendlist[f1]
|
||||
if appends:
|
||||
logger.plain(' Applying appends to %s' % fdest )
|
||||
for appendname in appends:
|
||||
@@ -406,9 +405,9 @@ build results (as the layer priority order has effectively changed).
|
||||
appended_recipes.append(f1)
|
||||
|
||||
# Take care of when some layers are excluded and yet we have included bbappends for those recipes
|
||||
for recipename in self.bbhandler.cooker.collection.appendlist.iterkeys():
|
||||
for recipename in self.bbhandler.cooker.appendlist.iterkeys():
|
||||
if recipename not in appended_recipes:
|
||||
appends = self.bbhandler.cooker.collection.appendlist[recipename]
|
||||
appends = self.bbhandler.cooker.appendlist[recipename]
|
||||
first_append = None
|
||||
for appendname in appends:
|
||||
layer = layer_path_match(appendname)
|
||||
@@ -426,7 +425,7 @@ build results (as the layer priority order has effectively changed).
|
||||
# have come from)
|
||||
first_regex = None
|
||||
layerdir = layers[0]
|
||||
for layername, pattern, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layername, pattern, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(os.path.join(layerdir, 'test')):
|
||||
first_regex = regex
|
||||
break
|
||||
@@ -456,28 +455,13 @@ build results (as the layer priority order has effectively changed).
|
||||
logger.warning("File %s does not match the flattened layer's BBFILES setting, you may need to edit conf/layer.conf or move the file elsewhere" % f1full)
|
||||
|
||||
def get_file_layer(self, filename):
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(filename):
|
||||
for layerdir in self.bblayers:
|
||||
if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
|
||||
if regex.match(os.path.join(layerdir, 'test')):
|
||||
return self.get_layer_name(layerdir)
|
||||
return "?"
|
||||
|
||||
def get_file_layerdir(self, filename):
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
if regex.match(filename):
|
||||
for layerdir in self.bblayers:
|
||||
if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
|
||||
return layerdir
|
||||
return "?"
|
||||
|
||||
def remove_layer_prefix(self, f):
|
||||
"""Remove the layer_dir prefix, e.g., f = /path/to/layer_dir/foo/blah, the
|
||||
return value will be: layer_dir/foo/blah"""
|
||||
f_layerdir = self.get_file_layerdir(f)
|
||||
prefix = os.path.join(os.path.dirname(f_layerdir), '')
|
||||
return f[len(prefix):] if f.startswith(prefix) else f
|
||||
|
||||
def get_layer_name(self, layerdir):
|
||||
return os.path.basename(layerdir.rstrip(os.sep))
|
||||
|
||||
@@ -498,7 +482,7 @@ usage: show-appends
|
||||
Recipes are listed with the bbappends that apply to them as subitems.
|
||||
"""
|
||||
self.bbhandler.prepare()
|
||||
if not self.bbhandler.cooker.collection.appendlist:
|
||||
if not self.bbhandler.cooker.appendlist:
|
||||
logger.plain('No append files found')
|
||||
return
|
||||
|
||||
@@ -515,7 +499,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
|
||||
filenames = self.bbhandler.cooker_data.pkg_pn[pn]
|
||||
|
||||
best = bb.providers.findBestProvider(pn,
|
||||
self.bbhandler.config_data,
|
||||
self.bbhandler.cooker.configuration.data,
|
||||
self.bbhandler.cooker_data,
|
||||
self.bbhandler.cooker_data.pkg_pn)
|
||||
best_filename = os.path.basename(best[3])
|
||||
@@ -550,171 +534,13 @@ Recipes are listed with the bbappends that apply to them as subitems.
|
||||
continue
|
||||
|
||||
basename = os.path.basename(filename)
|
||||
appends = self.bbhandler.cooker.collection.appendlist.get(basename)
|
||||
appends = self.bbhandler.cooker.appendlist.get(basename)
|
||||
if appends:
|
||||
appended.append((basename, list(appends)))
|
||||
else:
|
||||
notappended.append(basename)
|
||||
return appended, notappended
|
||||
|
||||
def do_show_cross_depends(self, args):
|
||||
"""figure out the dependency between recipes that crosses a layer boundary.
|
||||
|
||||
usage: show-cross-depends [-f]
|
||||
|
||||
Figure out the dependency between recipes that crosses a layer boundary.
|
||||
|
||||
Options:
|
||||
-f show full file path
|
||||
|
||||
NOTE:
|
||||
The .bbappend file can impact the dependency.
|
||||
"""
|
||||
self.bbhandler.prepare()
|
||||
|
||||
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))
|
||||
self.require_re = re.compile(r"require\s+(.+)")
|
||||
self.include_re = re.compile(r"include\s+(.+)")
|
||||
self.inherit_re = re.compile(r"inherit\s+(.+)")
|
||||
|
||||
# The bb's DEPENDS and RDEPENDS
|
||||
for f in pkg_fn:
|
||||
f = bb.cache.Cache.virtualfn2realfn(f)[0]
|
||||
# Get the layername that the file is in
|
||||
layername = self.get_file_layer(f)
|
||||
|
||||
# The DEPENDS
|
||||
deps = self.bbhandler.cooker_data.deps[f]
|
||||
for pn in deps:
|
||||
if pn in self.bbhandler.cooker_data.pkg_pn:
|
||||
best = bb.providers.findBestProvider(pn,
|
||||
self.bbhandler.config_data,
|
||||
self.bbhandler.cooker_data,
|
||||
self.bbhandler.cooker_data.pkg_pn)
|
||||
self.check_cross_depends("DEPENDS", layername, f, best[3], show_filenames)
|
||||
|
||||
# The RDPENDS
|
||||
all_rdeps = self.bbhandler.cooker_data.rundeps[f].values()
|
||||
# Remove the duplicated or null one.
|
||||
sorted_rdeps = {}
|
||||
# The all_rdeps is the list in list, so we need two for loops
|
||||
for k1 in all_rdeps:
|
||||
for k2 in k1:
|
||||
sorted_rdeps[k2] = 1
|
||||
all_rdeps = sorted_rdeps.keys()
|
||||
for rdep in all_rdeps:
|
||||
all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep)
|
||||
if all_p:
|
||||
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, show_filenames)
|
||||
|
||||
# The inherit class
|
||||
cls_re = re.compile('classes/')
|
||||
if f in self.bbhandler.cooker_data.inherits:
|
||||
inherits = self.bbhandler.cooker_data.inherits[f]
|
||||
for cls in inherits:
|
||||
# The inherits' format is [classes/cls, /path/to/classes/cls]
|
||||
# ignore the classes/cls.
|
||||
if not cls_re.match(cls):
|
||||
inherit_layername = self.get_file_layer(cls)
|
||||
if inherit_layername != layername:
|
||||
if not show_filenames:
|
||||
f_short = self.remove_layer_prefix(f)
|
||||
cls = self.remove_layer_prefix(cls)
|
||||
else:
|
||||
f_short = f
|
||||
logger.plain("%s inherits %s" % (f_short, cls))
|
||||
|
||||
# The 'require/include xxx' in the bb file
|
||||
pv_re = re.compile(r"\${PV}")
|
||||
fnfile = open(f, 'r')
|
||||
line = fnfile.readline()
|
||||
while line:
|
||||
m, keyword = self.match_require_include(line)
|
||||
# Found the 'require/include xxxx'
|
||||
if m:
|
||||
needed_file = m.group(1)
|
||||
# Replace the ${PV} with the real PV
|
||||
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, show_filenames)
|
||||
line = fnfile.readline()
|
||||
fnfile.close()
|
||||
|
||||
# The "require/include xxx" in conf/machine/*.conf, .inc and .bbclass
|
||||
conf_re = re.compile(".*/conf/machine/[^\/]*\.conf$")
|
||||
inc_re = re.compile(".*\.inc$")
|
||||
# The "inherit xxx" in .bbclass
|
||||
bbclass_re = re.compile(".*\.bbclass$")
|
||||
for layerdir in self.bblayers:
|
||||
layername = self.get_layer_name(layerdir)
|
||||
for dirpath, dirnames, filenames in os.walk(layerdir):
|
||||
for name in filenames:
|
||||
f = os.path.join(dirpath, name)
|
||||
s = conf_re.match(f) or inc_re.match(f) or bbclass_re.match(f)
|
||||
if s:
|
||||
ffile = open(f, 'r')
|
||||
line = ffile.readline()
|
||||
while line:
|
||||
m, keyword = self.match_require_include(line)
|
||||
# Only bbclass has the "inherit xxx" here.
|
||||
bbclass=""
|
||||
if not m and f.endswith(".bbclass"):
|
||||
m, keyword = self.match_inherit(line)
|
||||
bbclass=".bbclass"
|
||||
# Find a 'require/include xxxx'
|
||||
if m:
|
||||
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):
|
||||
"""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:
|
||||
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")
|
||||
|
||||
def match_require_include(self, line):
|
||||
"""Match the require/include xxx line"""
|
||||
m = self.require_re.match(line)
|
||||
keyword = "requires"
|
||||
if not m:
|
||||
m = self.include_re.match(line)
|
||||
keyword = "includes"
|
||||
return (m, keyword)
|
||||
|
||||
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:
|
||||
if not show_filenames:
|
||||
f = self.remove_layer_prefix(f)
|
||||
best_realfn = self.remove_layer_prefix(best_realfn)
|
||||
|
||||
logger.plain("%s %s %s" % (f, keyword, best_realfn))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]) or 0)
|
||||
|
||||
119
bitbake/bin/bitbake-runtask
Executable file
119
bitbake/bin/bitbake-runtask
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
from bb import fetch2
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||
|
||||
class BBConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self, **options):
|
||||
self.data = {}
|
||||
self.file = []
|
||||
self.cmd = None
|
||||
self.dump_signatures = True
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.parse_only = True
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
try:
|
||||
return super(BBConfiguration, self).__getattribute__(attribute)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
_warnings_showwarning = warnings.showwarning
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Display python warning messages using bb.msg"""
|
||||
if file is not None:
|
||||
if _warnings_showwarning is not None:
|
||||
_warnings_showwarning(message, category, filename, lineno, file, line)
|
||||
else:
|
||||
s = warnings.formatwarning(message, category, filename, lineno)
|
||||
s = s.split("\n")[0]
|
||||
bb.msg.warn(None, s)
|
||||
|
||||
warnings.showwarning = _showwarning
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
import bb.event
|
||||
import bb.cooker
|
||||
|
||||
buildfile = sys.argv[1]
|
||||
taskname = sys.argv[2]
|
||||
if len(sys.argv) >= 4:
|
||||
dryrun = sys.argv[3]
|
||||
else:
|
||||
dryrun = False
|
||||
if len(sys.argv) >= 5:
|
||||
hashfile = sys.argv[4]
|
||||
p = pickle.Unpickler(file(hashfile, "rb"))
|
||||
hashdata = p.load()
|
||||
else:
|
||||
hashdata = None
|
||||
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
#An example to make debug log messages show up
|
||||
#bb.msg.init_msgconfig(True, 3, [])
|
||||
|
||||
console = logging.StreamHandler(sys.stdout)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
|
||||
def worker_fire(event, d):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
console.handle(event)
|
||||
bb.event.worker_fire = worker_fire
|
||||
bb.event.worker_pid = os.getpid()
|
||||
|
||||
initialenv = os.environ.copy()
|
||||
config = BBConfiguration()
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
pass
|
||||
|
||||
cooker = bb.cooker.BBCooker(config, register_idle_function, initialenv)
|
||||
config_data = cooker.configuration.data
|
||||
cooker.status = config_data
|
||||
cooker.handleCollections(config_data.getVar("BBFILE_COLLECTIONS", 1))
|
||||
|
||||
fn, cls = bb.cache.Cache.virtualfn2realfn(buildfile)
|
||||
buildfile = cooker.matchFile(fn)
|
||||
fn = bb.cache.Cache.realfn2virtual(buildfile, cls)
|
||||
|
||||
cooker.buildSetVars()
|
||||
|
||||
# Load data into the cache for fn and parse the loaded cache data
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, cooker.get_file_appends(fn), cooker.configuration.data)
|
||||
|
||||
if taskname.endswith("_setscene"):
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
if hashdata:
|
||||
bb.parse.siggen.set_taskdata(hashdata["hashes"], hashdata["deps"])
|
||||
for h in hashdata["hashes"]:
|
||||
the_data.setVar("BBHASH_%s" % h, hashdata["hashes"][h])
|
||||
for h in hashdata["deps"]:
|
||||
the_data.setVar("BBHASHDEPS_%s" % h, hashdata["deps"][h])
|
||||
|
||||
ret = 0
|
||||
if dryrun != "True":
|
||||
ret = bb.build.exec_task(fn, taskname, the_data)
|
||||
sys.exit(ret)
|
||||
|
||||
@@ -1,364 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
from bb import fetch2
|
||||
import logging
|
||||
import bb
|
||||
import select
|
||||
import errno
|
||||
import signal
|
||||
|
||||
# Users shouldn't be running this code directly
|
||||
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)
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||
|
||||
|
||||
worker_pipe = sys.stdout.fileno()
|
||||
bb.utils.nonblockingfd(worker_pipe)
|
||||
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
if 0:
|
||||
# Code to write out a log file of all events passing through the worker
|
||||
logfilename = "/tmp/workerlogfile"
|
||||
format_str = "%(levelname)s: %(message)s"
|
||||
conlogformat = bb.msg.BBLogFormatter(format_str)
|
||||
consolelog = logging.FileHandler(logfilename)
|
||||
bb.msg.addDefaultlogFilter(consolelog)
|
||||
consolelog.setFormatter(conlogformat)
|
||||
logger.addHandler(consolelog)
|
||||
|
||||
worker_queue = ""
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_fire_prepickled(data)
|
||||
|
||||
def worker_fire_prepickled(event):
|
||||
global worker_queue
|
||||
|
||||
worker_queue = worker_queue + event
|
||||
worker_flush()
|
||||
|
||||
def worker_flush():
|
||||
global worker_queue, worker_pipe
|
||||
|
||||
if not worker_queue:
|
||||
return
|
||||
|
||||
try:
|
||||
written = os.write(worker_pipe, worker_queue)
|
||||
worker_queue = worker_queue[written:]
|
||||
except (IOError, OSError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
|
||||
def worker_child_fire(event, d):
|
||||
global worker_pipe
|
||||
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_pipe.write(data)
|
||||
|
||||
bb.event.worker_fire = worker_fire
|
||||
|
||||
lf = None
|
||||
#lf = open("/tmp/workercommandlog", "w+")
|
||||
def workerlog_write(msg):
|
||||
if lf:
|
||||
lf.write(msg)
|
||||
lf.flush()
|
||||
|
||||
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...
|
||||
|
||||
envbackup = {}
|
||||
fakeenv = {}
|
||||
umask = None
|
||||
|
||||
taskdep = workerdata["taskdeps"][fn]
|
||||
if 'umask' in taskdep and taskname in taskdep['umask']:
|
||||
# umask might come in as a number or text string..
|
||||
try:
|
||||
umask = int(taskdep['umask'][taskname],8)
|
||||
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:
|
||||
envvars = (workerdata["fakerootenv"][fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
fakedirs = (workerdata["fakerootdirs"][fn] or "").split()
|
||||
for p in fakedirs:
|
||||
bb.utils.mkdirhier(p)
|
||||
logger.debug(2, 'Running %s:%s under fakeroot, fakedirs: %s' %
|
||||
(fn, taskname, ', '.join(fakedirs)))
|
||||
else:
|
||||
envvars = (workerdata["fakerootnoenv"][fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
try:
|
||||
pipein, pipeout = os.pipe()
|
||||
pipein = os.fdopen(pipein, 'rb', 4096)
|
||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||
pid = os.fork()
|
||||
except OSError as e:
|
||||
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
|
||||
if pid == 0:
|
||||
global worker_pipe
|
||||
pipein.close()
|
||||
|
||||
# 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
|
||||
os.setpgid(0, 0)
|
||||
# No stdin
|
||||
newsi = os.open(os.devnull, os.O_RDWR)
|
||||
os.dup2(newsi, sys.stdin.fileno())
|
||||
|
||||
if umask:
|
||||
os.umask(umask)
|
||||
|
||||
data.setVar("BB_WORKERCONTEXT", "1")
|
||||
data.setVar("BUILDNAME", workerdata["buildname"])
|
||||
data.setVar("DATE", workerdata["date"])
|
||||
data.setVar("TIME", workerdata["time"])
|
||||
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
|
||||
exports = bb.data.exported_vars(the_data)
|
||||
bb.utils.empty_environment()
|
||||
for e, v in exports:
|
||||
os.environ[e] = v
|
||||
for e in fakeenv:
|
||||
os.environ[e] = fakeenv[e]
|
||||
the_data.setVar(e, fakeenv[e])
|
||||
the_data.setVarFlag(e, 'export', "1")
|
||||
|
||||
if quieterrors:
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
except Exception as exc:
|
||||
if not quieterrors:
|
||||
logger.critical(str(exc))
|
||||
os._exit(1)
|
||||
try:
|
||||
if not cfg.dry_run:
|
||||
ret = bb.build.exec_task(fn, taskname, the_data, cfg.profile)
|
||||
os._exit(ret)
|
||||
except:
|
||||
os._exit(1)
|
||||
else:
|
||||
for key, value in envbackup.iteritems():
|
||||
if value is None:
|
||||
del os.environ[key]
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
return pid, pipein, pipeout
|
||||
|
||||
class runQueueWorkerPipe():
|
||||
"""
|
||||
Abstraction for a pipe between a worker thread and the worker server
|
||||
"""
|
||||
def __init__(self, pipein, pipeout):
|
||||
self.input = pipein
|
||||
if pipeout:
|
||||
pipeout.close()
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
|
||||
def read(self):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read(102400)
|
||||
except (OSError, IOError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
|
||||
end = len(self.queue)
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1:
|
||||
worker_fire_prepickled(self.queue[:index+8])
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find("</event>")
|
||||
return (end > start)
|
||||
|
||||
def close(self):
|
||||
while self.read():
|
||||
continue
|
||||
if len(self.queue) > 0:
|
||||
print("Warning, worker child left partial message: %s" % self.queue)
|
||||
self.input.close()
|
||||
|
||||
normalexit = False
|
||||
|
||||
class BitbakeWorker(object):
|
||||
def __init__(self, din):
|
||||
self.input = din
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
self.cookercfg = None
|
||||
self.databuilder = None
|
||||
self.data = None
|
||||
self.build_pids = {}
|
||||
self.build_pipes = {}
|
||||
|
||||
def serve(self):
|
||||
while True:
|
||||
(ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
|
||||
if self.input in ready or len(self.queue):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read()
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
end = len(self.queue)
|
||||
self.handle_item("cookerconfig", self.handle_cookercfg)
|
||||
self.handle_item("workerdata", self.handle_workerdata)
|
||||
self.handle_item("runtask", self.handle_runtask)
|
||||
self.handle_item("finishnow", self.handle_finishnow)
|
||||
self.handle_item("ping", self.handle_ping)
|
||||
self.handle_item("quit", self.handle_quit)
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
if len(self.build_pids):
|
||||
self.process_waitpid()
|
||||
worker_flush()
|
||||
|
||||
|
||||
def handle_item(self, item, func):
|
||||
if self.queue.startswith("<" + item + ">"):
|
||||
index = self.queue.find("</" + item + ">")
|
||||
while index != -1:
|
||||
func(self.queue[(len(item) + 2):index])
|
||||
self.queue = self.queue[(index + len(item) + 3):]
|
||||
index = self.queue.find("</" + item + ">")
|
||||
|
||||
def handle_cookercfg(self, data):
|
||||
self.cookercfg = pickle.loads(data)
|
||||
self.databuilder = bb.cookerdata.CookerDataBuilder(self.cookercfg, worker=True)
|
||||
self.databuilder.parseBaseConfiguration()
|
||||
self.data = self.databuilder.data
|
||||
|
||||
def handle_workerdata(self, data):
|
||||
self.workerdata = pickle.loads(data)
|
||||
bb.msg.loggerDefaultDebugLevel = self.workerdata["logdefaultdebug"]
|
||||
bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
|
||||
bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
|
||||
bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
|
||||
self.data.setVar("PRSERV_HOST", self.workerdata["prhost"])
|
||||
|
||||
def handle_ping(self, _):
|
||||
workerlog_write("Handling ping\n")
|
||||
|
||||
logger.warn("Pong from bitbake-worker!")
|
||||
|
||||
def handle_quit(self, data):
|
||||
workerlog_write("Handling quit\n")
|
||||
|
||||
global normalexit
|
||||
normalexit = True
|
||||
sys.exit(0)
|
||||
|
||||
def handle_runtask(self, 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, quieterrors)
|
||||
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
|
||||
|
||||
def process_waitpid(self):
|
||||
"""
|
||||
Return none is there are no processes awaiting result collection, otherwise
|
||||
collect the process exit codes and close the information pipe.
|
||||
"""
|
||||
try:
|
||||
pid, status = os.waitpid(-1, os.WNOHANG)
|
||||
if pid == 0 or os.WIFSTOPPED(status):
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
|
||||
|
||||
if os.WIFEXITED(status):
|
||||
status = os.WEXITSTATUS(status)
|
||||
elif os.WIFSIGNALED(status):
|
||||
# Per shell conventions for $?, when a process exits due to
|
||||
# a signal, we return an exit code of 128 + SIGNUM
|
||||
status = 128 + os.WTERMSIG(status)
|
||||
|
||||
task = self.build_pids[pid]
|
||||
del self.build_pids[pid]
|
||||
|
||||
self.build_pipes[pid].close()
|
||||
del self.build_pipes[pid]
|
||||
|
||||
worker_fire_prepickled("<exitcode>" + pickle.dumps((task, status)) + "</exitcode>")
|
||||
|
||||
def handle_finishnow(self, _):
|
||||
if self.build_pids:
|
||||
logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
|
||||
for k, v in self.build_pids.iteritems():
|
||||
try:
|
||||
os.kill(-k, signal.SIGTERM)
|
||||
os.waitpid(-1, 0)
|
||||
except:
|
||||
pass
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
try:
|
||||
worker = BitbakeWorker(sys.stdin)
|
||||
worker.serve()
|
||||
except BaseException as e:
|
||||
if not normalexit:
|
||||
import traceback
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
sys.stderr.write(str(e))
|
||||
while len(worker_queue):
|
||||
worker_flush()
|
||||
workerlog_write("exitting")
|
||||
sys.exit(0)
|
||||
|
||||
@@ -14,7 +14,7 @@ endif
|
||||
au BufNewFile,BufRead *.{bb,bbappend,bbclass} set filetype=bitbake
|
||||
|
||||
" .inc
|
||||
au BufNewFile,BufRead *.inc set filetype=bitbake
|
||||
au BufNewFile,BufRead *.inc set filetype=bitbake
|
||||
|
||||
" .conf
|
||||
au BufNewFile,BufRead *.conf
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
set sts=4 sw=4 et
|
||||
set cms=#%s
|
||||
|
||||
@@ -15,7 +15,7 @@ if &compatible || v:version < 600
|
||||
endif
|
||||
|
||||
fun! <SID>GetUserName()
|
||||
let l:user_name = system("git config --get user.name")
|
||||
let l:user_name = system("git-config --get user.name")
|
||||
if v:shell_error
|
||||
return "Unknow User"
|
||||
else
|
||||
@@ -23,7 +23,7 @@ fun! <SID>GetUserName()
|
||||
endfun
|
||||
|
||||
fun! <SID>GetUserEmail()
|
||||
let l:user_email = system("git config --get user.email")
|
||||
let l:user_email = system("git-config --get user.email")
|
||||
if v:shell_error
|
||||
return "unknow@user.org"
|
||||
else
|
||||
|
||||
@@ -44,8 +44,8 @@ syn match bbArrayBrackets "[\[\]]" contained
|
||||
|
||||
" BitBake strings
|
||||
syn match bbContinue "\\$"
|
||||
syn region bbString matchgroup=bbQuote start=+"+ skip=+\\$+ end=+"+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+'+ skip=+\\$+ end=+'+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+"+ skip=+\\$+ excludenl end=+"+ contained keepend contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+'+ skip=+\\$+ excludenl end=+'+ contained keepend contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
|
||||
" Vars definition
|
||||
syn match bbExport "^export" nextgroup=bbIdentifier skipwhite
|
||||
@@ -55,11 +55,11 @@ syn match bbVarDeref "${[a-zA-Z0-9\-_\.\/\+]\+}" contained
|
||||
syn match bbVarEq "\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)" contained nextgroup=bbVarValue
|
||||
syn match bbVarDef "^\(export\s*\)\?\([a-zA-Z0-9\-_\.\/\+]\+\(_[${}a-zA-Z0-9\-_\.\/\+]\+\)\?\)\s*\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)\@=" contains=bbExportFlag,bbIdentifier,bbVarDeref nextgroup=bbVarEq
|
||||
syn match bbVarValue ".*$" contained contains=bbString,bbVarDeref,bbVarPyValue
|
||||
syn region bbVarPyValue start=+${@+ skip=+\\$+ end=+}+ contained contains=@python
|
||||
syn region bbVarPyValue start=+${@+ skip=+\\$+ excludenl end=+}+ contained contains=@python
|
||||
|
||||
" Vars metadata flags
|
||||
syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag
|
||||
syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(=\|+=\|=+\|?=\)\@=" contained contains=bbIdentifier nextgroup=bbVarEq
|
||||
syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(=\)\@=" keepend excludenl contained contains=bbIdentifier nextgroup=bbVarEq
|
||||
|
||||
" Includes and requires
|
||||
syn keyword bbInclude inherit include require contained
|
||||
@@ -83,16 +83,13 @@ if exists("b:current_syntax")
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
syn keyword bbShFakeRootFlag fakeroot contained
|
||||
syn match bbShFuncDef "^\(fakeroot\s*\)\?\([0-9A-Za-z_${}-]\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbFunction,bbVarDeref,bbDelimiter nextgroup=bbShFuncRegion skipwhite
|
||||
syn region bbShFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@shell
|
||||
|
||||
" Python value inside shell functions
|
||||
syn region shDeref start=+${@+ skip=+\\$+ excludenl end=+}+ contained contains=@python
|
||||
syn match bbShFuncDef "^\(fakeroot\s*\)\?\([0-9A-Za-z_-]\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbFunction,bbDelimiter nextgroup=bbShFuncRegion skipwhite
|
||||
syn region bbShFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@shell
|
||||
|
||||
" BitBake python metadata
|
||||
syn keyword bbPyFlag python contained
|
||||
syn match bbPyFuncDef "^\(python\s\+\)\([0-9A-Za-z_${}-]\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPyFlag,bbFunction,bbVarDeref,bbDelimiter nextgroup=bbPyFuncRegion skipwhite
|
||||
syn region bbPyFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@python
|
||||
syn match bbPyFuncDef "^\(python\s\+\)\([0-9A-Za-z_-]\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPyFlag,bbFunction,bbDelimiter nextgroup=bbPyFuncRegion skipwhite
|
||||
syn region bbPyFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@python
|
||||
|
||||
" BitBake 'def'd python functions
|
||||
syn keyword bbPyDef def contained
|
||||
|
||||
@@ -103,24 +103,6 @@ Show debug logging for the specified logging domains
|
||||
.TP
|
||||
.B \-P, \-\-profile
|
||||
profile the command and print a report
|
||||
.TP
|
||||
.B \-uUI, \-\-ui=UI
|
||||
User interface to use. Currently, hob, depexp, goggle or ncurses can be specified as UI.
|
||||
.TP
|
||||
.B \-tSERVERTYPE, \-\-servertype=SERVERTYPE
|
||||
Choose which server to use, none, process or xmlrpc.
|
||||
.TP
|
||||
.B \-\-revisions-changed
|
||||
Set the exit code depending on whether upstream floating revisions have changed or not.
|
||||
.TP
|
||||
.B \-\-server-only
|
||||
Run bitbake without UI, the frontend can connect with bitbake server itself.
|
||||
.TP
|
||||
.B \-BBIND, \-\-bind=BIND
|
||||
The name/address for the bitbake server to bind to.
|
||||
.TP
|
||||
.B \-\-no\-setscene
|
||||
Do not run any setscene tasks, forces builds.
|
||||
|
||||
.SH ENVIRONMENT VARIABLES
|
||||
bitbake uses the following environment variables to control its
|
||||
|
||||
@@ -137,24 +137,6 @@ share common metadata between many packages.</para></listitem>
|
||||
<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>
|
||||
@@ -554,7 +536,7 @@ options:
|
||||
<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>.
|
||||
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>
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
__version__ = "1.20.0"
|
||||
__version__ = "1.17.0"
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2, 7, 3):
|
||||
raise RuntimeError("Sorry, python 2.7.3 or later is required for this version of bitbake")
|
||||
if sys.version_info < (2, 6, 0):
|
||||
raise RuntimeError("Sorry, python 2.6.0 or later is required for this version of bitbake")
|
||||
|
||||
|
||||
class BBHandledException(Exception):
|
||||
|
||||
@@ -34,7 +34,7 @@ import bb
|
||||
import bb.msg
|
||||
import bb.process
|
||||
from contextlib import nested
|
||||
from bb import event, utils
|
||||
from bb import data, event, utils
|
||||
|
||||
bblogger = logging.getLogger('BitBake')
|
||||
logger = logging.getLogger('BitBake.Build')
|
||||
@@ -60,7 +60,7 @@ class FuncFailed(Exception):
|
||||
|
||||
def __str__(self):
|
||||
if self.logfile and os.path.exists(self.logfile):
|
||||
msg = ("%s (log file is located at %s)" %
|
||||
msg = ("%s (see %s for further information)" %
|
||||
(self.msg, self.logfile))
|
||||
else:
|
||||
msg = self.msg
|
||||
@@ -69,12 +69,9 @@ class FuncFailed(Exception):
|
||||
class TaskBase(event.Event):
|
||||
"""Base class for task events"""
|
||||
|
||||
def __init__(self, t, logfile, d):
|
||||
def __init__(self, t, d ):
|
||||
self._task = t
|
||||
self._package = d.getVar("PF", True)
|
||||
self.taskfile = d.getVar("FILE", True)
|
||||
self.taskname = self._task
|
||||
self.logfile = logfile
|
||||
event.Event.__init__(self)
|
||||
self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
|
||||
|
||||
@@ -99,11 +96,16 @@ class TaskFailed(TaskBase):
|
||||
"""Task execution failed"""
|
||||
|
||||
def __init__(self, task, logfile, metadata, errprinted = False):
|
||||
self.logfile = logfile
|
||||
self.errprinted = errprinted
|
||||
super(TaskFailed, self).__init__(task, logfile, metadata)
|
||||
super(TaskFailed, self).__init__(task, metadata)
|
||||
|
||||
class TaskFailedSilent(TaskBase):
|
||||
"""Task execution failed (silently)"""
|
||||
def __init__(self, task, logfile, metadata):
|
||||
self.logfile = logfile
|
||||
super(TaskFailedSilent, self).__init__(task, metadata)
|
||||
|
||||
def getDisplayName(self):
|
||||
# Don't need to tell the user it was silent
|
||||
return "Failed"
|
||||
@@ -111,7 +113,7 @@ class TaskFailedSilent(TaskBase):
|
||||
class TaskInvalid(TaskBase):
|
||||
|
||||
def __init__(self, task, metadata):
|
||||
super(TaskInvalid, self).__init__(task, None, metadata)
|
||||
super(TaskInvalid, self).__init__(task, metadata)
|
||||
self._message = "No such task '%s'" % task
|
||||
|
||||
|
||||
@@ -140,68 +142,54 @@ class LogTee(object):
|
||||
def exec_func(func, d, dirs = None):
|
||||
"""Execute an BB 'function'"""
|
||||
|
||||
body = d.getVar(func)
|
||||
body = data.getVar(func, d)
|
||||
if not body:
|
||||
if body is None:
|
||||
logger.warn("Function %s doesn't exist", func)
|
||||
return
|
||||
|
||||
flags = d.getVarFlags(func)
|
||||
flags = data.getVarFlags(func, d)
|
||||
cleandirs = flags.get('cleandirs')
|
||||
if cleandirs:
|
||||
for cdir in d.expand(cleandirs).split():
|
||||
for cdir in data.expand(cleandirs, d).split():
|
||||
bb.utils.remove(cdir, True)
|
||||
|
||||
if dirs is None:
|
||||
dirs = flags.get('dirs')
|
||||
if dirs:
|
||||
dirs = d.expand(dirs).split()
|
||||
dirs = data.expand(dirs, d).split()
|
||||
|
||||
if dirs:
|
||||
for adir in dirs:
|
||||
bb.utils.mkdirhier(adir)
|
||||
adir = dirs[-1]
|
||||
else:
|
||||
adir = d.getVar('B', True)
|
||||
adir = data.getVar('B', d, 1)
|
||||
bb.utils.mkdirhier(adir)
|
||||
|
||||
ispython = flags.get('python')
|
||||
|
||||
lockflag = flags.get('lockfiles')
|
||||
if lockflag:
|
||||
lockfiles = [d.expand(f) for f in lockflag.split()]
|
||||
lockfiles = [data.expand(f, d) for f in lockflag.split()]
|
||||
else:
|
||||
lockfiles = None
|
||||
|
||||
tempdir = d.getVar('T', True)
|
||||
tempdir = data.getVar('T', d, 1)
|
||||
|
||||
# or func allows items to be executed outside of the normal
|
||||
# task set, such as buildhistory
|
||||
task = d.getVar('BB_RUNTASK', True) or func
|
||||
task = data.getVar('BB_RUNTASK', d, 1) or func
|
||||
if task == func:
|
||||
taskfunc = task
|
||||
else:
|
||||
taskfunc = "%s.%s" % (task, func)
|
||||
|
||||
runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
|
||||
runfmt = data.getVar('BB_RUNFMT', d, 1) or "run.{func}.{pid}"
|
||||
runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
|
||||
runfile = os.path.join(tempdir, runfn)
|
||||
bb.utils.mkdirhier(os.path.dirname(runfile))
|
||||
|
||||
# Setup the courtesy link to the runfn, only for tasks
|
||||
# we create the link 'just' before the run script is created
|
||||
# if we create it after, and if the run script fails, then the
|
||||
# link won't be created as an exception would be fired.
|
||||
if task == func:
|
||||
runlink = os.path.join(tempdir, 'run.{0}'.format(task))
|
||||
if runlink:
|
||||
bb.utils.remove(runlink)
|
||||
|
||||
try:
|
||||
os.symlink(runfn, runlink)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
with bb.utils.fileslocked(lockfiles):
|
||||
if ispython:
|
||||
exec_func_python(func, d, runfile, cwd=adir)
|
||||
@@ -262,37 +250,14 @@ def exec_func_shell(func, d, runfile, cwd=None):
|
||||
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=$?
|
||||
case $ret in
|
||||
0) ;;
|
||||
*) case $BASH_VERSION in
|
||||
"") echo "WARNING: exit code $ret from a shell command.";;
|
||||
*) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from
|
||||
\"$BASH_COMMAND\"";;
|
||||
esac
|
||||
exit $ret
|
||||
esac
|
||||
}
|
||||
trap 'bb_exit_handler' 0
|
||||
set -e
|
||||
''')
|
||||
|
||||
bb.data.emit_func(func, script, d)
|
||||
script.write('#!/bin/sh -e\n')
|
||||
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
|
||||
ret=$?
|
||||
trap '' 0
|
||||
exit $?
|
||||
''')
|
||||
|
||||
os.chmod(runfile, 0775)
|
||||
|
||||
@@ -310,8 +275,7 @@ exit $?
|
||||
bb.debug(2, "Executing shell function %s" % func)
|
||||
|
||||
try:
|
||||
with open(os.devnull, 'r+') as stdin:
|
||||
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile)
|
||||
bb.process.run(cmd, shell=False, stdin=NULL, log=logfile)
|
||||
except bb.process.CmdError:
|
||||
logfn = d.getVar('BB_LOGFILE', True)
|
||||
raise FuncFailed(func, logfn)
|
||||
@@ -319,13 +283,13 @@ exit $?
|
||||
bb.debug(2, "Shell function %s finished" % func)
|
||||
|
||||
def _task_data(fn, task, d):
|
||||
localdata = bb.data.createCopy(d)
|
||||
localdata = data.createCopy(d)
|
||||
localdata.setVar('BB_FILENAME', fn)
|
||||
localdata.setVar('BB_CURRENTTASK', task[3:])
|
||||
localdata.setVar('OVERRIDES', 'task-%s:%s' %
|
||||
(task[3:], d.getVar('OVERRIDES', False)))
|
||||
localdata.finalize()
|
||||
bb.data.expandKeys(localdata)
|
||||
data.expandKeys(localdata)
|
||||
return localdata
|
||||
|
||||
def _exec_task(fn, task, d, quieterr):
|
||||
@@ -334,7 +298,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
Execution of a task involves a bit more setup than executing a function,
|
||||
running it with its own local metadata, and with some useful variables set.
|
||||
"""
|
||||
if not d.getVarFlag(task, 'task'):
|
||||
if not data.getVarFlag(task, 'task', d):
|
||||
event.fire(TaskInvalid(task, d), d)
|
||||
logger.error("No such task: %s" % task)
|
||||
return 1
|
||||
@@ -346,14 +310,6 @@ def _exec_task(fn, task, d, quieterr):
|
||||
if not tempdir:
|
||||
bb.fatal("T variable not set, unable to build")
|
||||
|
||||
# Change nice level if we're asked to
|
||||
nice = localdata.getVar("BB_TASK_NICE_LEVEL", True)
|
||||
if nice:
|
||||
curnice = os.nice(0)
|
||||
nice = int(nice) - curnice
|
||||
newnice = os.nice(nice)
|
||||
logger.debug(1, "Renice to %s " % newnice)
|
||||
|
||||
bb.utils.mkdirhier(tempdir)
|
||||
|
||||
# Determine the logfile to generate
|
||||
@@ -363,11 +319,12 @@ def _exec_task(fn, task, d, quieterr):
|
||||
# Document the order of the tasks...
|
||||
logorder = os.path.join(tempdir, 'log.task_order')
|
||||
try:
|
||||
with open(logorder, 'a') as logorderfile:
|
||||
logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
|
||||
logorderfile = file(logorder, 'a')
|
||||
except OSError:
|
||||
logger.exception("Opening log file '%s'", logorder)
|
||||
pass
|
||||
logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
|
||||
logorderfile.close()
|
||||
|
||||
# Setup the courtesy link to the logfn
|
||||
loglink = os.path.join(tempdir, 'log.{0}'.format(task))
|
||||
@@ -391,10 +348,10 @@ def _exec_task(fn, task, d, quieterr):
|
||||
self.triggered = True
|
||||
|
||||
# Handle logfiles
|
||||
si = open('/dev/null', 'r')
|
||||
si = file('/dev/null', 'r')
|
||||
try:
|
||||
bb.utils.mkdirhier(os.path.dirname(logfn))
|
||||
logfile = open(logfn, 'w')
|
||||
logfile = file(logfn, 'w')
|
||||
except OSError:
|
||||
logger.exception("Opening log file '%s'", logfn)
|
||||
pass
|
||||
@@ -422,7 +379,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
localdata.setVar('BB_LOGFILE', logfn)
|
||||
localdata.setVar('BB_RUNTASK', task)
|
||||
|
||||
event.fire(TaskStarted(task, logfn, localdata), localdata)
|
||||
event.fire(TaskStarted(task, localdata), localdata)
|
||||
try:
|
||||
for func in (prefuncs or '').split():
|
||||
exec_func(func, localdata)
|
||||
@@ -459,34 +416,20 @@ def _exec_task(fn, task, d, quieterr):
|
||||
logger.debug(2, "Zero size logfn %s, removing", logfn)
|
||||
bb.utils.remove(logfn)
|
||||
bb.utils.remove(loglink)
|
||||
event.fire(TaskSucceeded(task, logfn, localdata), localdata)
|
||||
event.fire(TaskSucceeded(task, localdata), localdata)
|
||||
|
||||
if not localdata.getVarFlag(task, 'nostamp') and not localdata.getVarFlag(task, 'selfstamp'):
|
||||
make_stamp(task, localdata)
|
||||
|
||||
return 0
|
||||
|
||||
def exec_task(fn, task, d, profile = False):
|
||||
def exec_task(fn, task, d):
|
||||
try:
|
||||
quieterr = False
|
||||
if d.getVarFlag(task, "quieterrors") is not None:
|
||||
quieterr = True
|
||||
|
||||
if profile:
|
||||
profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
|
||||
try:
|
||||
import cProfile as profile
|
||||
except:
|
||||
import profile
|
||||
prof = profile.Profile()
|
||||
ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
|
||||
prof.dump_stats(profname)
|
||||
bb.utils.process_profilelog(profname)
|
||||
|
||||
return ret
|
||||
else:
|
||||
return _exec_task(fn, task, d, quieterr)
|
||||
|
||||
return _exec_task(fn, task, d, quieterr)
|
||||
except Exception:
|
||||
from traceback import format_exc
|
||||
if not quieterr:
|
||||
@@ -562,13 +505,10 @@ def make_stamp(task, d, file_name = None):
|
||||
"""
|
||||
cleanmask = stamp_cleanmask_internal(task, d, file_name)
|
||||
for mask in cleanmask:
|
||||
# Preserve sigdata files in the stamps directory
|
||||
for name in glob.glob(mask):
|
||||
# Preserve sigdata files in the stamps directory
|
||||
if "sigdata" in name:
|
||||
continue
|
||||
# Preserve taint files in the stamps directory
|
||||
if name.endswith('.taint'):
|
||||
continue
|
||||
os.unlink(name)
|
||||
|
||||
stamp = stamp_internal(task, d, file_name)
|
||||
@@ -576,7 +516,8 @@ def make_stamp(task, d, file_name = None):
|
||||
# change on broken NFS filesystems
|
||||
if stamp:
|
||||
bb.utils.remove(stamp)
|
||||
open(stamp, "w").close()
|
||||
f = open(stamp, "w")
|
||||
f.close()
|
||||
|
||||
# If we're in task context, write out a signature file for each task
|
||||
# as it completes
|
||||
@@ -618,7 +559,7 @@ def stampfile(taskname, d, file_name = None):
|
||||
return stamp_internal(taskname, d, file_name)
|
||||
|
||||
def add_tasks(tasklist, d):
|
||||
task_deps = d.getVar('_task_deps')
|
||||
task_deps = data.getVar('_task_deps', d)
|
||||
if not task_deps:
|
||||
task_deps = {}
|
||||
if not 'tasks' in task_deps:
|
||||
@@ -627,25 +568,24 @@ def add_tasks(tasklist, d):
|
||||
task_deps['parents'] = {}
|
||||
|
||||
for task in tasklist:
|
||||
task = d.expand(task)
|
||||
d.setVarFlag(task, 'task', 1)
|
||||
task = data.expand(task, d)
|
||||
data.setVarFlag(task, 'task', 1, d)
|
||||
|
||||
if not task in task_deps['tasks']:
|
||||
task_deps['tasks'].append(task)
|
||||
|
||||
flags = d.getVarFlags(task)
|
||||
flags = data.getVarFlags(task, d)
|
||||
def getTask(name):
|
||||
if not name in task_deps:
|
||||
task_deps[name] = {}
|
||||
if name in flags:
|
||||
deptask = d.expand(flags[name])
|
||||
deptask = data.expand(flags[name], d)
|
||||
task_deps[name][task] = deptask
|
||||
getTask('depends')
|
||||
getTask('rdepends')
|
||||
getTask('deptask')
|
||||
getTask('rdeptask')
|
||||
getTask('recrdeptask')
|
||||
getTask('recideptask')
|
||||
getTask('nostamp')
|
||||
getTask('fakeroot')
|
||||
getTask('noexec')
|
||||
@@ -653,15 +593,15 @@ def add_tasks(tasklist, d):
|
||||
task_deps['parents'][task] = []
|
||||
if 'deps' in flags:
|
||||
for dep in flags['deps']:
|
||||
dep = d.expand(dep)
|
||||
dep = data.expand(dep, d)
|
||||
task_deps['parents'][task].append(dep)
|
||||
|
||||
# don't assume holding a reference
|
||||
d.setVar('_task_deps', task_deps)
|
||||
data.setVar('_task_deps', task_deps, d)
|
||||
|
||||
def remove_task(task, kill, d):
|
||||
"""Remove an BB 'task'.
|
||||
|
||||
If kill is 1, also remove tasks that depend on this task."""
|
||||
|
||||
d.delVarFlag(task, 'task')
|
||||
data.delVarFlag(task, 'task', d)
|
||||
|
||||
@@ -43,7 +43,7 @@ except ImportError:
|
||||
logger.info("Importing cPickle failed. "
|
||||
"Falling back to a very slow implementation.")
|
||||
|
||||
__cache_version__ = "147"
|
||||
__cache_version__ = "145"
|
||||
|
||||
def getCacheFile(path, filename, data_hash):
|
||||
return os.path.join(path, filename + "." + data_hash)
|
||||
@@ -119,6 +119,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
||||
self.basetaskhashes = self.taskvar('BB_BASEHASH', self.tasks, metadata)
|
||||
self.hashfilename = self.getvar('BB_HASHFILENAME', metadata)
|
||||
|
||||
self.file_depends = metadata.getVar('__depends', False)
|
||||
self.task_deps = metadata.getVar('_task_deps', False) or {'tasks': [], 'parents': {}}
|
||||
|
||||
self.skipped = False
|
||||
@@ -126,6 +127,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
||||
self.pv = self.getvar('PV', metadata)
|
||||
self.pr = self.getvar('PR', metadata)
|
||||
self.defaultpref = self.intvar('DEFAULT_PREFERENCE', metadata)
|
||||
self.broken = self.getvar('BROKEN', metadata)
|
||||
self.not_world = self.getvar('EXCLUDE_FROM_WORLD', metadata)
|
||||
self.stamp = self.getvar('STAMP', metadata)
|
||||
self.stampclean = self.getvar('STAMPCLEAN', metadata)
|
||||
@@ -232,7 +234,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
||||
|
||||
# Collect files we may need for possible world-dep
|
||||
# calculations
|
||||
if not self.not_world:
|
||||
if not self.broken and not self.not_world:
|
||||
cachedata.possible_world.append(fn)
|
||||
|
||||
# create a collection of all targets for sanity checking
|
||||
@@ -524,18 +526,9 @@ class Cache(object):
|
||||
self.remove(fn)
|
||||
return False
|
||||
|
||||
if hasattr(info_array[0], 'file_checksums'):
|
||||
for _, fl in info_array[0].file_checksums.items():
|
||||
for f in fl.split():
|
||||
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
|
||||
|
||||
if appends != info_array[0].appends:
|
||||
logger.debug(2, "Cache: appends for %s changed", fn)
|
||||
logger.debug(2, "%s to %s" % (str(appends), str(info_array[0].appends)))
|
||||
bb.note("%s to %s" % (str(appends), str(info_array[0].appends)))
|
||||
self.remove(fn)
|
||||
return False
|
||||
|
||||
@@ -724,6 +717,7 @@ class CacheData(object):
|
||||
for info in info_array:
|
||||
info.add_cacheData(self, fn)
|
||||
|
||||
|
||||
class MultiProcessCache(object):
|
||||
"""
|
||||
BitBake multi-process cache implementation
|
||||
@@ -745,18 +739,12 @@ class MultiProcessCache(object):
|
||||
self.cachefile = os.path.join(cachedir, self.__class__.cache_file_name)
|
||||
logger.debug(1, "Using cache in '%s'", self.cachefile)
|
||||
|
||||
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
||||
|
||||
try:
|
||||
with open(self.cachefile, "rb") as f:
|
||||
p = pickle.Unpickler(f)
|
||||
data, version = p.load()
|
||||
p = pickle.Unpickler(file(self.cachefile, "rb"))
|
||||
data, version = p.load()
|
||||
except:
|
||||
bb.utils.unlockfile(glf)
|
||||
return
|
||||
|
||||
bb.utils.unlockfile(glf)
|
||||
|
||||
if version != self.__class__.CACHE_VERSION:
|
||||
return
|
||||
|
||||
@@ -793,9 +781,8 @@ class MultiProcessCache(object):
|
||||
i = i + 1
|
||||
continue
|
||||
|
||||
with open(self.cachefile + "-" + str(i), "wb") as f:
|
||||
p = pickle.Pickler(f, -1)
|
||||
p.dump([self.cachedata_extras, self.__class__.CACHE_VERSION])
|
||||
p = pickle.Pickler(file(self.cachefile + "-" + str(i), "wb"), -1)
|
||||
p.dump([self.cachedata_extras, self.__class__.CACHE_VERSION])
|
||||
|
||||
bb.utils.unlockfile(lf)
|
||||
bb.utils.unlockfile(glf)
|
||||
@@ -813,9 +800,8 @@ class MultiProcessCache(object):
|
||||
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
||||
|
||||
try:
|
||||
with open(self.cachefile, "rb") as f:
|
||||
p = pickle.Unpickler(f)
|
||||
data, version = p.load()
|
||||
p = pickle.Unpickler(file(self.cachefile, "rb"))
|
||||
data, version = p.load()
|
||||
except (IOError, EOFError):
|
||||
data, version = None, None
|
||||
|
||||
@@ -825,9 +811,8 @@ class MultiProcessCache(object):
|
||||
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)
|
||||
try:
|
||||
with open(f, "rb") as fd:
|
||||
p = pickle.Unpickler(fd)
|
||||
extradata, version = p.load()
|
||||
p = pickle.Unpickler(file(f, "rb"))
|
||||
extradata, version = p.load()
|
||||
except (IOError, EOFError):
|
||||
extradata, version = self.create_cachedata(), None
|
||||
|
||||
@@ -839,9 +824,8 @@ class MultiProcessCache(object):
|
||||
|
||||
self.compress_keys(data)
|
||||
|
||||
with open(self.cachefile, "wb") as f:
|
||||
p = pickle.Pickler(f, -1)
|
||||
p.dump([data, self.__class__.CACHE_VERSION])
|
||||
p = pickle.Pickler(file(self.cachefile, "wb"), -1)
|
||||
p.dump([data, self.__class__.CACHE_VERSION])
|
||||
|
||||
bb.utils.unlockfile(glf)
|
||||
|
||||
|
||||
@@ -35,22 +35,12 @@ class HobRecipeInfo(RecipeInfoCommon):
|
||||
# such as (bb_cache.dat, bb_extracache_hob.dat)
|
||||
cachefile = "bb_extracache_" + classname +".dat"
|
||||
|
||||
# override this member with the list of extra cache fields
|
||||
# that this class will provide
|
||||
cachefields = ['summary', 'license', 'section',
|
||||
'description', 'homepage', 'bugtracker',
|
||||
'prevision', 'files_info']
|
||||
|
||||
def __init__(self, filename, metadata):
|
||||
|
||||
self.summary = self.getvar('SUMMARY', metadata)
|
||||
self.license = self.getvar('LICENSE', metadata)
|
||||
self.section = self.getvar('SECTION', metadata)
|
||||
self.description = self.getvar('DESCRIPTION', metadata)
|
||||
self.homepage = self.getvar('HOMEPAGE', metadata)
|
||||
self.bugtracker = self.getvar('BUGTRACKER', metadata)
|
||||
self.prevision = self.getvar('PR', metadata)
|
||||
self.files_info = self.getvar('FILES_INFO', metadata)
|
||||
|
||||
@classmethod
|
||||
def init_cacheData(cls, cachedata):
|
||||
@@ -59,17 +49,9 @@ class HobRecipeInfo(RecipeInfoCommon):
|
||||
cachedata.license = {}
|
||||
cachedata.section = {}
|
||||
cachedata.description = {}
|
||||
cachedata.homepage = {}
|
||||
cachedata.bugtracker = {}
|
||||
cachedata.prevision = {}
|
||||
cachedata.files_info = {}
|
||||
|
||||
def add_cacheData(self, cachedata, fn):
|
||||
cachedata.summary[fn] = self.summary
|
||||
cachedata.license[fn] = self.license
|
||||
cachedata.section[fn] = self.section
|
||||
cachedata.description[fn] = self.description
|
||||
cachedata.homepage[fn] = self.homepage
|
||||
cachedata.bugtracker[fn] = self.bugtracker
|
||||
cachedata.prevision[fn] = self.prevision
|
||||
cachedata.files_info[fn] = self.files_info
|
||||
|
||||
@@ -35,7 +35,7 @@ def check_indent(codestr):
|
||||
|
||||
class CodeParserCache(MultiProcessCache):
|
||||
cache_file_name = "bb_codeparser.dat"
|
||||
CACHE_VERSION = 3
|
||||
CACHE_VERSION = 2
|
||||
|
||||
def __init__(self):
|
||||
MultiProcessCache.__init__(self)
|
||||
@@ -100,8 +100,7 @@ class BufferedLogger(Logger):
|
||||
self.buffer = []
|
||||
|
||||
class PythonParser():
|
||||
getvars = ("d.getVar", "bb.data.getVar", "data.getVar", "d.appendVar", "d.prependVar")
|
||||
containsfuncs = ("bb.utils.contains", "base_contains", "oe.utils.contains")
|
||||
getvars = ("d.getVar", "bb.data.getVar", "data.getVar")
|
||||
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
|
||||
|
||||
def warn(self, func, arg):
|
||||
@@ -120,7 +119,7 @@ class PythonParser():
|
||||
|
||||
def visit_Call(self, node):
|
||||
name = self.called_node_name(node.func)
|
||||
if name in self.getvars or name in self.containsfuncs:
|
||||
if name in self.getvars:
|
||||
if isinstance(node.args[0], ast.Str):
|
||||
self.var_references.add(node.args[0].s)
|
||||
else:
|
||||
|
||||
@@ -59,14 +59,11 @@ class Command:
|
||||
# FIXME Add lock for this
|
||||
self.currentAsyncCommand = None
|
||||
|
||||
def runCommand(self, commandline, ro_only = False):
|
||||
def runCommand(self, commandline):
|
||||
command = commandline.pop(0)
|
||||
if hasattr(CommandsSync, command):
|
||||
# Can run synchronous commands straight away
|
||||
command_method = getattr(self.cmds_sync, command)
|
||||
if ro_only:
|
||||
if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'):
|
||||
return None, "Not able to execute not readonly commands in readonly mode"
|
||||
try:
|
||||
result = command_method(self, commandline)
|
||||
except CommandError as exc:
|
||||
@@ -81,18 +78,17 @@ class Command:
|
||||
if command not in CommandsAsync.__dict__:
|
||||
return None, "No such command"
|
||||
self.currentAsyncCommand = (command, commandline)
|
||||
self.cooker.configuration.server_register_idlecallback(self.cooker.runCommands, self.cooker)
|
||||
self.cooker.server_registration_cb(self.cooker.runCommands, self.cooker)
|
||||
return True, None
|
||||
|
||||
def runAsyncCommand(self):
|
||||
try:
|
||||
if self.cooker.state == bb.cooker.state.error:
|
||||
return False
|
||||
if self.currentAsyncCommand is not None:
|
||||
(command, options) = self.currentAsyncCommand
|
||||
commandmethod = getattr(CommandsAsync, command)
|
||||
needcache = getattr( commandmethod, "needcache" )
|
||||
if needcache and self.cooker.state != bb.cooker.state.running:
|
||||
if (needcache and self.cooker.state in
|
||||
(bb.cooker.state.initial, bb.cooker.state.parsing)):
|
||||
self.cooker.updateCache()
|
||||
return True
|
||||
else:
|
||||
@@ -119,14 +115,14 @@ class Command:
|
||||
return False
|
||||
|
||||
def finishAsyncCommand(self, msg=None, code=None):
|
||||
if msg or msg == "":
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.event_data)
|
||||
if msg:
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.configuration.event_data)
|
||||
elif code:
|
||||
bb.event.fire(CommandExit(code), self.cooker.event_data)
|
||||
bb.event.fire(CommandExit(code), self.cooker.configuration.event_data)
|
||||
else:
|
||||
bb.event.fire(CommandCompleted(), self.cooker.event_data)
|
||||
bb.event.fire(CommandCompleted(), self.cooker.configuration.event_data)
|
||||
self.currentAsyncCommand = None
|
||||
self.cooker.finishcommand()
|
||||
|
||||
|
||||
class CommandsSync:
|
||||
"""
|
||||
@@ -139,62 +135,44 @@ class CommandsSync:
|
||||
"""
|
||||
Trigger cooker 'shutdown' mode
|
||||
"""
|
||||
command.cooker.shutdown(False)
|
||||
command.cooker.shutdown()
|
||||
|
||||
def stateForceShutdown(self, command, params):
|
||||
def stateStop(self, command, params):
|
||||
"""
|
||||
Stop the cooker
|
||||
"""
|
||||
command.cooker.shutdown(True)
|
||||
command.cooker.stop()
|
||||
|
||||
def getAllKeysWithFlags(self, command, params):
|
||||
def getCmdLineAction(self, command, params):
|
||||
"""
|
||||
Returns a dump of the global state. Call with
|
||||
variable flags to be retrieved as params.
|
||||
Get any command parsed from the commandline
|
||||
"""
|
||||
flaglist = params[0]
|
||||
return command.cooker.getAllKeysWithFlags(flaglist)
|
||||
getAllKeysWithFlags.readonly = True
|
||||
cmd_action = command.cooker.commandlineAction
|
||||
if cmd_action is None:
|
||||
return None
|
||||
elif 'msg' in cmd_action and cmd_action['msg']:
|
||||
raise CommandError(cmd_action['msg'])
|
||||
else:
|
||||
return cmd_action['action']
|
||||
|
||||
def getVariable(self, command, params):
|
||||
"""
|
||||
Read the value of a variable from data
|
||||
Read the value of a variable from configuration.data
|
||||
"""
|
||||
varname = params[0]
|
||||
expand = True
|
||||
if len(params) > 1:
|
||||
expand = (params[1] == "True")
|
||||
expand = params[1]
|
||||
|
||||
return command.cooker.data.getVar(varname, expand)
|
||||
getVariable.readonly = True
|
||||
return command.cooker.configuration.data.getVar(varname, expand)
|
||||
|
||||
def setVariable(self, command, params):
|
||||
"""
|
||||
Set the value of variable in data
|
||||
Set the value of variable in configuration.data
|
||||
"""
|
||||
varname = params[0]
|
||||
value = str(params[1])
|
||||
command.cooker.data.setVar(varname, value)
|
||||
|
||||
def setConfig(self, command, params):
|
||||
"""
|
||||
Set the value of variable in configuration
|
||||
"""
|
||||
varname = params[0]
|
||||
value = str(params[1])
|
||||
setattr(command.cooker.configuration, varname, value)
|
||||
|
||||
def enableDataTracking(self, command, params):
|
||||
"""
|
||||
Enable history tracking for variables
|
||||
"""
|
||||
command.cooker.enableDataTracking()
|
||||
|
||||
def disableDataTracking(self, command, params):
|
||||
"""
|
||||
Disable history tracking for variables
|
||||
"""
|
||||
command.cooker.disableDataTracking()
|
||||
command.cooker.configuration.data.setVar(varname, value)
|
||||
|
||||
def initCooker(self, command, params):
|
||||
"""
|
||||
@@ -214,7 +192,13 @@ class CommandsSync:
|
||||
Get the CPU count on the bitbake server
|
||||
"""
|
||||
return bb.utils.cpu_count()
|
||||
getCpuCount.readonly = True
|
||||
|
||||
def setConfFilter(self, command, params):
|
||||
"""
|
||||
Set the configuration file parsing filter
|
||||
"""
|
||||
filterfunc = params[0]
|
||||
bb.parse.parse_py.ConfHandler.confFilters.append(filterfunc)
|
||||
|
||||
def matchFile(self, command, params):
|
||||
fMatch = params[0]
|
||||
@@ -224,45 +208,7 @@ class CommandsSync:
|
||||
image = params[0]
|
||||
base_image = params[1]
|
||||
package_queue = params[2]
|
||||
timestamp = params[3]
|
||||
description = params[4]
|
||||
return command.cooker.generateNewImage(image, base_image,
|
||||
package_queue, timestamp, description)
|
||||
|
||||
def ensureDir(self, command, params):
|
||||
directory = params[0]
|
||||
bb.utils.mkdirhier(directory)
|
||||
|
||||
def setVarFile(self, command, params):
|
||||
"""
|
||||
Save a variable in a file; used for saving in a configuration file
|
||||
"""
|
||||
var = params[0]
|
||||
val = params[1]
|
||||
default_file = params[2]
|
||||
op = params[3]
|
||||
command.cooker.modifyConfigurationVar(var, val, default_file, op)
|
||||
|
||||
def removeVarFile(self, command, params):
|
||||
"""
|
||||
Remove a variable declaration from a file
|
||||
"""
|
||||
var = params[0]
|
||||
command.cooker.removeConfigurationVar(var)
|
||||
|
||||
def createConfigFile(self, command, params):
|
||||
"""
|
||||
Create an extra configuration file
|
||||
"""
|
||||
name = params[0]
|
||||
command.cooker.createConfigFile(name)
|
||||
|
||||
def setEventMask(self, command, params):
|
||||
handlerNum = params[0]
|
||||
llevel = params[1]
|
||||
debug_domains = params[2]
|
||||
mask = params[3]
|
||||
return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
|
||||
return command.cooker.generateNewImage(image, base_image, package_queue)
|
||||
|
||||
class CommandsAsync:
|
||||
"""
|
||||
@@ -410,11 +356,19 @@ class CommandsAsync:
|
||||
command.finishAsyncCommand()
|
||||
parseFiles.needcache = True
|
||||
|
||||
def reparseFiles(self, command, params):
|
||||
"""
|
||||
Reparse .bb files
|
||||
"""
|
||||
command.cooker.reparseFiles()
|
||||
command.finishAsyncCommand()
|
||||
reparseFiles.needcache = True
|
||||
|
||||
def compareRevisions(self, command, params):
|
||||
"""
|
||||
Parse the .bb files
|
||||
"""
|
||||
if bb.fetch.fetcher_compare_revisions(command.cooker.data):
|
||||
if bb.fetch.fetcher_compare_revisions(command.cooker.configuration.data):
|
||||
command.finishAsyncCommand(code=1)
|
||||
else:
|
||||
command.finishAsyncCommand()
|
||||
@@ -424,11 +378,9 @@ class CommandsAsync:
|
||||
"""
|
||||
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()
|
||||
prefiles = params[0]
|
||||
postfiles = params[1]
|
||||
command.cooker.parseConfigurationFiles(prefiles, postfiles)
|
||||
command.finishAsyncCommand()
|
||||
parseConfigurationFiles.needcache = False
|
||||
|
||||
@@ -437,7 +389,7 @@ class CommandsAsync:
|
||||
Trigger a certain event
|
||||
"""
|
||||
event = params[0]
|
||||
bb.event.fire(eval(event), command.cooker.data)
|
||||
bb.event.fire(eval(event), command.cooker.configuration.data)
|
||||
command.currentAsyncCommand = None
|
||||
triggerEvent.needcache = False
|
||||
|
||||
|
||||
@@ -1,6 +1,241 @@
|
||||
"""Code pulled from future python versions, here for compatibility"""
|
||||
|
||||
from collections import MutableMapping, KeysView, ValuesView, ItemsView, OrderedDict
|
||||
from functools import total_ordering
|
||||
from collections import MutableMapping, KeysView, ValuesView, ItemsView
|
||||
try:
|
||||
from thread import get_ident as _get_ident
|
||||
except ImportError:
|
||||
from dummy_thread import get_ident as _get_ident
|
||||
|
||||
def total_ordering(cls):
|
||||
"""Class decorator that fills in missing ordering methods"""
|
||||
convert = {
|
||||
'__lt__': [('__gt__', lambda self, other: other < self),
|
||||
('__le__', lambda self, other: not other < self),
|
||||
('__ge__', lambda self, other: not self < other)],
|
||||
'__le__': [('__ge__', lambda self, other: other <= self),
|
||||
('__lt__', lambda self, other: not other <= self),
|
||||
('__gt__', lambda self, other: not self <= other)],
|
||||
'__gt__': [('__lt__', lambda self, other: other > self),
|
||||
('__ge__', lambda self, other: not other > self),
|
||||
('__le__', lambda self, other: not self > other)],
|
||||
'__ge__': [('__le__', lambda self, other: other >= self),
|
||||
('__gt__', lambda self, other: not other >= self),
|
||||
('__lt__', lambda self, other: not self >= other)]
|
||||
}
|
||||
roots = set(dir(cls)) & set(convert)
|
||||
if not roots:
|
||||
raise ValueError('must define at least one ordering operation: < > <= >=')
|
||||
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
|
||||
for opname, opfunc in convert[root]:
|
||||
if opname not in roots:
|
||||
opfunc.__name__ = opname
|
||||
opfunc.__doc__ = getattr(int, opname).__doc__
|
||||
setattr(cls, opname, opfunc)
|
||||
return cls
|
||||
|
||||
class OrderedDict(dict):
|
||||
'Dictionary that remembers insertion order'
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as regular dictionaries.
|
||||
|
||||
# The internal self.__map dict maps keys to links in a doubly linked list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. The signature is the same as
|
||||
regular dictionaries, but keyword arguments are not recommended because
|
||||
their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
|
||||
'od.__setitem__(i, y) <==> od[i]=y'
|
||||
# Setting a new item creates a new link at the end of the linked list,
|
||||
# and the inherited dictionary is updated with the new key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[PREV]
|
||||
last[NEXT] = root[PREV] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__):
|
||||
'od.__delitem__(y) <==> del od[y]'
|
||||
# Deleting an existing item uses self.__map to find the link which gets
|
||||
# removed by updating the links in the predecessor and successor nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[NEXT] = link_next
|
||||
link_next[PREV] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
'od.__iter__() <==> iter(od)'
|
||||
# Traverse the linked list in order.
|
||||
NEXT, KEY = 1, 2
|
||||
root = self.__root
|
||||
curr = root[NEXT]
|
||||
while curr is not root:
|
||||
yield curr[KEY]
|
||||
curr = curr[NEXT]
|
||||
|
||||
def __reversed__(self):
|
||||
'od.__reversed__() <==> reversed(od)'
|
||||
# Traverse the linked list in reverse order.
|
||||
PREV, KEY = 0, 2
|
||||
root = self.__root
|
||||
curr = root[PREV]
|
||||
while curr is not root:
|
||||
yield curr[KEY]
|
||||
curr = curr[PREV]
|
||||
|
||||
def clear(self):
|
||||
'od.clear() -> None. Remove all items from od.'
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
dict.clear(self)
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
'od.keys() -> list of keys in od'
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
'od.values() -> list of values in od'
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
'od.items() -> list of (key, value) pairs in od'
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
'od.iterkeys() -> an iterator over the keys in od'
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
'od.itervalues -> an iterator over the values in od'
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
'od.iteritems -> an iterator over the (key, value) pairs in od'
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
update = MutableMapping.update
|
||||
|
||||
__update = update # let subclasses override update without breaking __init__
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding
|
||||
value. If key is not found, d is returned if given, otherwise KeyError
|
||||
is raised.
|
||||
|
||||
'''
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
key = next(reversed(self) if last else iter(self))
|
||||
value = self.pop(key)
|
||||
return key, value
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
'od.__repr__() <==> repr(od)'
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
'Return state information for pickling'
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
'od.copy() -> a shallow copy of od'
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
|
||||
If not specified, the value defaults to None.
|
||||
|
||||
'''
|
||||
self = cls()
|
||||
for key in iterable:
|
||||
self[key] = value
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
|
||||
while comparison to a regular mapping is order-insensitive.
|
||||
|
||||
'''
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self)==len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
'od.__ne__(y) <==> od!=y'
|
||||
return not self == other
|
||||
|
||||
# -- the following methods support python 3.x style dictionary views --
|
||||
|
||||
def viewkeys(self):
|
||||
"od.viewkeys() -> a set-like object providing a view on od's keys"
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self):
|
||||
"od.viewvalues() -> an object providing a view on od's values"
|
||||
return ValuesView(self)
|
||||
|
||||
def viewitems(self):
|
||||
"od.viewitems() -> a set-like object providing a view on od's items"
|
||||
return ItemsView(self)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,304 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# Copyright (C) 2003, 2004 Chris Larson
|
||||
# Copyright (C) 2003, 2004 Phil Blundell
|
||||
# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2005 Holger Hans Peter Freyther
|
||||
# Copyright (C) 2005 ROAD GmbH
|
||||
# Copyright (C) 2006 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os, sys
|
||||
from functools import wraps
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
import bb.parse
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
parselog = logging.getLogger("BitBake.Parsing")
|
||||
|
||||
class ConfigParameters(object):
|
||||
def __init__(self):
|
||||
self.options, targets = self.parseCommandLine()
|
||||
self.environment = self.parseEnvironment()
|
||||
|
||||
self.options.pkgs_to_build = targets or []
|
||||
|
||||
self.options.tracking = False
|
||||
if hasattr(self.options, "show_environment") and self.options.show_environment:
|
||||
self.options.tracking = True
|
||||
|
||||
for key, val in self.options.__dict__.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
def parseCommandLine(self):
|
||||
raise Exception("Caller must implement commandline option parsing")
|
||||
|
||||
def parseEnvironment(self):
|
||||
return os.environ.copy()
|
||||
|
||||
def updateFromServer(self, server):
|
||||
if not self.options.cmd:
|
||||
defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
|
||||
if error:
|
||||
raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
|
||||
self.options.cmd = defaulttask or "build"
|
||||
_, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
|
||||
if error:
|
||||
raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
|
||||
|
||||
if not self.options.pkgs_to_build:
|
||||
bbpkgs, error = server.runCommand(["getVariable", "BBPKGS"])
|
||||
if error:
|
||||
raise Exception("Unable to get the value of BBPKGS from the server: %s" % error)
|
||||
if bbpkgs:
|
||||
self.options.pkgs_to_build.extend(bbpkgs.split())
|
||||
|
||||
def parseActions(self):
|
||||
# Parse any commandline into actions
|
||||
action = {'action':None, 'msg':None}
|
||||
if self.options.show_environment:
|
||||
if 'world' in self.options.pkgs_to_build:
|
||||
action['msg'] = "'world' is not a valid target for --environment."
|
||||
elif 'universe' in self.options.pkgs_to_build:
|
||||
action['msg'] = "'universe' is not a valid target for --environment."
|
||||
elif len(self.options.pkgs_to_build) > 1:
|
||||
action['msg'] = "Only one target can be used with the --environment option."
|
||||
elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
|
||||
action['msg'] = "No target should be used with the --environment and --buildfile options."
|
||||
elif len(self.options.pkgs_to_build) > 0:
|
||||
action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
|
||||
else:
|
||||
action['action'] = ["showEnvironment", self.options.buildfile]
|
||||
elif self.options.buildfile is not None:
|
||||
action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
|
||||
elif self.options.revisions_changed:
|
||||
action['action'] = ["compareRevisions"]
|
||||
elif self.options.show_versions:
|
||||
action['action'] = ["showVersions"]
|
||||
elif self.options.parse_only:
|
||||
action['action'] = ["parseFiles"]
|
||||
elif self.options.dot_graph:
|
||||
if self.options.pkgs_to_build:
|
||||
action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
|
||||
else:
|
||||
action['msg'] = "Please specify a package name for dependency graph generation."
|
||||
else:
|
||||
if self.options.pkgs_to_build:
|
||||
action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
|
||||
else:
|
||||
#action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
|
||||
action = None
|
||||
self.options.initialaction = action
|
||||
return action
|
||||
|
||||
class CookerConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.debug_domains = []
|
||||
self.extra_assume_provided = []
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.debug = 0
|
||||
self.cmd = None
|
||||
self.abort = True
|
||||
self.force = False
|
||||
self.profile = False
|
||||
self.nosetscene = False
|
||||
self.invalidate_stamp = False
|
||||
self.dump_signatures = False
|
||||
self.dry_run = False
|
||||
self.tracking = False
|
||||
|
||||
self.env = {}
|
||||
|
||||
def setConfigParameters(self, parameters):
|
||||
for key in self.__dict__.keys():
|
||||
if key in parameters.options.__dict__:
|
||||
setattr(self, key, parameters.options.__dict__[key])
|
||||
self.env = parameters.environment.copy()
|
||||
self.tracking = parameters.tracking
|
||||
|
||||
def setServerRegIdleCallback(self, srcb):
|
||||
self.server_register_idlecallback = srcb
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
for key in self.__dict__.keys():
|
||||
if key == "server_register_idlecallback":
|
||||
state[key] = None
|
||||
else:
|
||||
state[key] = getattr(self, key)
|
||||
return state
|
||||
|
||||
def __setstate__(self,state):
|
||||
for k in state:
|
||||
setattr(self, k, state[k])
|
||||
|
||||
|
||||
def catch_parse_error(func):
|
||||
"""Exception handling bits for our parsing"""
|
||||
@wraps(func)
|
||||
def wrapped(fn, *args):
|
||||
try:
|
||||
return func(fn, *args)
|
||||
except (IOError, bb.parse.ParseError, bb.data_smart.ExpansionError) as exc:
|
||||
import traceback
|
||||
parselog.critical( traceback.format_exc())
|
||||
parselog.critical("Unable to parse %s: %s" % (fn, exc))
|
||||
sys.exit(1)
|
||||
return wrapped
|
||||
|
||||
@catch_parse_error
|
||||
def parse_config_file(fn, data, include=True):
|
||||
return bb.parse.handle(fn, data, include)
|
||||
|
||||
@catch_parse_error
|
||||
def _inherit(bbclass, data):
|
||||
bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
|
||||
return data
|
||||
|
||||
def findConfigFile(configfile, data):
|
||||
search = []
|
||||
bbpath = data.getVar("BBPATH", True)
|
||||
if bbpath:
|
||||
for i in bbpath.split(":"):
|
||||
search.append(os.path.join(i, "conf", configfile))
|
||||
path = os.getcwd()
|
||||
while path != "/":
|
||||
search.append(os.path.join(path, "conf", configfile))
|
||||
path, _ = os.path.split(path)
|
||||
|
||||
for i in search:
|
||||
if os.path.exists(i):
|
||||
return i
|
||||
|
||||
return None
|
||||
|
||||
class CookerDataBuilder(object):
|
||||
|
||||
def __init__(self, cookercfg, worker = False):
|
||||
|
||||
self.prefiles = cookercfg.prefile
|
||||
self.postfiles = cookercfg.postfile
|
||||
self.tracking = cookercfg.tracking
|
||||
|
||||
bb.utils.set_context(bb.utils.clean_context())
|
||||
bb.event.set_class_handlers(bb.event.clean_class_handlers())
|
||||
self.data = bb.data.init()
|
||||
if self.tracking:
|
||||
self.data.enableTracking()
|
||||
|
||||
# Keep a datastore of the initial environment variables and their
|
||||
# values from when BitBake was launched to enable child processes
|
||||
# to use environment variables which have been cleaned from the
|
||||
# BitBake processes env
|
||||
self.savedenv = bb.data.init()
|
||||
for k in cookercfg.env:
|
||||
self.savedenv.setVar(k, cookercfg.env[k])
|
||||
|
||||
filtered_keys = bb.utils.approved_variables()
|
||||
bb.data.inheritFromOS(self.data, self.savedenv, filtered_keys)
|
||||
self.data.setVar("BB_ORIGENV", self.savedenv)
|
||||
|
||||
if worker:
|
||||
self.data.setVar("BB_WORKERCONTEXT", "1")
|
||||
|
||||
def parseBaseConfiguration(self):
|
||||
try:
|
||||
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
except SyntaxError:
|
||||
sys.exit(1)
|
||||
except Exception:
|
||||
logger.exception("Error parsing configuration files")
|
||||
sys.exit(1)
|
||||
|
||||
def _findLayerConf(self, data):
|
||||
return findConfigFile("bblayers.conf", data)
|
||||
|
||||
def parseConfigurationFiles(self, prefiles, postfiles):
|
||||
data = self.data
|
||||
bb.parse.init_parser(data)
|
||||
|
||||
# Parse files for loading *before* bitbake.conf and any includes
|
||||
for f in prefiles:
|
||||
data = parse_config_file(f, data)
|
||||
|
||||
layerconf = self._findLayerConf(data)
|
||||
if layerconf:
|
||||
parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
|
||||
# By definition bblayers.conf is in conf/ of TOPDIR.
|
||||
# We may have been called with cwd somewhere else so reset TOPDIR
|
||||
data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
|
||||
data = parse_config_file(layerconf, data)
|
||||
|
||||
layers = (data.getVar('BBLAYERS', True) or "").split()
|
||||
|
||||
data = bb.data.createCopy(data)
|
||||
for layer in layers:
|
||||
parselog.debug(2, "Adding layer %s", layer)
|
||||
data.setVar('LAYERDIR', layer)
|
||||
data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
|
||||
data.expandVarref('LAYERDIR')
|
||||
|
||||
data.delVar('LAYERDIR')
|
||||
|
||||
if not data.getVar("BBPATH", True):
|
||||
msg = "The BBPATH variable is not set"
|
||||
if not layerconf:
|
||||
msg += (" and bitbake did not find a conf/bblayers.conf file in"
|
||||
" the expected location.\nMaybe you accidentally"
|
||||
" invoked bitbake from the wrong directory?")
|
||||
raise SystemExit(msg)
|
||||
|
||||
data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
|
||||
|
||||
# Parse files for loading *after* bitbake.conf and any includes
|
||||
for p in postfiles:
|
||||
data = parse_config_file(p, data)
|
||||
|
||||
# Handle any INHERITs and inherit the base class
|
||||
bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
|
||||
for bbclass in bbclasses:
|
||||
data = _inherit(bbclass, data)
|
||||
|
||||
# Nomally we only register event handlers at the end of parsing .bb files
|
||||
# We register any handlers we've found so far here...
|
||||
for var in data.getVar('__BBHANDLERS') or []:
|
||||
bb.event.register(var, data.getVar(var), (data.getVarFlag(var, "eventmask", True) or "").split())
|
||||
|
||||
if data.getVar("BB_WORKERCONTEXT", False) is None:
|
||||
bb.fetch.fetcher_init(data)
|
||||
bb.codeparser.parser_cache_init(data)
|
||||
bb.event.fire(bb.event.ConfigParsed(), data)
|
||||
|
||||
if data.getVar("BB_INVALIDCONF") is True:
|
||||
data.setVar("BB_INVALIDCONF", False)
|
||||
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
return
|
||||
|
||||
bb.parse.init_parser(data)
|
||||
data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
|
||||
self.data = data
|
||||
self.data_hash = data.get_hash()
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def init():
|
||||
def init_db(parent = None):
|
||||
"""Return a new object representing the Bitbake data,
|
||||
optionally based on an existing object"""
|
||||
if parent is not None:
|
||||
if parent:
|
||||
return parent.createCopy()
|
||||
else:
|
||||
return _dict_type()
|
||||
@@ -97,10 +97,6 @@ def delVar(var, d):
|
||||
"""Removes a variable from the data set"""
|
||||
d.delVar(var)
|
||||
|
||||
def appendVar(var, value, d):
|
||||
"""Append additional value to a variable"""
|
||||
d.appendVar(var, value)
|
||||
|
||||
def setVarFlag(var, flag, flagvalue, d):
|
||||
"""Set a flag for a given variable to a given value"""
|
||||
d.setVarFlag(var, flag, flagvalue)
|
||||
@@ -148,7 +144,7 @@ def expandKeys(alterdata, readdata = None):
|
||||
readdata = alterdata
|
||||
|
||||
todolist = {}
|
||||
for key in alterdata:
|
||||
for key in keys(alterdata):
|
||||
if not '${' in key:
|
||||
continue
|
||||
|
||||
@@ -162,11 +158,6 @@ def expandKeys(alterdata, readdata = None):
|
||||
|
||||
for key in todolist:
|
||||
ekey = todolist[key]
|
||||
newval = alterdata.getVar(ekey, 0)
|
||||
if newval:
|
||||
val = alterdata.getVar(key, 0)
|
||||
if val is not None and newval is not None:
|
||||
bb.warn("Variable key %s (%s) replaces original key %s (%s)." % (key, val, ekey, newval))
|
||||
alterdata.renameVar(key, ekey)
|
||||
|
||||
def inheritFromOS(d, savedenv, permitted):
|
||||
@@ -214,7 +205,7 @@ 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)
|
||||
@@ -229,7 +220,7 @@ 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)
|
||||
o.write('%s="%s"\n' % (varExpanded, alter))
|
||||
return 0
|
||||
@@ -278,31 +269,26 @@ def emit_func(func, o=sys.__stdout__, d = init()):
|
||||
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
|
||||
|
||||
def update_data(d):
|
||||
"""Performs final steps upon the datastore, including application of overrides"""
|
||||
d.finalize(parent = True)
|
||||
|
||||
def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
def build_dependencies(key, keys, shelldeps, vardepvals, d):
|
||||
deps = set()
|
||||
vardeps = d.getVarFlag(key, "vardeps", True)
|
||||
try:
|
||||
if key[-1] == ']':
|
||||
vf = key[:-1].split('[')
|
||||
value = d.getVarFlag(vf[0], vf[1], False)
|
||||
parser = d.expandWithRefs(value, key)
|
||||
deps |= parser.references
|
||||
deps = deps | (keys & parser.execs)
|
||||
return deps, value
|
||||
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude"]) or {}
|
||||
vardeps = varflags.get("vardeps")
|
||||
value = d.getVar(key, False)
|
||||
else:
|
||||
value = d.getVar(key, False)
|
||||
|
||||
if "vardepvalue" in varflags:
|
||||
value = varflags.get("vardepvalue")
|
||||
elif varflags.get("func"):
|
||||
if varflags.get("python"):
|
||||
if key in vardepvals:
|
||||
value = d.getVarFlag(key, "vardepvalue", True)
|
||||
elif d.getVarFlag(key, "func"):
|
||||
if d.getVarFlag(key, "python"):
|
||||
parsedvar = d.expandWithRefs(value, key)
|
||||
parser = bb.codeparser.PythonParser(key, logger)
|
||||
if parsedvar.value and "\t" in parsedvar.value:
|
||||
@@ -324,16 +310,19 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
deps = deps | (keys & parser.execs)
|
||||
|
||||
# Add varflags, assuming an exclusion list is set
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||
if varflagsexcl:
|
||||
varfdeps = []
|
||||
for f in varflags:
|
||||
if f not in varflagsexcl:
|
||||
varfdeps.append('%s[%s]' % (key, f))
|
||||
varflags = d.getVarFlags(key)
|
||||
if varflags:
|
||||
for f in varflags:
|
||||
if f not in varflagsexcl:
|
||||
varfdeps.append('%s[%s]' % (key, f))
|
||||
if varfdeps:
|
||||
deps |= set(varfdeps)
|
||||
|
||||
deps |= set((vardeps or "").split())
|
||||
deps -= set(varflags.get("vardepsexclude", "").split())
|
||||
deps -= set((d.getVarFlag(key, "vardepsexclude", True) or "").split())
|
||||
except Exception as e:
|
||||
raise bb.data_smart.ExpansionError(key, None, e)
|
||||
return deps, value
|
||||
@@ -342,16 +331,16 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
|
||||
def generate_dependencies(d):
|
||||
|
||||
keys = set(key for key in d if not key.startswith("__"))
|
||||
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||
keys = set(key for key in d.keys() if not key.startswith("__"))
|
||||
shelldeps = set(key for key in keys if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||
vardepvals = set(key for key in keys if d.getVarFlag(key, "vardepvalue"))
|
||||
|
||||
deps = {}
|
||||
values = {}
|
||||
|
||||
tasklist = d.getVar('__BBTASKS') or []
|
||||
for task in tasklist:
|
||||
deps[task], values[task] = build_dependencies(task, keys, shelldeps, varflagsexcl, d)
|
||||
deps[task], values[task] = build_dependencies(task, keys, shelldeps, vardepvals, d)
|
||||
newdeps = deps[task]
|
||||
seen = set()
|
||||
while newdeps:
|
||||
@@ -360,7 +349,7 @@ def generate_dependencies(d):
|
||||
newdeps = set()
|
||||
for dep in nextdeps:
|
||||
if dep not in deps:
|
||||
deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, varflagsexcl, d)
|
||||
deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, vardepvals, d)
|
||||
newdeps |= deps[dep]
|
||||
newdeps -= seen
|
||||
#print "For %s: %s" % (task, str(deps[task]))
|
||||
|
||||
@@ -38,9 +38,9 @@ from bb.COW import COWDictBase
|
||||
|
||||
logger = logging.getLogger("BitBake.Data")
|
||||
|
||||
__setvar_keyword__ = ["_append", "_prepend", "_remove"]
|
||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>.*))?$')
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}@\n\t ]+}")
|
||||
__setvar_keyword__ = ["_append", "_prepend"]
|
||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?$')
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
|
||||
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
||||
|
||||
def infer_caller_details(loginfo, parent = False, varval = True):
|
||||
@@ -94,13 +94,9 @@ class VariableParse:
|
||||
if self.varname and key:
|
||||
if self.varname == key:
|
||||
raise Exception("variable %s references itself!" % self.varname)
|
||||
if key in self.d.expand_cache:
|
||||
varparse = self.d.expand_cache[key]
|
||||
var = varparse.value
|
||||
else:
|
||||
var = self.d.getVar(key, True)
|
||||
self.references.add(key)
|
||||
var = self.d.getVar(key, True)
|
||||
if var is not None:
|
||||
self.references.add(key)
|
||||
return var
|
||||
else:
|
||||
return match.group()
|
||||
@@ -263,31 +259,6 @@ class VariableHistory(object):
|
||||
o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
|
||||
o.write('# "%s"\n' % (commentVal))
|
||||
|
||||
def get_variable_files(self, var):
|
||||
"""Get the files where operations are made on a variable"""
|
||||
var_history = self.variable(var)
|
||||
files = []
|
||||
for event in var_history:
|
||||
files.append(event['file'])
|
||||
return files
|
||||
|
||||
def get_variable_lines(self, var, f):
|
||||
"""Get the line where a operation is made on a variable in file f"""
|
||||
var_history = self.variable(var)
|
||||
lines = []
|
||||
for event in var_history:
|
||||
if f== event['file']:
|
||||
line = event['line']
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
def del_var_history(self, var, f=None, line=None):
|
||||
"""If file f and line are not given, the entire history of var is deleted"""
|
||||
if var in self.variables:
|
||||
if f and line:
|
||||
self.variables[var] = [ x for x in self.variables[var] if x['file']!=f and x['line']!=line]
|
||||
else:
|
||||
self.variables[var] = []
|
||||
|
||||
class DataSmart(MutableMapping):
|
||||
def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ):
|
||||
@@ -365,8 +336,7 @@ class DataSmart(MutableMapping):
|
||||
|
||||
#
|
||||
# First we apply all overrides
|
||||
# Then we will handle _append and _prepend and store the _remove
|
||||
# information for later.
|
||||
# Then we will handle _append and _prepend
|
||||
#
|
||||
|
||||
# We only want to report finalization once per variable overridden.
|
||||
@@ -401,7 +371,7 @@ class DataSmart(MutableMapping):
|
||||
except Exception:
|
||||
logger.info("Untracked delVar")
|
||||
|
||||
# now on to the appends and prepends, and stashing the removes
|
||||
# now on to the appends and prepends
|
||||
for op in __setvar_keyword__:
|
||||
if op in self._special_values:
|
||||
appends = self._special_values[op] or []
|
||||
@@ -424,10 +394,6 @@ class DataSmart(MutableMapping):
|
||||
elif op == "_prepend":
|
||||
sval = a + (self.getVar(append, False) or "")
|
||||
self.setVar(append, sval)
|
||||
elif op == "_remove":
|
||||
removes = self.getVarFlag(append, "_removeactive", False) or []
|
||||
removes.extend(a.split())
|
||||
self.setVarFlag(append, "_removeactive", removes, ignore=True)
|
||||
|
||||
# We save overrides that may be applied at some later stage
|
||||
if keep:
|
||||
@@ -463,7 +429,6 @@ class DataSmart(MutableMapping):
|
||||
|
||||
|
||||
def setVar(self, var, value, **loginfo):
|
||||
#print("var=" + str(var) + " val=" + str(value))
|
||||
if 'op' not in loginfo:
|
||||
loginfo['op'] = "set"
|
||||
self.expand_cache = {}
|
||||
@@ -498,22 +463,23 @@ class DataSmart(MutableMapping):
|
||||
|
||||
# more cookies for the cookie monster
|
||||
if '_' in var:
|
||||
self._setvar_update_overrides(var)
|
||||
override = var[var.rfind('_')+1:]
|
||||
if len(override) > 0:
|
||||
if override not in self._seen_overrides:
|
||||
self._seen_overrides[override] = set()
|
||||
self._seen_overrides[override].add( var )
|
||||
|
||||
# setting var
|
||||
self.dict[var]["_content"] = value
|
||||
self.varhistory.record(**loginfo)
|
||||
|
||||
def _setvar_update_overrides(self, var):
|
||||
# aka pay the cookie monster
|
||||
override = var[var.rfind('_')+1:]
|
||||
if len(override) > 0:
|
||||
if override not in self._seen_overrides:
|
||||
self._seen_overrides[override] = set()
|
||||
self._seen_overrides[override].add( var )
|
||||
|
||||
def getVar(self, var, expand=False, noweakdefault=False):
|
||||
return self.getVarFlag(var, "_content", expand, noweakdefault)
|
||||
value = self.getVarFlag(var, "_content", False, noweakdefault)
|
||||
|
||||
# Call expand() separately to make use of the expand cache
|
||||
if expand and value:
|
||||
return self.expand(value, var)
|
||||
return value
|
||||
|
||||
def renameVar(self, key, newkey, **loginfo):
|
||||
"""
|
||||
@@ -527,7 +493,7 @@ class DataSmart(MutableMapping):
|
||||
self.varhistory.record(**loginfo)
|
||||
self.setVar(newkey, val, ignore=True)
|
||||
|
||||
for i in (__setvar_keyword__):
|
||||
for i in ('_append', '_prepend'):
|
||||
src = self.getVarFlag(key, i)
|
||||
if src is None:
|
||||
continue
|
||||
@@ -578,36 +544,16 @@ class DataSmart(MutableMapping):
|
||||
self._makeShadowCopy(var)
|
||||
self.dict[var][flag] = value
|
||||
|
||||
if flag == "defaultval" and '_' in var:
|
||||
self._setvar_update_overrides(var)
|
||||
|
||||
if flag == "unexport" or flag == "export":
|
||||
if not "__exportlist" in self.dict:
|
||||
self._makeShadowCopy("__exportlist")
|
||||
if not "_content" in self.dict["__exportlist"]:
|
||||
self.dict["__exportlist"]["_content"] = set()
|
||||
self.dict["__exportlist"]["_content"].add(var)
|
||||
|
||||
def getVarFlag(self, var, flag, expand=False, noweakdefault=False):
|
||||
local_var = self._findVar(var)
|
||||
value = None
|
||||
if local_var is not None:
|
||||
if local_var:
|
||||
if flag in local_var:
|
||||
value = copy.copy(local_var[flag])
|
||||
elif flag == "_content" and "defaultval" in local_var and not noweakdefault:
|
||||
value = copy.copy(local_var["defaultval"])
|
||||
if expand and value:
|
||||
# Only getvar (flag == _content) hits the expand cache
|
||||
cachename = None
|
||||
if flag == "_content":
|
||||
cachename = var
|
||||
else:
|
||||
cachename = var + "[" + flag + "]"
|
||||
value = self.expand(value, cachename)
|
||||
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)
|
||||
value = self.expand(value, None)
|
||||
return value
|
||||
|
||||
def delVarFlag(self, var, flag, **loginfo):
|
||||
@@ -652,17 +598,16 @@ class DataSmart(MutableMapping):
|
||||
self.varhistory.record(**loginfo)
|
||||
self.dict[var][i] = flags[i]
|
||||
|
||||
def getVarFlags(self, var, expand = False, internalflags=False):
|
||||
def getVarFlags(self, var):
|
||||
local_var = self._findVar(var)
|
||||
flags = {}
|
||||
|
||||
if local_var:
|
||||
for i in local_var:
|
||||
if i.startswith("_") and not internalflags:
|
||||
if i.startswith("_"):
|
||||
continue
|
||||
flags[i] = local_var[i]
|
||||
if expand and i in expand:
|
||||
flags[i] = self.expand(flags[i], var + "[" + i + "]")
|
||||
|
||||
if len(flags) == 0:
|
||||
return None
|
||||
return flags
|
||||
@@ -768,27 +713,8 @@ class DataSmart(MutableMapping):
|
||||
for key in keys:
|
||||
if key in config_whitelist:
|
||||
continue
|
||||
|
||||
value = d.getVar(key, False) or ""
|
||||
data.update({key:value})
|
||||
|
||||
varflags = d.getVarFlags(key, internalflags = True)
|
||||
if not varflags:
|
||||
continue
|
||||
for f in varflags:
|
||||
if f == "_content":
|
||||
continue
|
||||
data.update({'%s[%s]' % (key, f):varflags[f]})
|
||||
|
||||
for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
|
||||
bb_list = d.getVar(key, False) or []
|
||||
bb_list.sort()
|
||||
data.update({key:str(bb_list)})
|
||||
|
||||
if key == "__BBANONFUNCS":
|
||||
for i in bb_list:
|
||||
value = d.getVar(i, True) or ""
|
||||
data.update({i:value})
|
||||
|
||||
data_str = str([(k, data[k]) for k in sorted(data.keys())])
|
||||
return hashlib.md5(data_str).hexdigest()
|
||||
|
||||
@@ -33,12 +33,11 @@ import atexit
|
||||
import traceback
|
||||
import bb.utils
|
||||
import bb.compat
|
||||
import bb.exceptions
|
||||
|
||||
# This is the pid for which we should generate the event. This is set when
|
||||
# the runqueue forks off.
|
||||
worker_pid = 0
|
||||
worker_fire = None
|
||||
worker_pipe = None
|
||||
|
||||
logger = logging.getLogger('BitBake.Event')
|
||||
|
||||
@@ -48,25 +47,20 @@ class Event(object):
|
||||
def __init__(self):
|
||||
self.pid = worker_pid
|
||||
|
||||
NotHandled = 0
|
||||
Handled = 1
|
||||
|
||||
Registered = 10
|
||||
AlreadyRegistered = 14
|
||||
|
||||
def get_class_handlers():
|
||||
return _handlers
|
||||
|
||||
def set_class_handlers(h):
|
||||
_handlers = h
|
||||
|
||||
def clean_class_handlers():
|
||||
return bb.compat.OrderedDict()
|
||||
|
||||
# Internal
|
||||
_handlers = clean_class_handlers()
|
||||
_handlers = bb.compat.OrderedDict()
|
||||
_ui_handlers = {}
|
||||
_ui_logfilters = {}
|
||||
_ui_handler_seq = 0
|
||||
_event_handler_map = {}
|
||||
_catchall_handlers = {}
|
||||
|
||||
# For compatibility
|
||||
bb.utils._context["NotHandled"] = NotHandled
|
||||
bb.utils._context["Handled"] = Handled
|
||||
|
||||
def execute_handler(name, handler, event, d):
|
||||
event.data = d
|
||||
@@ -86,18 +80,19 @@ def execute_handler(name, handler, event, d):
|
||||
finally:
|
||||
del event.data
|
||||
|
||||
if ret is not None:
|
||||
warnings.warn("Using Handled/NotHandled in event handlers is deprecated",
|
||||
DeprecationWarning, stacklevel = 2)
|
||||
|
||||
def fire_class_handlers(event, d):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
return
|
||||
|
||||
eid = str(event.__class__)[8:-2]
|
||||
evt_hmap = _event_handler_map.get(eid, {})
|
||||
for name, handler in _handlers.iteritems():
|
||||
if name in _catchall_handlers or name in evt_hmap:
|
||||
try:
|
||||
execute_handler(name, handler, event, d)
|
||||
except Exception:
|
||||
continue
|
||||
try:
|
||||
execute_handler(name, handler, event, d)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
ui_queue = []
|
||||
@atexit.register
|
||||
@@ -136,8 +131,6 @@ def fire_ui_handlers(event, d):
|
||||
for h in _ui_handlers:
|
||||
#print "Sending event %s" % event
|
||||
try:
|
||||
if not _ui_logfilters[h].filter(event):
|
||||
continue
|
||||
# We use pickle here since it better handles object instances
|
||||
# which xmlrpc's marshaller does not. Events *must* be serializable
|
||||
# by pickle.
|
||||
@@ -159,16 +152,24 @@ def fire(event, d):
|
||||
# don't have a datastore so the datastore context isn't a problem.
|
||||
|
||||
fire_class_handlers(event, d)
|
||||
if worker_fire:
|
||||
if worker_pid != 0:
|
||||
worker_fire(event, d)
|
||||
else:
|
||||
fire_ui_handlers(event, d)
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_pipe.write(data)
|
||||
|
||||
def fire_from_worker(event, d):
|
||||
if not event.startswith("<event>") or not event.endswith("</event>"):
|
||||
print("Error, not an event %s" % event)
|
||||
return
|
||||
event = pickle.loads(event[7:-8])
|
||||
fire_ui_handlers(event, d)
|
||||
|
||||
noop = lambda _: None
|
||||
def register(name, handler, mask=[]):
|
||||
def register(name, handler):
|
||||
"""Register an Event handler"""
|
||||
|
||||
# already registered
|
||||
@@ -193,14 +194,6 @@ def register(name, handler, mask=[]):
|
||||
else:
|
||||
_handlers[name] = handler
|
||||
|
||||
if not mask or '*' in mask:
|
||||
_catchall_handlers[name] = True
|
||||
else:
|
||||
for m in mask:
|
||||
if _event_handler_map.get(m, None) is None:
|
||||
_event_handler_map[m] = {}
|
||||
_event_handler_map[m][name] = True
|
||||
|
||||
return Registered
|
||||
|
||||
def remove(name, handler):
|
||||
@@ -210,8 +203,6 @@ def remove(name, handler):
|
||||
def register_UIHhandler(handler):
|
||||
bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1
|
||||
_ui_handlers[_ui_handler_seq] = handler
|
||||
level, debug_domains = bb.msg.constructLogOptions()
|
||||
_ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains)
|
||||
return _ui_handler_seq
|
||||
|
||||
def unregister_UIHhandler(handlerNum):
|
||||
@@ -219,37 +210,6 @@ def unregister_UIHhandler(handlerNum):
|
||||
del _ui_handlers[handlerNum]
|
||||
return
|
||||
|
||||
# Class to allow filtering of events and specific filtering of LogRecords *before* we put them over the IPC
|
||||
class UIEventFilter(object):
|
||||
def __init__(self, level, debug_domains):
|
||||
self.update(None, level, debug_domains)
|
||||
|
||||
def update(self, eventmask, level, debug_domains):
|
||||
self.eventmask = eventmask
|
||||
self.stdlevel = level
|
||||
self.debug_domains = debug_domains
|
||||
|
||||
def filter(self, event):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
if event.levelno >= self.stdlevel:
|
||||
return True
|
||||
if event.name in self.debug_domains and event.levelno >= self.debug_domains[event.name]:
|
||||
return True
|
||||
return False
|
||||
eid = str(event.__class__)[8:-2]
|
||||
if self.eventmask and eid not in self.eventmask:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_UIHmask(handlerNum, level, debug_domains, mask):
|
||||
if not handlerNum in _ui_handlers:
|
||||
return False
|
||||
if '*' in mask:
|
||||
_ui_logfilters[handlerNum].update(None, level, debug_domains)
|
||||
else:
|
||||
_ui_logfilters[handlerNum].update(mask, level, debug_domains)
|
||||
return True
|
||||
|
||||
def getName(e):
|
||||
"""Returns the name of a class or class instance"""
|
||||
if getattr(e, "__name__", None) == None:
|
||||
@@ -377,13 +337,12 @@ class DiskFull(Event):
|
||||
class NoProvider(Event):
|
||||
"""No Provider for an Event"""
|
||||
|
||||
def __init__(self, item, runtime=False, dependees=None, reasons=[], close_matches=[]):
|
||||
def __init__(self, item, runtime=False, dependees=None, reasons=[]):
|
||||
Event.__init__(self)
|
||||
self._item = item
|
||||
self._runtime = runtime
|
||||
self._dependees = dependees
|
||||
self._reasons = reasons
|
||||
self._close_matches = close_matches
|
||||
|
||||
def getItem(self):
|
||||
return self._item
|
||||
@@ -589,16 +548,6 @@ class PackageInfo(Event):
|
||||
Event.__init__(self)
|
||||
self._pkginfolist = pkginfolist
|
||||
|
||||
class MetadataEvent(Event):
|
||||
"""
|
||||
Generic event that target for OE-Core classes
|
||||
to report information during asynchrous execution
|
||||
"""
|
||||
def __init__(self, eventtype, eventdata):
|
||||
Event.__init__(self)
|
||||
self.type = eventtype
|
||||
self.data = eventdata
|
||||
|
||||
class SanityCheck(Event):
|
||||
"""
|
||||
Event to issue sanity check
|
||||
|
||||
@@ -28,20 +28,12 @@ BitBake build tools.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
import os, re
|
||||
import signal
|
||||
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
|
||||
from bb import data
|
||||
import bb.process
|
||||
import subprocess
|
||||
|
||||
__version__ = "2"
|
||||
_checksum_cache = bb.checksum.FileChecksumCache()
|
||||
@@ -78,9 +70,6 @@ class FetchError(BBFetchException):
|
||||
|
||||
class ChecksumError(FetchError):
|
||||
"""Exception when mismatched checksum encountered"""
|
||||
def __init__(self, message, url = None, checksum = None):
|
||||
self.checksum = checksum
|
||||
FetchError.__init__(self, message, url)
|
||||
|
||||
class NoChecksumError(FetchError):
|
||||
"""Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set"""
|
||||
@@ -121,7 +110,7 @@ class ParameterError(BBFetchException):
|
||||
class NetworkAccess(BBFetchException):
|
||||
"""Exception raised when network access is disabled but it is required."""
|
||||
def __init__(self, url, cmd):
|
||||
msg = "Network access disabled through BB_NO_NETWORK (or set indirectly due to use of BB_FETCH_PREMIRRORONLY) but access requested with command %s (for url %s)" % (cmd, url)
|
||||
msg = "Network access disabled through BB_NO_NETWORK but access requested with command %s (for url %s)" % (cmd, url)
|
||||
self.url = url
|
||||
self.cmd = cmd
|
||||
BBFetchException.__init__(self, msg)
|
||||
@@ -131,205 +120,12 @@ class NonLocalMethod(Exception):
|
||||
def __init__(self):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
||||
class URI(object):
|
||||
"""
|
||||
A class representing a generic URI, with methods for
|
||||
accessing the URI components, and stringifies to the
|
||||
URI.
|
||||
|
||||
It is constructed by calling it with a URI, or setting
|
||||
the attributes manually:
|
||||
|
||||
uri = URI("http://example.com/")
|
||||
|
||||
uri = URI()
|
||||
uri.scheme = 'http'
|
||||
uri.hostname = 'example.com'
|
||||
uri.path = '/'
|
||||
|
||||
It has the following attributes:
|
||||
|
||||
* scheme (read/write)
|
||||
* userinfo (authentication information) (read/write)
|
||||
* username (read/write)
|
||||
* password (read/write)
|
||||
|
||||
Note, password is deprecated as of RFC 3986.
|
||||
|
||||
* hostname (read/write)
|
||||
* port (read/write)
|
||||
* hostport (read only)
|
||||
"hostname:port", if both are set, otherwise just "hostname"
|
||||
* path (read/write)
|
||||
* path_quoted (read/write)
|
||||
A URI quoted version of path
|
||||
* params (dict) (read/write)
|
||||
* relative (bool) (read only)
|
||||
True if this is a "relative URI", (e.g. file:foo.diff)
|
||||
|
||||
It stringifies to the URI itself.
|
||||
|
||||
Some notes about relative URIs: while it's specified that
|
||||
a URI beginning with <scheme>:// should either be directly
|
||||
followed by a hostname or a /, the old URI handling of the
|
||||
fetch2 library did not comform to this. Therefore, this URI
|
||||
class has some kludges to make sure that URIs are parsed in
|
||||
a way comforming to bitbake's current usage. This URI class
|
||||
supports the following:
|
||||
|
||||
file:relative/path.diff (IETF compliant)
|
||||
git:relative/path.git (IETF compliant)
|
||||
git:///absolute/path.git (IETF compliant)
|
||||
file:///absolute/path.diff (IETF compliant)
|
||||
|
||||
file://relative/path.diff (not IETF compliant)
|
||||
|
||||
But it does not support the following:
|
||||
|
||||
file://hostname/absolute/path.diff (would be IETF compliant)
|
||||
|
||||
Note that the last case only applies to a list of
|
||||
"whitelisted" schemes (currently only file://), that requires
|
||||
its URIs to not have a network location.
|
||||
"""
|
||||
|
||||
_relative_schemes = ['file', 'git']
|
||||
_netloc_forbidden = ['file']
|
||||
|
||||
def __init__(self, uri=None):
|
||||
self.scheme = ''
|
||||
self.userinfo = ''
|
||||
self.hostname = ''
|
||||
self.port = None
|
||||
self._path = ''
|
||||
self.params = {}
|
||||
self.relative = False
|
||||
|
||||
if not uri:
|
||||
return
|
||||
|
||||
urlp = urlparse(uri)
|
||||
self.scheme = urlp.scheme
|
||||
|
||||
# Convert URI to be relative
|
||||
if urlp.scheme in self._netloc_forbidden:
|
||||
uri = re.sub("(?<=:)//(?!/)", "", uri, 1)
|
||||
urlp = urlparse(uri)
|
||||
|
||||
# Identify if the URI is relative or not
|
||||
if urlp.scheme in self._relative_schemes and \
|
||||
re.compile("^\w+:(?!//)").match(uri):
|
||||
self.relative = True
|
||||
|
||||
if not self.relative:
|
||||
self.hostname = urlp.hostname or ''
|
||||
self.port = urlp.port
|
||||
|
||||
self.userinfo += urlp.username or ''
|
||||
|
||||
if urlp.password:
|
||||
self.userinfo += ':%s' % urlp.password
|
||||
|
||||
# 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_dict(param_str)
|
||||
|
||||
def __str__(self):
|
||||
userinfo = self.userinfo
|
||||
if userinfo:
|
||||
userinfo += '@'
|
||||
|
||||
return "%s:%s%s%s%s%s" % (
|
||||
self.scheme,
|
||||
'' if self.relative else '//',
|
||||
userinfo,
|
||||
self.hostport,
|
||||
self.path_quoted,
|
||||
self._param_str)
|
||||
|
||||
@property
|
||||
def _param_str(self):
|
||||
ret = ''
|
||||
for key, val in self.params.items():
|
||||
ret += ";%s=%s" % (key, val)
|
||||
return ret
|
||||
|
||||
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):
|
||||
if not self.port:
|
||||
return self.hostname
|
||||
return "%s:%d" % (self.hostname, self.port)
|
||||
|
||||
@property
|
||||
def path_quoted(self):
|
||||
return urllib.quote(self.path)
|
||||
|
||||
@path_quoted.setter
|
||||
def path_quoted(self, path):
|
||||
self.path = urllib.unquote(path)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
self._path = path
|
||||
|
||||
if re.compile("^/").match(path):
|
||||
self.relative = False
|
||||
else:
|
||||
self.relative = True
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
if self.userinfo:
|
||||
return (self.userinfo.split(":", 1))[0]
|
||||
return ''
|
||||
|
||||
@username.setter
|
||||
def username(self, username):
|
||||
self.userinfo = username
|
||||
if self.password:
|
||||
self.userinfo += ":%s" % self.password
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
if self.userinfo and ":" in self.userinfo:
|
||||
return (self.userinfo.split(":", 1))[1]
|
||||
return ''
|
||||
|
||||
@password.setter
|
||||
def password(self, password):
|
||||
self.userinfo = "%s:%s" % (self.username, password)
|
||||
|
||||
def decodeurl(url):
|
||||
"""Decodes an URL into the tokens (scheme, network location, path,
|
||||
user, password, parameters).
|
||||
"""
|
||||
|
||||
m = re.compile('(?P<type>[^:]*)://((?P<user>[^/]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
|
||||
m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
|
||||
if not m:
|
||||
raise MalformedUrl(url)
|
||||
|
||||
@@ -418,8 +214,6 @@ def uri_replace(ud, uri_find, uri_replace, replacements, d):
|
||||
return None
|
||||
# Overwrite any specified replacement parameters
|
||||
for k in uri_replace_decoded[loc]:
|
||||
for l in replacements:
|
||||
uri_replace_decoded[loc][k] = uri_replace_decoded[loc][k].replace(l, replacements[l])
|
||||
result_decoded[loc][k] = uri_replace_decoded[loc][k]
|
||||
elif (re.match(regexp, uri_decoded[loc])):
|
||||
if not uri_replace_decoded[loc]:
|
||||
@@ -568,7 +362,7 @@ def verify_checksum(u, 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, u, md5data)
|
||||
raise ChecksumError('Checksum mismatch!%s' % msg, u)
|
||||
|
||||
|
||||
def update_stamp(u, ud, d):
|
||||
@@ -588,6 +382,7 @@ def update_stamp(u, ud, d):
|
||||
open(ud.donestamp, 'w').close()
|
||||
|
||||
def subprocess_setup():
|
||||
import signal
|
||||
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||
# non-Python subprocesses expect.
|
||||
# SIGPIPE errors are known issues with gzip/bash
|
||||
@@ -619,12 +414,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(scms[0], urldata[scms[0]], d, urldata[scms[0]].names[0])
|
||||
if len(rev) > 10:
|
||||
rev = rev[:10]
|
||||
if autoinc:
|
||||
return "AUTOINC+" + rev
|
||||
return rev
|
||||
return urldata[scms[0]].method.sortable_revision(scms[0], urldata[scms[0]], d, urldata[scms[0]].names[0])
|
||||
|
||||
#
|
||||
# Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
|
||||
@@ -633,17 +423,19 @@ def get_srcrev(d):
|
||||
if not format:
|
||||
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
|
||||
|
||||
seenautoinc = False
|
||||
autoinc = False
|
||||
autoinc_templ = 'AUTOINC+'
|
||||
for scm in scms:
|
||||
ud = urldata[scm]
|
||||
for name in ud.names:
|
||||
autoinc, rev = ud.method.sortable_revision(scm, ud, d, name)
|
||||
seenautoinc = seenautoinc or autoinc
|
||||
if len(rev) > 10:
|
||||
rev = rev[:10]
|
||||
rev = ud.method.sortable_revision(scm, ud, d, name)
|
||||
if rev.startswith(autoinc_templ):
|
||||
if not autoinc:
|
||||
autoinc = True
|
||||
format = "%s%s" % (autoinc_templ, format)
|
||||
rev = rev[len(autoinc_templ):]
|
||||
|
||||
format = format.replace(name, rev)
|
||||
if seenautoinc:
|
||||
format = "AUTOINC+" + format
|
||||
|
||||
return format
|
||||
|
||||
@@ -659,20 +451,18 @@ def runfetchcmd(cmd, d, quiet = False, cleanup = []):
|
||||
Optionally remove the files/directories listed in cleanup upon failure
|
||||
"""
|
||||
|
||||
import bb.process
|
||||
import subprocess
|
||||
|
||||
# Need to export PATH as binary could be in metadata paths
|
||||
# rather than host provided
|
||||
# Also include some other variables.
|
||||
# FIXME: Should really include all export varaiables?
|
||||
exportvars = ['HOME', 'PATH',
|
||||
'HTTP_PROXY', 'http_proxy',
|
||||
'HTTPS_PROXY', 'https_proxy',
|
||||
'FTP_PROXY', 'ftp_proxy',
|
||||
'FTPS_PROXY', 'ftps_proxy',
|
||||
'NO_PROXY', 'no_proxy',
|
||||
'ALL_PROXY', 'all_proxy',
|
||||
'GIT_PROXY_COMMAND',
|
||||
'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
|
||||
'SOCKS5_USER', 'SOCKS5_PASSWD']
|
||||
exportvars = ['PATH', 'GIT_PROXY_COMMAND', 'GIT_PROXY_HOST',
|
||||
'GIT_PROXY_PORT', 'GIT_CONFIG', 'http_proxy', 'ftp_proxy',
|
||||
'https_proxy', 'no_proxy', 'ALL_PROXY', 'all_proxy',
|
||||
'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 'HOME',
|
||||
'GIT_PROXY_IGNORE', 'SOCKS5_USER', 'SOCKS5_PASSWD']
|
||||
|
||||
for var in exportvars:
|
||||
val = d.getVar(var, True)
|
||||
@@ -759,19 +549,6 @@ def build_mirroruris(origud, mirrors, ld):
|
||||
|
||||
return uris, uds
|
||||
|
||||
def rename_bad_checksum(ud, suffix):
|
||||
"""
|
||||
Renames files to have suffix from parameter
|
||||
"""
|
||||
|
||||
if ud.localpath is None:
|
||||
return
|
||||
|
||||
new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
|
||||
bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
|
||||
bb.utils.movefile(ud.localpath, new_localpath)
|
||||
|
||||
|
||||
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
|
||||
@@ -800,16 +577,11 @@ def try_mirror_url(newuri, origud, ud, ld, check = False):
|
||||
dldir = ld.getVar("DL_DIR", True)
|
||||
if origud.mirrortarball and os.path.basename(ud.localpath) == os.path.basename(origud.mirrortarball) \
|
||||
and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
|
||||
bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
|
||||
open(ud.donestamp, 'w').close()
|
||||
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.url, origud, ld):
|
||||
origud.method.download(origud.url, origud, ld)
|
||||
if hasattr(origud.method,"build_mirror_data"):
|
||||
origud.method.build_mirror_data(origud.url, 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):
|
||||
@@ -827,7 +599,6 @@ def try_mirror_url(newuri, origud, ud, ld, check = False):
|
||||
if isinstance(e, ChecksumError):
|
||||
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:
|
||||
@@ -879,14 +650,11 @@ def srcrev_internal_helper(ud, d, name):
|
||||
if not rev:
|
||||
rev = d.getVar("SRCREV_%s" % name, True)
|
||||
if not rev:
|
||||
rev = d.getVar("SRCREV_pn-%s" % pn, True)
|
||||
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)
|
||||
raise FetchError("Please set SRCREV to a valid value", ud.url)
|
||||
if rev == "AUTOINC":
|
||||
rev = ud.method.latest_revision(ud.url, ud, d, name)
|
||||
|
||||
@@ -933,6 +701,7 @@ def get_file_checksums(filelist, pn):
|
||||
try:
|
||||
checksum = _checksum_cache.get_checksum(f)
|
||||
except OSError as e:
|
||||
import traceback
|
||||
bb.warn("Unable to get checksum for %s SRC_URI entry %s: %s" % (pn, os.path.basename(f), e))
|
||||
return None
|
||||
return checksum
|
||||
@@ -942,6 +711,7 @@ def get_file_checksums(filelist, pn):
|
||||
checksum = None
|
||||
if '*' in pth:
|
||||
# Handle globs
|
||||
import glob
|
||||
for f in glob.glob(pth):
|
||||
checksum = checksum_file(f)
|
||||
if checksum:
|
||||
@@ -976,7 +746,6 @@ class FetchData(object):
|
||||
self.lockfile = None
|
||||
self.mirrortarball = None
|
||||
self.basename = None
|
||||
self.basepath = None
|
||||
(self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(data.expand(url, d))
|
||||
self.date = self.getSRCDate(d)
|
||||
self.url = url
|
||||
@@ -994,13 +763,13 @@ class FetchData(object):
|
||||
self.sha256_name = "sha256sum"
|
||||
if self.md5_name in self.parm:
|
||||
self.md5_expected = self.parm[self.md5_name]
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||
elif self.type not in ["http", "https", "ftp", "ftps"]:
|
||||
self.md5_expected = None
|
||||
else:
|
||||
self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
|
||||
if self.sha256_name in self.parm:
|
||||
self.sha256_expected = self.parm[self.sha256_name]
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||
elif self.type not in ["http", "https", "ftp", "ftps"]:
|
||||
self.sha256_expected = None
|
||||
else:
|
||||
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
|
||||
@@ -1033,14 +802,8 @@ class FetchData(object):
|
||||
elif self.localfile:
|
||||
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.
|
||||
if self.localpath and self.localpath.startswith(dldir):
|
||||
basepath = self.localpath
|
||||
elif self.localpath:
|
||||
basepath = dldir + os.sep + os.path.basename(self.localpath)
|
||||
else:
|
||||
basepath = dldir + os.sep + (self.basepath or self.basename)
|
||||
# Note: These files should always be in DL_DIR whereas localpath may not be.
|
||||
basepath = d.expand("${DL_DIR}/%s" % os.path.basename(self.localpath or self.basename))
|
||||
self.donestamp = basepath + '.done'
|
||||
self.lockfile = basepath + '.lock'
|
||||
|
||||
@@ -1109,7 +872,7 @@ class FetchMethod(object):
|
||||
def recommends_checksum(self, urldata):
|
||||
"""
|
||||
Is the backend on where checksumming is recommended (should warnings
|
||||
be displayed if there is no checksum)?
|
||||
by displayed if there is no checksum)?
|
||||
"""
|
||||
return False
|
||||
|
||||
@@ -1151,6 +914,7 @@ class FetchMethod(object):
|
||||
raise NoMethodError(url)
|
||||
|
||||
def unpack(self, urldata, rootdir, data):
|
||||
import subprocess
|
||||
iterate = False
|
||||
file = urldata.localpath
|
||||
|
||||
@@ -1161,7 +925,7 @@ class FetchMethod(object):
|
||||
(file, urldata.parm.get('unpack')))
|
||||
|
||||
dots = file.split(".")
|
||||
if dots[-1] in ['gz', 'bz2', 'Z', 'xz']:
|
||||
if dots[-1] in ['gz', 'bz2', 'Z']:
|
||||
efile = os.path.join(rootdir, os.path.basename('.'.join(dots[0:-1])))
|
||||
else:
|
||||
efile = file
|
||||
@@ -1297,8 +1061,14 @@ class FetchMethod(object):
|
||||
return rev
|
||||
|
||||
def sortable_revision(self, url, ud, d, name):
|
||||
"""
|
||||
|
||||
"""
|
||||
if hasattr(self, "_sortable_revision"):
|
||||
return self._sortable_revision(url, ud, d)
|
||||
|
||||
latest_rev = self._build_revision(url, ud, d, name)
|
||||
return True, str(latest_rev)
|
||||
return 'AUTOINC+%s' % str(latest_rev)
|
||||
|
||||
def generate_revision_key(self, url, ud, d, name):
|
||||
key = self._revision_key(url, ud, d, name)
|
||||
@@ -1403,7 +1173,6 @@ class Fetch(object):
|
||||
if isinstance(e, ChecksumError):
|
||||
logger.warn("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
|
||||
logger.debug(1, str(e))
|
||||
rename_bad_checksum(ud, e.checksum)
|
||||
elif isinstance(e, NoChecksumError):
|
||||
raise
|
||||
else:
|
||||
@@ -1513,13 +1282,11 @@ class Fetch(object):
|
||||
|
||||
from . import cvs
|
||||
from . import git
|
||||
from . import gitsm
|
||||
from . import local
|
||||
from . import svn
|
||||
from . import wget
|
||||
from . import svk
|
||||
from . import ssh
|
||||
from . import sftp
|
||||
from . import perforce
|
||||
from . import bzr
|
||||
from . import hg
|
||||
@@ -1530,11 +1297,9 @@ methods.append(local.Local())
|
||||
methods.append(wget.Wget())
|
||||
methods.append(svn.Svn())
|
||||
methods.append(git.Git())
|
||||
methods.append(gitsm.GitSM())
|
||||
methods.append(cvs.Cvs())
|
||||
methods.append(svk.Svk())
|
||||
methods.append(ssh.SSH())
|
||||
methods.append(sftp.SFTP())
|
||||
methods.append(perforce.Perforce())
|
||||
methods.append(bzr.Bzr())
|
||||
methods.append(hg.Hg())
|
||||
|
||||
@@ -132,12 +132,12 @@ class Bzr(FetchMethod):
|
||||
|
||||
return output.strip()
|
||||
|
||||
def sortable_revision(self, url, ud, d, name):
|
||||
def _sortable_revision(self, url, ud, d):
|
||||
"""
|
||||
Return a sortable revision number which in our case is the revision number
|
||||
"""
|
||||
|
||||
return False, self._build_revision(url, ud, d)
|
||||
return self._build_revision(url, ud, d)
|
||||
|
||||
def _build_revision(self, url, ud, d):
|
||||
return ud.revision
|
||||
|
||||
@@ -217,10 +217,6 @@ class Git(FetchMethod):
|
||||
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
|
||||
if os.path.islink(ud.fullmirror):
|
||||
os.unlink(ud.fullmirror)
|
||||
|
||||
os.chdir(ud.clonedir)
|
||||
logger.info("Creating tarball of git repository")
|
||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, os.path.join(".") ), d)
|
||||
@@ -238,7 +234,7 @@ class Git(FetchMethod):
|
||||
def_destsuffix = "git/"
|
||||
|
||||
destsuffix = ud.parm.get("destsuffix", def_destsuffix)
|
||||
destdir = ud.destdir = os.path.join(destdir, destsuffix)
|
||||
destdir = os.path.join(destdir, destsuffix)
|
||||
if os.path.exists(destdir):
|
||||
bb.utils.prunedir(destdir)
|
||||
|
||||
@@ -305,8 +301,8 @@ class Git(FetchMethod):
|
||||
username = ""
|
||||
|
||||
basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
|
||||
cmd = "%s ls-remote %s://%s%s%s refs/heads/%s refs/tags/%s" % \
|
||||
(basecmd, ud.proto, username, ud.host, ud.path, ud.branches[name], ud.branches[name])
|
||||
cmd = "%s ls-remote %s://%s%s%s %s" % \
|
||||
(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)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
"""
|
||||
BitBake 'Fetch' git submodules implementation
|
||||
"""
|
||||
|
||||
# Copyright (C) 2013 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2.git import Git
|
||||
from bb.fetch2 import runfetchcmd
|
||||
from bb.fetch2 import logger
|
||||
|
||||
class GitSM(Git):
|
||||
def supports(self, url, ud, d):
|
||||
"""
|
||||
Check to see if a given url can be fetched with git.
|
||||
"""
|
||||
return ud.type in ['gitsm']
|
||||
|
||||
def uses_submodules(self, ud, d):
|
||||
for name in ud.names:
|
||||
try:
|
||||
runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True)
|
||||
return True
|
||||
except bb.fetch.FetchError:
|
||||
pass
|
||||
return False
|
||||
|
||||
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"
|
||||
bb.utils.remove(tmpclonedir, True)
|
||||
os.mkdir(tmpclonedir)
|
||||
os.rename(ud.clonedir, gitdir)
|
||||
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
|
||||
os.chdir(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, loc, ud, d):
|
||||
Git.download(self, loc, ud, d)
|
||||
|
||||
os.chdir(ud.clonedir)
|
||||
submodules = self.uses_submodules(ud, d)
|
||||
if submodules:
|
||||
self.update_submodules(loc, ud, d)
|
||||
|
||||
def unpack(self, ud, destdir, d):
|
||||
Git.unpack(self, ud, destdir, d)
|
||||
|
||||
os.chdir(ud.destdir)
|
||||
submodules = self.uses_submodules(ud, d)
|
||||
if submodules:
|
||||
runfetchcmd("cp -r " + ud.clonedir + "/modules " + ud.destdir + "/.git/", d)
|
||||
runfetchcmd("git submodule init", d)
|
||||
runfetchcmd("git submodule update", d)
|
||||
|
||||
@@ -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)
|
||||
@@ -115,10 +112,7 @@ class Hg(FetchMethod):
|
||||
# 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":
|
||||
cmd = "%s update -C %s" % (basecmd, " ".join(options))
|
||||
else:
|
||||
|
||||
@@ -112,7 +112,7 @@ class Perforce(FetchMethod):
|
||||
base = path
|
||||
which = path.find('/...')
|
||||
if which != -1:
|
||||
base = path[:which-1]
|
||||
base = path[:which]
|
||||
|
||||
base = self._strip_leading_slashes(base)
|
||||
|
||||
@@ -170,7 +170,7 @@ class Perforce(FetchMethod):
|
||||
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()]
|
||||
p4file = p4file.strip()
|
||||
|
||||
if not p4file:
|
||||
raise FetchError("Fetch: unable to get the P4 files from %s" % depot, loc)
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
"""
|
||||
BitBake SFTP Fetch implementation
|
||||
|
||||
Class for fetching files via SFTP. It tries to adhere to the (now
|
||||
expired) IETF Internet Draft for "Uniform Resource Identifier (URI)
|
||||
Scheme for Secure File Transfer Protocol (SFTP) and Secure Shell
|
||||
(SSH)" (SECSH URI).
|
||||
|
||||
It uses SFTP (as to adhere to the SECSH URI specification). It only
|
||||
supports key based authentication, not password. This class, unlike
|
||||
the SSH fetcher, does not support fetching a directory tree from the
|
||||
remote.
|
||||
|
||||
http://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
|
||||
https://www.iana.org/assignments/uri-schemes/prov/sftp
|
||||
https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
||||
|
||||
Please note that '/' is used as host path seperator, and not ":"
|
||||
as you may be used to from the scp/sftp commands. You can use a
|
||||
~ (tilde) to specify a path relative to your home directory.
|
||||
(The /~user/ syntax, for specyfing a path relative to another
|
||||
user's home directory is not supported.) Note that the tilde must
|
||||
still follow the host path seperator ("/"). See exampels below.
|
||||
|
||||
Example SRC_URIs:
|
||||
|
||||
SRC_URI = "sftp://host.example.com/dir/path.file.txt"
|
||||
|
||||
A path relative to your home directory.
|
||||
|
||||
SRC_URI = "sftp://host.example.com/~/dir/path.file.txt"
|
||||
|
||||
You can also specify a username (specyfing password in the
|
||||
URI is not supported, use SSH keys to authenticate):
|
||||
|
||||
SRC_URI = "sftp://user@host.example.com/dir/path.file.txt"
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (C) 2013, Olof Johansson <olof.johansson@axis.com>
|
||||
#
|
||||
# Based in part on bb.fetch2.wget:
|
||||
# Copyright (C) 2003, 2004 Chris Larson
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||
|
||||
import os
|
||||
import bb
|
||||
import urllib
|
||||
import commands
|
||||
from bb import data
|
||||
from bb.fetch2 import URI
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
|
||||
class SFTP(FetchMethod):
|
||||
"""Class to fetch urls via 'sftp'"""
|
||||
|
||||
def supports(self, url, ud, d):
|
||||
"""
|
||||
Check to see if a given url can be fetched with sftp.
|
||||
"""
|
||||
return ud.type in ['sftp']
|
||||
|
||||
def recommends_checksum(self, urldata):
|
||||
return True
|
||||
|
||||
def urldata_init(self, ud, d):
|
||||
if 'protocol' in ud.parm and ud.parm['protocol'] == 'git':
|
||||
raise bb.fetch2.ParameterError(
|
||||
"Invalid protocol - if you wish to fetch from a " +
|
||||
"git repository using ssh, you need to use the " +
|
||||
"git:// prefix with protocol=ssh", ud.url)
|
||||
|
||||
if 'downloadfilename' in ud.parm:
|
||||
ud.basename = ud.parm['downloadfilename']
|
||||
else:
|
||||
ud.basename = os.path.basename(ud.path)
|
||||
|
||||
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
|
||||
|
||||
def download(self, uri, ud, d):
|
||||
"""Fetch urls"""
|
||||
|
||||
urlo = URI(uri)
|
||||
basecmd = 'sftp -oPasswordAuthentication=no'
|
||||
port = ''
|
||||
if urlo.port:
|
||||
port = '-P %d' % urlo.port
|
||||
urlo.port = None
|
||||
|
||||
dldir = data.getVar('DL_DIR', d, True)
|
||||
lpath = os.path.join(dldir, ud.localfile)
|
||||
|
||||
user = ''
|
||||
if urlo.userinfo:
|
||||
user = urlo.userinfo + '@'
|
||||
|
||||
path = urlo.path
|
||||
|
||||
# Supoprt URIs relative to the user's home directory, with
|
||||
# the tilde syntax. (E.g. <sftp://example.com/~/foo.diff>).
|
||||
if path[:3] == '/~/':
|
||||
path = path[3:]
|
||||
|
||||
remote = '%s%s:%s' % (user, urlo.hostname, path)
|
||||
|
||||
cmd = '%s %s %s %s' % (basecmd, port, commands.mkarg(remote),
|
||||
commands.mkarg(lpath))
|
||||
|
||||
bb.fetch2.check_network_access(d, cmd, uri)
|
||||
runfetchcmd(cmd, d)
|
||||
return True
|
||||
@@ -10,12 +10,6 @@ IETF secsh internet draft:
|
||||
Currently does not support the sftp parameters, as this uses scp
|
||||
Also does not support the 'fingerprint' connection parameter.
|
||||
|
||||
Please note that '/' is used as host, path separator not ':' as you may
|
||||
be used to, also '~' can be used to specify user HOME, but again after '/'
|
||||
|
||||
Example SRC_URI:
|
||||
SRC_URI = "ssh://user@host.example.com/dir/path/file.txt"
|
||||
SRC_URI = "ssh://user@host.example.com/~/file.txt"
|
||||
'''
|
||||
|
||||
# Copyright (C) 2006 OpenedHand Ltd.
|
||||
@@ -78,19 +72,15 @@ class SSH(FetchMethod):
|
||||
def supports_checksum(self, urldata):
|
||||
return False
|
||||
|
||||
def urldata_init(self, urldata, d):
|
||||
if 'protocol' in urldata.parm and urldata.parm['protocol'] == 'git':
|
||||
raise bb.fetch2.ParameterError(
|
||||
"Invalid protocol - if you wish to fetch from a git " +
|
||||
"repository using ssh, you need to use " +
|
||||
"git:// prefix with protocol=ssh", urldata.url)
|
||||
def localpath(self, url, urldata, d):
|
||||
m = __pattern__.match(urldata.url)
|
||||
path = m.group('path')
|
||||
host = m.group('host')
|
||||
urldata.localpath = os.path.join(d.getVar('DL_DIR', True), os.path.basename(path))
|
||||
lpath = os.path.join(data.getVar('DL_DIR', d, True), host, os.path.basename(path))
|
||||
return lpath
|
||||
|
||||
def download(self, url, urldata, d):
|
||||
dldir = d.getVar('DL_DIR', True)
|
||||
dldir = data.getVar('DL_DIR', d, True)
|
||||
|
||||
m = __pattern__.match(url)
|
||||
path = m.group('path')
|
||||
@@ -99,10 +89,16 @@ class SSH(FetchMethod):
|
||||
user = m.group('user')
|
||||
password = m.group('pass')
|
||||
|
||||
ldir = os.path.join(dldir, host)
|
||||
lpath = os.path.join(ldir, os.path.basename(path))
|
||||
|
||||
if not os.path.exists(ldir):
|
||||
os.makedirs(ldir)
|
||||
|
||||
if port:
|
||||
portarg = '-P %s' % port
|
||||
port = '-P %s' % port
|
||||
else:
|
||||
portarg = ''
|
||||
port = ''
|
||||
|
||||
if user:
|
||||
fr = user
|
||||
@@ -116,9 +112,9 @@ class SSH(FetchMethod):
|
||||
|
||||
import commands
|
||||
cmd = 'scp -B -r %s %s %s/' % (
|
||||
portarg,
|
||||
port,
|
||||
commands.mkarg(fr),
|
||||
commands.mkarg(dldir)
|
||||
commands.mkarg(ldir)
|
||||
)
|
||||
|
||||
bb.fetch2.check_network_access(d, cmd, urldata.url)
|
||||
|
||||
@@ -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
|
||||
@@ -82,8 +81,6 @@ class Svn(FetchMethod):
|
||||
|
||||
options = []
|
||||
|
||||
options.append("--no-auth-cache")
|
||||
|
||||
if ud.user:
|
||||
options.append("--username %s" % ud.user)
|
||||
|
||||
@@ -92,8 +89,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:
|
||||
@@ -170,22 +165,23 @@ class Svn(FetchMethod):
|
||||
"""
|
||||
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, url, ud, d, name):
|
||||
def _sortable_revision(self, url, ud, d):
|
||||
"""
|
||||
Return a sortable revision number which in our case is the revision number
|
||||
"""
|
||||
|
||||
return False, self._build_revision(url, ud, d)
|
||||
return self._build_revision(url, ud, d)
|
||||
|
||||
def _build_revision(self, url, ud, d):
|
||||
return ud.revision
|
||||
|
||||
@@ -32,6 +32,8 @@ import urllib
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import encodeurl
|
||||
from bb.fetch2 import decodeurl
|
||||
from bb.fetch2 import logger
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
@@ -63,10 +65,8 @@ class Wget(FetchMethod):
|
||||
|
||||
basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
|
||||
|
||||
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))
|
||||
basecmd += " -O " + dldir + os.sep + ud.localfile
|
||||
if 'downloadfilename' in ud.parm:
|
||||
basecmd += " -O ${DL_DIR}/" + ud.localfile
|
||||
|
||||
if checkonly:
|
||||
fetchcmd = d.getVar("CHECKCOMMAND_wget", True) or d.expand(basecmd + " --spider '${URI}'")
|
||||
@@ -77,6 +77,9 @@ class Wget(FetchMethod):
|
||||
fetchcmd = d.getVar("FETCHCOMMAND_wget", True) or d.expand(basecmd + " -P ${DL_DIR} '${URI}'")
|
||||
|
||||
uri = uri.split(";")[0]
|
||||
uri_decoded = list(decodeurl(uri))
|
||||
uri_type = uri_decoded[0]
|
||||
uri_host = uri_decoded[1]
|
||||
|
||||
fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0])
|
||||
fetchcmd = fetchcmd.replace("${FILE}", ud.basename)
|
||||
|
||||
@@ -17,7 +17,24 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
"""
|
||||
What is a method pool?
|
||||
|
||||
BitBake has a global method scope where .bb, .inc and .bbclass
|
||||
files can install methods. These methods are parsed from strings.
|
||||
To avoid recompiling and executing these string we introduce
|
||||
a method pool to do this task.
|
||||
|
||||
This pool will be used to compile and execute the functions. It
|
||||
will be smart enough to
|
||||
"""
|
||||
|
||||
from bb.utils import better_compile, better_exec
|
||||
from bb import error
|
||||
|
||||
# A dict of function names we have seen
|
||||
_parsed_fns = { }
|
||||
|
||||
def insert_method(modulename, code, fn):
|
||||
"""
|
||||
@@ -27,3 +44,28 @@ def insert_method(modulename, code, fn):
|
||||
comp = better_compile(code, modulename, fn )
|
||||
better_exec(comp, None, code, fn)
|
||||
|
||||
# now some instrumentation
|
||||
code = comp.co_names
|
||||
for name in code:
|
||||
if name in ['None', 'False']:
|
||||
continue
|
||||
elif name in _parsed_fns and not _parsed_fns[name] == modulename:
|
||||
error("The function %s defined in %s was already declared in %s. BitBake has a global python function namespace so shared functions should be declared in a common include file rather than being duplicated, or if the functions are different, please use different function names." % (name, modulename, _parsed_fns[name]))
|
||||
else:
|
||||
_parsed_fns[name] = modulename
|
||||
|
||||
# A dict of modules the parser has finished with
|
||||
_parsed_methods = {}
|
||||
|
||||
def parsed_module(modulename):
|
||||
"""
|
||||
Has module been parsed?
|
||||
"""
|
||||
return modulename in _parsed_methods
|
||||
|
||||
def set_parsed_module(modulename):
|
||||
"""
|
||||
Set module as parsed
|
||||
"""
|
||||
_parsed_methods[modulename] = True
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ def getDiskData(BBDirs, configuration):
|
||||
printErr("Invalid disk space value in BB_DISKMON_DIRS: %s" % pathSpaceInodeRe.group(3))
|
||||
return None
|
||||
else:
|
||||
# None means that it is not specified
|
||||
# 0 means that it is not specified
|
||||
minSpace = None
|
||||
|
||||
minInode = pathSpaceInodeRe.group(4)
|
||||
@@ -117,7 +117,7 @@ def getDiskData(BBDirs, configuration):
|
||||
printErr("Invalid inode value in BB_DISKMON_DIRS: %s" % pathSpaceInodeRe.group(4))
|
||||
return None
|
||||
else:
|
||||
# None means that it is not specified
|
||||
# 0 means that it is not specified
|
||||
minInode = None
|
||||
|
||||
if minSpace is None and minInode is None:
|
||||
@@ -127,9 +127,8 @@ def getDiskData(BBDirs, configuration):
|
||||
# DL_DIR may not exist at the very beginning
|
||||
if not os.path.exists(path):
|
||||
bb.utils.mkdirhier(path)
|
||||
dev = getMountedDev(path)
|
||||
# Use path/action as the key
|
||||
devDict[os.path.join(path, action)] = [dev, minSpace, minInode]
|
||||
mountedDev = getMountedDev(path)
|
||||
devDict[mountedDev] = action, path, minSpace, minInode
|
||||
|
||||
return devDict
|
||||
|
||||
@@ -193,10 +192,10 @@ class diskMonitor:
|
||||
# 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
|
||||
self.preFreeI[k] = 0
|
||||
self.checked[k] = False
|
||||
for dev in self.devDict:
|
||||
self.preFreeS[dev] = 0
|
||||
self.preFreeI[dev] = 0
|
||||
self.checked[dev] = False
|
||||
if self.spaceInterval is None and self.inodeInterval is None:
|
||||
self.enableMonitor = False
|
||||
|
||||
@@ -205,61 +204,46 @@ class diskMonitor:
|
||||
""" Take action for the monitor """
|
||||
|
||||
if self.enableMonitor:
|
||||
for k in self.devDict:
|
||||
path = os.path.dirname(k)
|
||||
action = os.path.basename(k)
|
||||
dev = self.devDict[k][0]
|
||||
minSpace = self.devDict[k][1]
|
||||
minInode = self.devDict[k][2]
|
||||
|
||||
st = os.statvfs(path)
|
||||
for dev in self.devDict:
|
||||
st = os.statvfs(self.devDict[dev][1])
|
||||
|
||||
# The free space, float point number
|
||||
freeSpace = st.f_bavail * st.f_frsize
|
||||
|
||||
if minSpace and freeSpace < minSpace:
|
||||
if self.devDict[dev][2] and freeSpace < self.devDict[dev][2]:
|
||||
# Always show warning, the self.checked would always be False if the action is WARN
|
||||
if self.preFreeS[k] == 0 or self.preFreeS[k] - freeSpace > self.spaceInterval and not self.checked[k]:
|
||||
logger.warn("The free space of %s (%s) is running low (%.3fGB left)" % \
|
||||
(path, dev, freeSpace / 1024 / 1024 / 1024.0))
|
||||
self.preFreeS[k] = freeSpace
|
||||
if self.preFreeS[dev] == 0 or self.preFreeS[dev] - freeSpace > self.spaceInterval and not self.checked[dev]:
|
||||
logger.warn("The free space of %s is running low (%.3fGB left)" % (dev, freeSpace / 1024 / 1024 / 1024.0))
|
||||
self.preFreeS[dev] = freeSpace
|
||||
|
||||
if action == "STOPTASKS" and not self.checked[k]:
|
||||
logger.error("No new tasks can be executed since the disk space monitor action is \"STOPTASKS\"!")
|
||||
self.checked[k] = True
|
||||
if self.devDict[dev][0] == "STOPTASKS" and not self.checked[dev]:
|
||||
logger.error("No new tasks can be excuted since the disk space monitor action is \"STOPTASKS\"!")
|
||||
self.checked[dev] = True
|
||||
rq.finish_runqueue(False)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, path), self.configuration)
|
||||
elif action == "ABORT" and not self.checked[k]:
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, self.devDict[dev][1]), self.configuration)
|
||||
elif self.devDict[dev][0] == "ABORT" and not self.checked[dev]:
|
||||
logger.error("Immediately abort since the disk space monitor action is \"ABORT\"!")
|
||||
self.checked[k] = True
|
||||
self.checked[dev] = True
|
||||
rq.finish_runqueue(True)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, path), self.configuration)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, self.devDict[dev][1]), self.configuration)
|
||||
|
||||
# The free inodes, float point number
|
||||
freeInode = st.f_favail
|
||||
|
||||
if minInode and freeInode < minInode:
|
||||
# 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.info("Inode check for %s is unavaliable, will remove it from disk monitor" % path)
|
||||
self.devDict[k][2] = None
|
||||
continue
|
||||
if self.devDict[dev][3] and freeInode < self.devDict[dev][3]:
|
||||
# Always show warning, the self.checked would always be False if the action is WARN
|
||||
if self.preFreeI[k] == 0 or self.preFreeI[k] - freeInode > self.inodeInterval and not self.checked[k]:
|
||||
logger.warn("The free inode of %s (%s) is running low (%.3fK left)" % \
|
||||
(path, dev, freeInode / 1024.0))
|
||||
self.preFreeI[k] = freeInode
|
||||
if self.preFreeI[dev] == 0 or self.preFreeI[dev] - freeInode > self.inodeInterval and not self.checked[dev]:
|
||||
logger.warn("The free inode of %s is running low (%.3fK left)" % (dev, freeInode / 1024.0))
|
||||
self.preFreeI[dev] = freeInode
|
||||
|
||||
if action == "STOPTASKS" and not self.checked[k]:
|
||||
logger.error("No new tasks can be executed since the disk space monitor action is \"STOPTASKS\"!")
|
||||
self.checked[k] = True
|
||||
if self.devDict[dev][0] == "STOPTASKS" and not self.checked[dev]:
|
||||
logger.error("No new tasks can be excuted since the disk space monitor action is \"STOPTASKS\"!")
|
||||
self.checked[dev] = True
|
||||
rq.finish_runqueue(False)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeInode, path), self.configuration)
|
||||
elif action == "ABORT" and not self.checked[k]:
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeSpace, self.devDict[dev][1]), self.configuration)
|
||||
elif self.devDict[dev][0] == "ABORT" and not self.checked[dev]:
|
||||
logger.error("Immediately abort since the disk space monitor action is \"ABORT\"!")
|
||||
self.checked[k] = True
|
||||
self.checked[dev] = True
|
||||
rq.finish_runqueue(True)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeInode, path), self.configuration)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeSpace, self.devDict[dev][1]), self.configuration)
|
||||
return
|
||||
|
||||
@@ -146,7 +146,8 @@ def init_msgconfig(verbose, debug, debug_domains = []):
|
||||
bb.msg.loggerVerboseLogs = True
|
||||
bb.msg.loggerDefaultDomains = debug_domains
|
||||
|
||||
def constructLogOptions():
|
||||
def addDefaultlogFilter(handler):
|
||||
|
||||
debug = loggerDefaultDebugLevel
|
||||
verbose = loggerDefaultVerbose
|
||||
domains = loggerDefaultDomains
|
||||
@@ -162,10 +163,6 @@ def constructLogOptions():
|
||||
for (domainarg, iterator) in groupby(domains):
|
||||
dlevel = len(tuple(iterator))
|
||||
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
|
||||
return level, debug_domains
|
||||
|
||||
def addDefaultlogFilter(handler):
|
||||
level, debug_domains = constructLogOptions()
|
||||
|
||||
BBLogFilter(handler, level, debug_domains)
|
||||
|
||||
|
||||
@@ -73,17 +73,9 @@ def 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:
|
||||
@@ -110,17 +102,11 @@ 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
|
||||
|
||||
|
||||
@@ -147,9 +147,10 @@ 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(string.maketrans('/.+-@', '_____'))))
|
||||
text = "def %s(d):\n" % (funcname) + text
|
||||
bb.methodpool.insert_method(funcname, text, self.filename)
|
||||
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(string.maketrans('/.+-', '____'))))
|
||||
if not funcname in bb.methodpool._parsed_fns:
|
||||
text = "def %s(d):\n" % (funcname) + text
|
||||
bb.methodpool.insert_method(funcname, text, self.filename)
|
||||
anonfuncs = data.getVar('__BBANONFUNCS') or []
|
||||
anonfuncs.append(funcname)
|
||||
data.setVar('__BBANONFUNCS', anonfuncs)
|
||||
@@ -170,7 +171,8 @@ class PythonMethodNode(AstNode):
|
||||
# 'this' file. This means we will not parse methods from
|
||||
# bb classes twice
|
||||
text = '\n'.join(self.body)
|
||||
bb.methodpool.insert_method(self.modulename, text, self.filename)
|
||||
if not bb.methodpool.parsed_module(self.modulename):
|
||||
bb.methodpool.insert_method(self.modulename, text, self.filename)
|
||||
data.setVarFlag(self.function, "func", 1)
|
||||
data.setVarFlag(self.function, "python", 1)
|
||||
data.setVar(self.function, text)
|
||||
@@ -320,7 +322,8 @@ def finalize(fn, d, variant = None):
|
||||
all_handlers = {}
|
||||
for var in d.getVar('__BBHANDLERS') or []:
|
||||
# try to add the handler
|
||||
bb.event.register(var, d.getVar(var), (d.getVarFlag(var, "eventmask", True) or "").split())
|
||||
handler = d.getVar(var)
|
||||
bb.event.register(var, handler)
|
||||
|
||||
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ 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
|
||||
|
||||
@@ -107,7 +104,6 @@ def get_statements(filename, absolute_filename, base_name):
|
||||
if not s: break
|
||||
s = s.rstrip()
|
||||
feeder(lineno, s, filename, base_name, statements)
|
||||
file.close()
|
||||
if __inpython__:
|
||||
# add a blank line to close out any python definition
|
||||
feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
|
||||
@@ -170,6 +166,10 @@ def handle(fn, d, include):
|
||||
if oldfile:
|
||||
d.setVar("FILE", oldfile)
|
||||
|
||||
# we have parsed the bb class now
|
||||
if ext == ".bbclass" or ext == ".inc":
|
||||
bb.methodpool.set_parsed_module(base_name)
|
||||
|
||||
return d
|
||||
|
||||
def feeder(lineno, s, fn, root, statements):
|
||||
|
||||
@@ -29,30 +29,7 @@ import logging
|
||||
import bb.utils
|
||||
from bb.parse import ParseError, resolve_file, ast, logger
|
||||
|
||||
__config_regexp__ = re.compile( r"""
|
||||
^
|
||||
(?P<exp>export\s*)?
|
||||
(?P<var>[a-zA-Z0-9\-~_+.${}/]+?)
|
||||
(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?
|
||||
|
||||
\s* (
|
||||
(?P<colon>:=) |
|
||||
(?P<lazyques>\?\?=) |
|
||||
(?P<ques>\?=) |
|
||||
(?P<append>\+=) |
|
||||
(?P<prepend>=\+) |
|
||||
(?P<predot>=\.) |
|
||||
(?P<postdot>\.=) |
|
||||
=
|
||||
) \s*
|
||||
|
||||
(?!'[^']*'[^']*'$)
|
||||
(?!\"[^\"]*\"[^\"]*\"$)
|
||||
(?P<apo>['\"])
|
||||
(?P<value>.*)
|
||||
(?P=apo)
|
||||
$
|
||||
""", re.X)
|
||||
__config_regexp__ = re.compile( r"(?P<exp>export\s*)?(?P<var>[a-zA-Z0-9\-~_+.${}/]+)(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?\s*((?P<colon>:=)|(?P<lazyques>\?\?=)|(?P<ques>\?=)|(?P<append>\+=)|(?P<prepend>=\+)|(?P<predot>=\.)|(?P<postdot>\.=)|=)\s*(?!'[^']*'[^']*'$)(?!\"[^\"]*\"[^\"]*\"$)(?P<apo>['\"])(?P<value>.*)(?P=apo)$")
|
||||
__include_regexp__ = re.compile( r"include\s+(.+)" )
|
||||
__require_regexp__ = re.compile( r"require\s+(.+)" )
|
||||
__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
|
||||
@@ -82,24 +59,17 @@ 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:
|
||||
ret = handle(fn, data, True)
|
||||
except (IOError, OSError):
|
||||
except IOError:
|
||||
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
|
||||
@@ -152,8 +122,6 @@ def handle(fn, data, include):
|
||||
if oldfile:
|
||||
data.setVar('FILE', oldfile)
|
||||
|
||||
f.close()
|
||||
|
||||
for f in confFilters:
|
||||
f(fn, data)
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def _logged_communicate(pipe, log, input):
|
||||
rlist = rin
|
||||
try:
|
||||
r,w,e = select.select (rlist, [], [])
|
||||
except OSError as e:
|
||||
except OSError, e:
|
||||
if e.errno != errno.EINTR:
|
||||
raise
|
||||
|
||||
@@ -102,10 +102,6 @@ def _logged_communicate(pipe, log, input):
|
||||
log.write(data)
|
||||
finally:
|
||||
log.flush()
|
||||
if pipe.stdout is not None:
|
||||
pipe.stdout.close()
|
||||
if pipe.stderr is not None:
|
||||
pipe.stderr.close()
|
||||
return ''.join(outdata), ''.join(errdata)
|
||||
|
||||
def run(cmd, input=None, log=None, **options):
|
||||
|
||||
@@ -92,9 +92,9 @@ def sortPriorities(pn, dataCache, pkg_pn = None):
|
||||
priorities[priority][preference] = []
|
||||
priorities[priority][preference].append(f)
|
||||
tmp_pn = []
|
||||
for pri in sorted(priorities):
|
||||
for pri in sorted(priorities, lambda a, b: a - b):
|
||||
tmp_pref = []
|
||||
for pref in sorted(priorities[pri]):
|
||||
for pref in sorted(priorities[pri], lambda a, b: b - a):
|
||||
tmp_pref.extend(priorities[pri][pref])
|
||||
tmp_pn = [tmp_pref] + tmp_pn
|
||||
|
||||
@@ -341,7 +341,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
||||
if numberPreferred > 1:
|
||||
logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s", item, preferred, preferred_vars)
|
||||
|
||||
logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
|
||||
logger.debug(1, "sorted providers for %s are: %s", item, eligible)
|
||||
|
||||
return eligible, numberPreferred
|
||||
|
||||
@@ -376,6 +376,5 @@ def getRuntimeProviders(dataCache, rdepend):
|
||||
regexp_cache[pattern] = regexp
|
||||
if regexp.match(rdepend):
|
||||
rproviders += dataCache.packages_dynamic[pattern]
|
||||
logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
|
||||
|
||||
return rproviders
|
||||
|
||||
@@ -292,7 +292,7 @@ class WordLexer:
|
||||
elif sep=='${':
|
||||
parsefunc = self._parse_parameter
|
||||
else:
|
||||
raise NotImplementedError(sep)
|
||||
raise NotImplementedError()
|
||||
|
||||
pos, closed = parsefunc(buf, result, eof)
|
||||
return pos, closed
|
||||
|
||||
@@ -28,17 +28,10 @@ import sys
|
||||
import signal
|
||||
import stat
|
||||
import fcntl
|
||||
import errno
|
||||
import logging
|
||||
import bb
|
||||
from bb import msg, data, event
|
||||
from bb import monitordisk
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
bblogger = logging.getLogger("BitBake")
|
||||
logger = logging.getLogger("BitBake.RunQueue")
|
||||
@@ -84,6 +77,7 @@ runQueueRunning = 6
|
||||
runQueueFailed = 7
|
||||
runQueueCleanUp = 8
|
||||
runQueueComplete = 9
|
||||
runQueueChildProcess = 10
|
||||
|
||||
class RunQueueScheduler(object):
|
||||
"""
|
||||
@@ -217,15 +211,6 @@ class RunQueueData:
|
||||
ret.extend([nam])
|
||||
return ret
|
||||
|
||||
def get_task_name(self, task):
|
||||
return self.runq_task[task]
|
||||
|
||||
def get_task_file(self, task):
|
||||
return self.taskData.fn_index[self.runq_fnid[task]]
|
||||
|
||||
def get_task_hash(self, task):
|
||||
return self.runq_hash[task]
|
||||
|
||||
def get_user_idstring(self, task, task_name_suffix = ""):
|
||||
fn = self.taskData.fn_index[self.runq_fnid[task]]
|
||||
taskname = self.runq_task[task] + task_name_suffix
|
||||
@@ -391,7 +376,6 @@ class RunQueueData:
|
||||
|
||||
runq_build = []
|
||||
recursivetasks = {}
|
||||
recursiveitasks = {}
|
||||
recursivetasksselfref = set()
|
||||
|
||||
taskData = self.taskData
|
||||
@@ -488,7 +472,7 @@ class RunQueueData:
|
||||
if depdata is not None:
|
||||
taskid = taskData.gettask_id_fromfnid(depdata, idependtask)
|
||||
if taskid is None:
|
||||
bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskData.tasks_name[task], fn, idependtask, taskData.fn_index[depdata]))
|
||||
bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskData.tasks_name[task], fn, idependtask, dep))
|
||||
depends.add(taskid)
|
||||
irdepends = taskData.tasks_irdepends[task]
|
||||
for (depid, idependtask) in irdepends:
|
||||
@@ -498,7 +482,7 @@ class RunQueueData:
|
||||
if depdata is not None:
|
||||
taskid = taskData.gettask_id_fromfnid(depdata, idependtask)
|
||||
if taskid is None:
|
||||
bb.msg.fatal("RunQueue", "Task %s in %s rdepends upon non-existent task %s in %s" % (taskData.tasks_name[task], fn, idependtask, taskData.fn_index[depdata]))
|
||||
bb.msg.fatal("RunQueue", "Task %s in %s rdepends upon non-existent task %s in %s" % (taskData.tasks_name[task], fn, idependtask, dep))
|
||||
depends.add(taskid)
|
||||
|
||||
# Resolve recursive 'recrdeptask' dependencies (Part A)
|
||||
@@ -514,12 +498,6 @@ class RunQueueData:
|
||||
if taskData.tasks_name[task] in tasknames:
|
||||
recursivetasksselfref.add(task)
|
||||
|
||||
if 'recideptask' in task_deps and taskData.tasks_name[task] in task_deps['recideptask']:
|
||||
recursiveitasks[task] = []
|
||||
for t in task_deps['recideptask'][taskData.tasks_name[task]].split():
|
||||
newdep = taskData.gettask_id_fromfnid(fnid, t)
|
||||
recursiveitasks[task].append(newdep)
|
||||
|
||||
self.runq_fnid.append(taskData.tasks_fnid[task])
|
||||
self.runq_task.append(taskData.tasks_name[task])
|
||||
self.runq_depends.append(depends)
|
||||
@@ -552,10 +530,6 @@ class RunQueueData:
|
||||
generate_recdeps(n)
|
||||
generate_recdeps(task)
|
||||
|
||||
if task in recursiveitasks:
|
||||
for dep in recursiveitasks[task]:
|
||||
generate_recdeps(dep)
|
||||
|
||||
# Remove circular references so that do_a[recrdeptask] = "do_a do_b" can work
|
||||
for task in recursivetasks:
|
||||
extradeps[task].difference_update(recursivetasksselfref)
|
||||
@@ -610,13 +584,7 @@ class RunQueueData:
|
||||
continue
|
||||
|
||||
if target[1] not in taskData.tasks_lookup[fnid]:
|
||||
import difflib
|
||||
close_matches = difflib.get_close_matches(target[1], taskData.tasks_lookup[fnid], cutoff=0.7)
|
||||
if close_matches:
|
||||
extra = ". Close matches:\n %s" % "\n ".join(close_matches)
|
||||
else:
|
||||
extra = ""
|
||||
bb.msg.fatal("RunQueue", "Task %s does not exist for target %s%s" % (target[1], target[0], extra))
|
||||
bb.msg.fatal("RunQueue", "Task %s does not exist for target %s" % (target[1], target[0]))
|
||||
|
||||
listid = taskData.tasks_lookup[fnid][target[1]]
|
||||
|
||||
@@ -705,14 +673,6 @@ class RunQueueData:
|
||||
prov_list[prov].append(fn)
|
||||
for prov in prov_list:
|
||||
if len(prov_list[prov]) > 1 and prov not in self.multi_provider_whitelist:
|
||||
seen_pn = []
|
||||
# If two versions of the same PN are being built its fatal, we don't support it.
|
||||
for fn in prov_list[prov]:
|
||||
pn = self.dataCache.pkg_fn[fn]
|
||||
if pn not in seen_pn:
|
||||
seen_pn.append(pn)
|
||||
else:
|
||||
bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn))
|
||||
msg = "Multiple .bb files are due to be built which each provide %s (%s)." % (prov, " ".join(prov_list[prov]))
|
||||
if self.warn_multi_bb:
|
||||
logger.warn(msg)
|
||||
@@ -720,6 +680,7 @@ class RunQueueData:
|
||||
msg += "\n This usually means one provides something the other doesn't and should."
|
||||
logger.error(msg)
|
||||
|
||||
|
||||
# Create a whitelist usable by the stamp checks
|
||||
stampfnwhitelist = []
|
||||
for entry in self.stampwhitelist.split():
|
||||
@@ -742,9 +703,6 @@ class RunQueueData:
|
||||
|
||||
def invalidate_task(fn, taskname, error_nostamp):
|
||||
taskdep = self.dataCache.task_deps[fn]
|
||||
fnid = self.taskData.getfn_id(fn)
|
||||
if taskname not in taskData.tasks_lookup[fnid]:
|
||||
logger.warn("Task %s does not exist, invalidating this task will have no effect" % taskname)
|
||||
if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
|
||||
if error_nostamp:
|
||||
bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
|
||||
@@ -835,92 +793,6 @@ class RunQueue:
|
||||
self.dm = monitordisk.diskMonitor(cfgData)
|
||||
|
||||
self.rqexe = None
|
||||
self.worker = None
|
||||
self.workerpipe = None
|
||||
self.fakeworker = None
|
||||
self.fakeworkerpipe = None
|
||||
|
||||
def _start_worker(self, fakeroot = False, rqexec = None):
|
||||
logger.debug(1, "Starting bitbake-worker")
|
||||
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", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
|
||||
else:
|
||||
worker = subprocess.Popen(["bitbake-worker", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
bb.utils.nonblockingfd(worker.stdout)
|
||||
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,
|
||||
"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,
|
||||
"logdefaultverboselogs" : bb.msg.loggerVerboseLogs,
|
||||
"logdefaultdomain" : bb.msg.loggerDefaultDomains,
|
||||
"prhost" : self.cooker.prhost,
|
||||
"buildname" : self.cfgData.getVar("BUILDNAME", True),
|
||||
"date" : self.cfgData.getVar("DATE", True),
|
||||
"time" : self.cfgData.getVar("TIME", True),
|
||||
}
|
||||
|
||||
worker.stdin.write("<cookerconfig>" + pickle.dumps(self.cooker.configuration) + "</cookerconfig>")
|
||||
worker.stdin.write("<workerdata>" + pickle.dumps(workerdata) + "</workerdata>")
|
||||
worker.stdin.flush()
|
||||
|
||||
return worker, workerpipe
|
||||
|
||||
def _teardown_worker(self, worker, workerpipe):
|
||||
if not worker:
|
||||
return
|
||||
logger.debug(1, "Teardown for bitbake-worker")
|
||||
worker.stdin.write("<quit></quit>")
|
||||
worker.stdin.flush()
|
||||
while worker.returncode is None:
|
||||
workerpipe.read()
|
||||
worker.poll()
|
||||
while workerpipe.read():
|
||||
continue
|
||||
workerpipe.close()
|
||||
|
||||
def start_worker(self):
|
||||
if self.worker:
|
||||
self.teardown_workers()
|
||||
self.worker, self.workerpipe = self._start_worker()
|
||||
|
||||
def start_fakeworker(self, rqexec):
|
||||
if not self.fakeworker:
|
||||
self.fakeworker, self.fakeworkerpipe = self._start_worker(True, rqexec)
|
||||
|
||||
def teardown_workers(self):
|
||||
self._teardown_worker(self.worker, self.workerpipe)
|
||||
self.worker = None
|
||||
self.workerpipe = None
|
||||
self._teardown_worker(self.fakeworker, self.fakeworkerpipe)
|
||||
self.fakeworker = None
|
||||
self.fakeworkerpipe = None
|
||||
|
||||
def read_workers(self):
|
||||
self.workerpipe.read()
|
||||
if self.fakeworkerpipe:
|
||||
self.fakeworkerpipe.read()
|
||||
|
||||
def active_fds(self):
|
||||
fds = []
|
||||
if self.workerpipe:
|
||||
fds.append(self.workerpipe.input)
|
||||
if self.fakeworkerpipe:
|
||||
fds.append(self.fakeworkerpipe.input)
|
||||
return fds
|
||||
|
||||
def check_stamp_task(self, task, taskname = None, recurse = False, cache = None):
|
||||
def get_timestamp(f):
|
||||
@@ -999,7 +871,7 @@ class RunQueue:
|
||||
(if the abort on failure configuration option isn't set)
|
||||
"""
|
||||
|
||||
retval = True
|
||||
retval = 0.5
|
||||
|
||||
if self.state is runQueuePrepare:
|
||||
self.rqexe = RunQueueExecuteDummy(self)
|
||||
@@ -1008,16 +880,10 @@ class RunQueue:
|
||||
else:
|
||||
self.state = runQueueSceneInit
|
||||
|
||||
# we are ready to run, see if any UI client needs the dependency info
|
||||
if bb.cooker.CookerFeatures.SEND_DEPENDS_TREE in self.cooker.featureset:
|
||||
depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
|
||||
bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
|
||||
|
||||
if self.state is runQueueSceneInit:
|
||||
if self.cooker.configuration.dump_signatures:
|
||||
self.dump_signatures()
|
||||
else:
|
||||
self.start_worker()
|
||||
self.rqexe = RunQueueExecuteScenequeue(self)
|
||||
|
||||
if self.state in [runQueueSceneRun, runQueueRunning, runQueueCleanUp]:
|
||||
@@ -1038,7 +904,6 @@ class RunQueue:
|
||||
self.rqexe.finish()
|
||||
|
||||
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)
|
||||
else:
|
||||
@@ -1056,6 +921,10 @@ class RunQueue:
|
||||
# All done
|
||||
return False
|
||||
|
||||
if self.state is runQueueChildProcess:
|
||||
print("Child process, eeek, shouldn't happen!")
|
||||
return False
|
||||
|
||||
# Loop
|
||||
return retval
|
||||
|
||||
@@ -1069,10 +938,6 @@ class RunQueue:
|
||||
raise
|
||||
except:
|
||||
logger.error("An uncaught exception occured in runqueue, please see the failure below:")
|
||||
try:
|
||||
self.teardown_workers()
|
||||
except:
|
||||
pass
|
||||
self.state = runQueueComplete
|
||||
raise
|
||||
|
||||
@@ -1092,7 +957,7 @@ class RunQueue:
|
||||
for task in range(len(self.rqdata.runq_fnid)):
|
||||
if self.rqdata.runq_fnid[task] not in done:
|
||||
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.collection.get_file_appends(fn), self.cooker.data)
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.get_file_appends(fn), self.cooker.configuration.data)
|
||||
done.add(self.rqdata.runq_fnid[task])
|
||||
|
||||
bb.parse.siggen.dump_sigs(self.rqdata.dataCache)
|
||||
@@ -1114,21 +979,38 @@ class RunQueueExecute:
|
||||
self.runq_buildable = []
|
||||
self.runq_running = []
|
||||
self.runq_complete = []
|
||||
|
||||
self.build_pids = {}
|
||||
self.build_pipes = {}
|
||||
self.build_stamps = {}
|
||||
self.failed_fnids = []
|
||||
|
||||
self.stampcache = {}
|
||||
|
||||
rq.workerpipe.setrunqueueexec(self)
|
||||
if rq.fakeworkerpipe:
|
||||
rq.fakeworkerpipe.setrunqueueexec(self)
|
||||
def runqueue_process_waitpid(self):
|
||||
"""
|
||||
Return none is there are no processes awaiting result collection, otherwise
|
||||
collect the process exit codes and close the information pipe.
|
||||
"""
|
||||
pid, status = os.waitpid(-1, os.WNOHANG)
|
||||
if pid == 0 or os.WIFSTOPPED(status):
|
||||
return None
|
||||
|
||||
def runqueue_process_waitpid(self, task, status):
|
||||
if os.WIFEXITED(status):
|
||||
status = os.WEXITSTATUS(status)
|
||||
elif os.WIFSIGNALED(status):
|
||||
# Per shell conventions for $?, when a process exits due to
|
||||
# a signal, we return an exit code of 128 + SIGNUM
|
||||
status = 128 + os.WTERMSIG(status)
|
||||
|
||||
task = self.build_pids[pid]
|
||||
del self.build_pids[pid]
|
||||
|
||||
self.build_pipes[pid].close()
|
||||
del self.build_pipes[pid]
|
||||
|
||||
# self.build_stamps[pid] may not exist when use shared work directory.
|
||||
if task in self.build_stamps:
|
||||
del self.build_stamps[task]
|
||||
if pid in self.build_stamps:
|
||||
del self.build_stamps[pid]
|
||||
|
||||
if status != 0:
|
||||
self.task_fail(task, status)
|
||||
@@ -1137,12 +1019,16 @@ class RunQueueExecute:
|
||||
return True
|
||||
|
||||
def finish_now(self):
|
||||
|
||||
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 self.stats.active:
|
||||
logger.info("Sending SIGTERM to remaining %s tasks", self.stats.active)
|
||||
for k, v in self.build_pids.iteritems():
|
||||
try:
|
||||
os.kill(-k, signal.SIGTERM)
|
||||
os.waitpid(-1, 0)
|
||||
except:
|
||||
pass
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
self.rq.state = runQueueFailed
|
||||
@@ -1154,10 +1040,12 @@ class RunQueueExecute:
|
||||
def finish(self):
|
||||
self.rq.state = runQueueCleanUp
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData)
|
||||
self.rq.read_workers()
|
||||
|
||||
self.runqueue_process_waitpid()
|
||||
return
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
@@ -1167,6 +1055,113 @@ class RunQueueExecute:
|
||||
self.rq.state = runQueueComplete
|
||||
return
|
||||
|
||||
def fork_off_task(self, fn, task, taskname, quieterrors=False):
|
||||
# We need to setup the environment BEFORE the fork, since
|
||||
# a fork() or exec*() activates PSEUDO...
|
||||
|
||||
envbackup = {}
|
||||
fakeenv = {}
|
||||
umask = None
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
if 'umask' in taskdep and taskname in taskdep['umask']:
|
||||
# umask might come in as a number or text string..
|
||||
try:
|
||||
umask = int(taskdep['umask'][taskname],8)
|
||||
except TypeError:
|
||||
umask = taskdep['umask'][taskname]
|
||||
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
|
||||
envvars = (self.rqdata.dataCache.fakerootenv[fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
fakedirs = (self.rqdata.dataCache.fakerootdirs[fn] or "").split()
|
||||
for p in fakedirs:
|
||||
bb.utils.mkdirhier(p)
|
||||
|
||||
logger.debug(2, 'Running %s:%s under fakeroot, fakedirs: %s' %
|
||||
(fn, taskname, ', '.join(fakedirs)))
|
||||
else:
|
||||
envvars = (self.rqdata.dataCache.fakerootnoenv[fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
pipein, pipeout = os.pipe()
|
||||
pipein = os.fdopen(pipein, 'rb', 4096)
|
||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||
pid = os.fork()
|
||||
except OSError as e:
|
||||
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
|
||||
if pid == 0:
|
||||
pipein.close()
|
||||
|
||||
# Save out the PID so that the event can include it the
|
||||
# events
|
||||
bb.event.worker_pid = os.getpid()
|
||||
bb.event.worker_pipe = pipeout
|
||||
|
||||
self.rq.state = runQueueChildProcess
|
||||
# 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())
|
||||
|
||||
if umask:
|
||||
os.umask(umask)
|
||||
|
||||
self.cooker.configuration.data.setVar("BB_WORKERCONTEXT", "1")
|
||||
bb.parse.siggen.set_taskdata(self.rqdata.hashes, self.rqdata.hash_deps)
|
||||
ret = 0
|
||||
try:
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.get_file_appends(fn), self.cooker.configuration.data)
|
||||
the_data.setVar('BB_TASKHASH', self.rqdata.runq_hash[task])
|
||||
for h in self.rqdata.hashes:
|
||||
the_data.setVar("BBHASH_%s" % h, self.rqdata.hashes[h])
|
||||
for h in self.rqdata.hash_deps:
|
||||
the_data.setVar("BBHASHDEPS_%s" % h, self.rqdata.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
|
||||
exports = bb.data.exported_vars(the_data)
|
||||
bb.utils.empty_environment()
|
||||
for e, v in exports:
|
||||
os.environ[e] = v
|
||||
for e in fakeenv:
|
||||
os.environ[e] = fakeenv[e]
|
||||
the_data.setVar(e, fakeenv[e])
|
||||
|
||||
if quieterrors:
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
except Exception as exc:
|
||||
if not quieterrors:
|
||||
logger.critical(str(exc))
|
||||
os._exit(1)
|
||||
try:
|
||||
if not self.cooker.configuration.dry_run:
|
||||
ret = bb.build.exec_task(fn, taskname, the_data)
|
||||
os._exit(ret)
|
||||
except:
|
||||
os._exit(1)
|
||||
else:
|
||||
for key, value in envbackup.iteritems():
|
||||
if value is None:
|
||||
del os.environ[key]
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
return pid, pipein, pipeout
|
||||
|
||||
def check_dependencies(self, task, taskdeps, setscene = False):
|
||||
if not self.rq.depvalidate:
|
||||
return False
|
||||
@@ -1183,7 +1178,7 @@ class RunQueueExecute:
|
||||
taskname = self.rqdata.runq_task[depid]
|
||||
taskdata[dep] = [pn, taskname, fn]
|
||||
call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
|
||||
locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
|
||||
locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.configuration.data }
|
||||
valid = bb.utils.better_eval(call, locs)
|
||||
return valid
|
||||
|
||||
@@ -1204,8 +1199,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)
|
||||
@@ -1252,34 +1245,19 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
|
||||
call = self.rq.setsceneverify + "(covered, tasknames, fnids, fns, d, invalidtasks=invalidtasks)"
|
||||
call2 = self.rq.setsceneverify + "(covered, tasknames, fnids, fns, d)"
|
||||
locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : self.rqdata.runq_task, "fnids" : self.rqdata.runq_fnid, "fns" : self.rqdata.taskData.fn_index, "d" : self.cooker.data, "invalidtasks" : invalidtasks }
|
||||
locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : self.rqdata.runq_task, "fnids" : self.rqdata.runq_fnid, "fns" : self.rqdata.taskData.fn_index, "d" : self.cooker.configuration.data, "invalidtasks" : invalidtasks }
|
||||
# Backwards compatibility with older versions without invalidtasks
|
||||
try:
|
||||
covered_remove = bb.utils.better_eval(call, locs)
|
||||
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)
|
||||
|
||||
@@ -1295,6 +1273,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
|
||||
(self.scheduler, ", ".join(obj.name for obj in schedulers)))
|
||||
|
||||
|
||||
def get_schedulers(self):
|
||||
schedulers = set(obj for obj in globals().values()
|
||||
if type(obj) is type and
|
||||
@@ -1356,10 +1335,9 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if self.rqdata.taskData.abort:
|
||||
self.rq.state = runQueueCleanUp
|
||||
|
||||
def task_skip(self, task, reason):
|
||||
def task_skip(self, task):
|
||||
self.runq_running[task] = 1
|
||||
self.runq_buildable[task] = 1
|
||||
bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
|
||||
self.task_completeoutright(task)
|
||||
self.stats.taskCompleted()
|
||||
self.stats.taskSkipped()
|
||||
@@ -1369,9 +1347,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
Run the tasks in a queue prepared by rqdata.prepare()
|
||||
"""
|
||||
|
||||
self.rq.read_workers()
|
||||
|
||||
|
||||
if self.stats.total == 0:
|
||||
# nothing to do
|
||||
self.rq.state = runQueueCleanUp
|
||||
@@ -1384,13 +1359,13 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if task in self.rq.scenequeue_covered:
|
||||
logger.debug(2, "Setscene covered task %s (%s)", task,
|
||||
self.rqdata.get_user_idstring(task))
|
||||
self.task_skip(task, "covered")
|
||||
self.task_skip(task)
|
||||
return True
|
||||
|
||||
if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
|
||||
logger.debug(2, "Stamp current task %s (%s)", task,
|
||||
self.rqdata.get_user_idstring(task))
|
||||
self.task_skip(task, "existing")
|
||||
self.task_skip(task)
|
||||
return True
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
@@ -1407,25 +1382,23 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
startevent = runQueueTaskStarted(task, self.stats, self.rq)
|
||||
bb.event.fire(startevent, self.cfgData)
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
|
||||
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))) + "</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))) + "</runtask>")
|
||||
self.rq.worker.stdin.flush()
|
||||
pid, pipein, pipeout = self.fork_off_task(fn, task, taskname)
|
||||
|
||||
self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData)
|
||||
self.build_stamps[pid] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
|
||||
self.runq_running[task] = 1
|
||||
self.stats.taskActive()
|
||||
if self.stats.active < self.number_tasks:
|
||||
return True
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
self.rq.read_workers()
|
||||
return self.rq.active_fds()
|
||||
if self.runqueue_process_waitpid() is None:
|
||||
return 0.5
|
||||
return True
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
self.rq.state = runQueueFailed
|
||||
@@ -1440,7 +1413,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if self.runq_complete[task] == 0:
|
||||
logger.error("Task %s never completed!", task)
|
||||
self.rq.state = runQueueComplete
|
||||
|
||||
return True
|
||||
|
||||
class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
@@ -1462,7 +1434,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
sq_revdeps = []
|
||||
sq_revdeps_new = []
|
||||
sq_revdeps_squash = []
|
||||
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
|
||||
@@ -1575,7 +1546,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
if taskid is None:
|
||||
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()
|
||||
@@ -1635,7 +1605,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
sq_taskname.append(taskname)
|
||||
sq_task.append(task)
|
||||
call = self.rq.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 }
|
||||
locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.configuration.data }
|
||||
valid = bb.utils.better_eval(call, locs)
|
||||
|
||||
valid_new = stamppresent
|
||||
@@ -1653,10 +1623,8 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
|
||||
self.rq.state = runQueueSceneRun
|
||||
|
||||
def scenequeue_updatecounters(self, task, fail = False):
|
||||
def scenequeue_updatecounters(self, task):
|
||||
for dep in self.sq_deps[task]:
|
||||
if fail and task in self.sq_harddeps:
|
||||
continue
|
||||
self.sq_revdeps2[dep].remove(task)
|
||||
if len(self.sq_revdeps2[dep]) == 0:
|
||||
self.runq_buildable[dep] = 1
|
||||
@@ -1677,14 +1645,13 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
|
||||
def task_complete(self, task):
|
||||
self.stats.taskCompleted()
|
||||
bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
|
||||
self.task_completeoutright(task)
|
||||
|
||||
def task_fail(self, task, result):
|
||||
self.stats.taskFailed()
|
||||
bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
|
||||
self.scenequeue_notcovered.add(task)
|
||||
self.scenequeue_updatecounters(task, True)
|
||||
self.scenequeue_updatecounters(task)
|
||||
|
||||
def task_failoutright(self, task):
|
||||
self.runq_running[task] = 1
|
||||
@@ -1693,7 +1660,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
self.stats.taskSkipped()
|
||||
index = self.rqdata.runq_setscene[task]
|
||||
self.scenequeue_notcovered.add(task)
|
||||
self.scenequeue_updatecounters(task, True)
|
||||
self.scenequeue_updatecounters(task)
|
||||
|
||||
def task_skip(self, task):
|
||||
self.runq_running[task] = 1
|
||||
@@ -1707,8 +1674,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
Run the tasks in a queue prepared by prepare_runqueue
|
||||
"""
|
||||
|
||||
self.rq.read_workers()
|
||||
|
||||
task = None
|
||||
if self.stats.active < self.number_tasks:
|
||||
# Find the next setscene to run
|
||||
@@ -1749,24 +1714,22 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
|
||||
bb.event.fire(startevent, self.cfgData)
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
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))) + "</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))) + "</runtask>")
|
||||
self.rq.worker.stdin.flush()
|
||||
pid, pipein, pipeout = self.fork_off_task(fn, realtask, taskname)
|
||||
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData)
|
||||
self.runq_running[task] = 1
|
||||
self.stats.taskActive()
|
||||
if self.stats.active < self.number_tasks:
|
||||
return True
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
self.rq.read_workers()
|
||||
return self.rq.active_fds()
|
||||
if self.runqueue_process_waitpid() is None:
|
||||
return 0.5
|
||||
return True
|
||||
|
||||
# Convert scenequeue_covered task numbers into full taskgraph ids
|
||||
oldcovered = self.scenequeue_covered
|
||||
@@ -1782,10 +1745,8 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
self.rq.state = runQueueRunInit
|
||||
return True
|
||||
|
||||
def runqueue_process_waitpid(self, task, status):
|
||||
task = self.rq.rqdata.runq_setscene.index(task)
|
||||
|
||||
RunQueueExecute.runqueue_process_waitpid(self, task, status)
|
||||
def fork_off_task(self, fn, task, taskname):
|
||||
return RunQueueExecute.fork_off_task(self, fn, task, taskname, quieterrors=True)
|
||||
|
||||
class TaskFailure(Exception):
|
||||
"""
|
||||
@@ -1812,9 +1773,6 @@ class runQueueEvent(bb.event.Event):
|
||||
def __init__(self, task, stats, rq):
|
||||
self.taskid = task
|
||||
self.taskstring = rq.rqdata.get_user_idstring(task)
|
||||
self.taskname = rq.rqdata.get_task_name(task)
|
||||
self.taskfile = rq.rqdata.get_task_file(task)
|
||||
self.taskhash = rq.rqdata.get_task_hash(task)
|
||||
self.stats = stats.copy()
|
||||
bb.event.Event.__init__(self)
|
||||
|
||||
@@ -1826,9 +1784,6 @@ class sceneQueueEvent(runQueueEvent):
|
||||
runQueueEvent.__init__(self, task, stats, rq)
|
||||
realtask = rq.rqdata.runq_setscene[task]
|
||||
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(task)
|
||||
|
||||
class runQueueTaskStarted(runQueueEvent):
|
||||
"""
|
||||
@@ -1867,60 +1822,29 @@ class runQueueTaskCompleted(runQueueEvent):
|
||||
Event notifing a task completed
|
||||
"""
|
||||
|
||||
class sceneQueueTaskCompleted(sceneQueueEvent):
|
||||
"""
|
||||
Event notifing a setscene task completed
|
||||
"""
|
||||
|
||||
class runQueueTaskSkipped(runQueueEvent):
|
||||
"""
|
||||
Event notifing a task was skipped
|
||||
"""
|
||||
def __init__(self, task, stats, rq, reason):
|
||||
runQueueEvent.__init__(self, task, stats, rq)
|
||||
self.reason = reason
|
||||
|
||||
class runQueuePipe():
|
||||
"""
|
||||
Abstraction for a pipe between a worker thread and the server
|
||||
"""
|
||||
def __init__(self, pipein, pipeout, d, rq):
|
||||
def __init__(self, pipein, pipeout, d):
|
||||
self.input = pipein
|
||||
if pipeout:
|
||||
pipeout.close()
|
||||
pipeout.close()
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
self.d = d
|
||||
self.rq = rq
|
||||
|
||||
def setrunqueueexec(self, rq):
|
||||
self.rq = rq
|
||||
|
||||
def read(self):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read(102400)
|
||||
except (OSError, IOError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
end = len(self.queue)
|
||||
found = True
|
||||
while found and len(self.queue):
|
||||
found = False
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1:
|
||||
bb.event.fire_from_worker(self.queue[:index+8], self.d)
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1 and self.queue.startswith("<event>"):
|
||||
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>"):
|
||||
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>")
|
||||
return (end > start)
|
||||
|
||||
def close(self):
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#
|
||||
# BitBake Base Server Code
|
||||
#
|
||||
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2006 - 2008 Richard Purdie
|
||||
# Copyright (C) 2013 Alexandru Damian
|
||||
#
|
||||
# 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.
|
||||
|
||||
""" Base code for Bitbake server process
|
||||
|
||||
Have a common base for that all Bitbake server classes ensures a consistent
|
||||
approach to the interface, and minimize risks associated with code duplication.
|
||||
|
||||
"""
|
||||
|
||||
""" BaseImplServer() the base class for all XXServer() implementations.
|
||||
|
||||
These classes contain the actual code that runs the server side, i.e.
|
||||
listens for the commands and executes them. Although these implementations
|
||||
contain all the data of the original bitbake command, i.e the cooker instance,
|
||||
they may well run on a different process or even machine.
|
||||
|
||||
"""
|
||||
|
||||
class BaseImplServer():
|
||||
def __init__(self):
|
||||
self._idlefuns = {}
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
|
||||
|
||||
""" BitBakeBaseServerConnection class is the common ancestor to all
|
||||
BitBakeServerConnection classes.
|
||||
|
||||
These classes control the remote server. The only command currently
|
||||
implemented is the terminate() command.
|
||||
|
||||
"""
|
||||
|
||||
class BitBakeBaseServerConnection():
|
||||
def __init__(self, serverImpl):
|
||||
pass
|
||||
|
||||
def terminate(self):
|
||||
pass
|
||||
|
||||
|
||||
""" BitBakeBaseServer class is the common ancestor to all Bitbake servers
|
||||
|
||||
Derive this class in order to implement a BitBakeServer which is the
|
||||
controlling stub for the actual server implementation
|
||||
|
||||
"""
|
||||
class BitBakeBaseServer(object):
|
||||
def initServer(self):
|
||||
self.serverImpl = None # we ensure a runtime crash if not overloaded
|
||||
self.connection = None
|
||||
return
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.serverImpl.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.serverImpl.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def detach(self):
|
||||
return
|
||||
|
||||
def establishConnection(self, featureset):
|
||||
raise "Must redefine the %s.establishConnection()" % self.__class__.__name__
|
||||
|
||||
def endSession(self):
|
||||
self.connection.terminate()
|
||||
|
||||
203
bitbake/lib/bb/server/none.py
Normal file
203
bitbake/lib/bb/server/none.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#
|
||||
# BitBake 'dummy' Passthrough Server
|
||||
#
|
||||
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2006 - 2008 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
This module implements a passthrough server for BitBake.
|
||||
|
||||
Use register_idle_function() to add a function which the server
|
||||
calls from within idle_commands when no requests are pending. Make sure
|
||||
that those functions are non-blocking or else you will introduce latency
|
||||
in the server's main loop.
|
||||
"""
|
||||
|
||||
import time
|
||||
import bb
|
||||
import signal
|
||||
|
||||
DEBUG = False
|
||||
|
||||
import inspect, select
|
||||
|
||||
class BitBakeServerCommands():
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
|
||||
def runCommand(self, command):
|
||||
"""
|
||||
Run a cooker command on the server
|
||||
"""
|
||||
#print "Running Command %s" % command
|
||||
return self.cooker.command.runCommand(command)
|
||||
|
||||
def terminateServer(self):
|
||||
"""
|
||||
Trigger the server to quit
|
||||
"""
|
||||
self.server.server_exit()
|
||||
#print "Server (cooker) exitting"
|
||||
return
|
||||
|
||||
def ping(self):
|
||||
"""
|
||||
Dummy method which can be used to check the server is still alive
|
||||
"""
|
||||
return True
|
||||
|
||||
eventQueue = []
|
||||
|
||||
class BBUIEventQueue:
|
||||
class event:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
@staticmethod
|
||||
def send(event):
|
||||
bb.server.none.eventQueue.append(event)
|
||||
@staticmethod
|
||||
def quit():
|
||||
return
|
||||
|
||||
def __init__(self, BBServer):
|
||||
self.eventQueue = bb.server.none.eventQueue
|
||||
self.BBServer = BBServer
|
||||
self.EventHandle = bb.event.register_UIHhandler(self)
|
||||
|
||||
def __popEvent(self):
|
||||
if len(self.eventQueue) == 0:
|
||||
return None
|
||||
return self.eventQueue.pop(0)
|
||||
|
||||
def getEvent(self):
|
||||
if len(self.eventQueue) == 0:
|
||||
self.BBServer.idle_commands(0)
|
||||
return self.__popEvent()
|
||||
|
||||
def waitEvent(self, delay):
|
||||
event = self.__popEvent()
|
||||
if event:
|
||||
return event
|
||||
self.BBServer.idle_commands(delay)
|
||||
return self.__popEvent()
|
||||
|
||||
def queue_event(self, event):
|
||||
self.eventQueue.append(event)
|
||||
|
||||
def system_quit( self ):
|
||||
bb.event.unregister_UIHhandler(self.EventHandle)
|
||||
|
||||
# Dummy signal handler to ensure we break out of sleep upon SIGCHLD
|
||||
def chldhandler(signum, stackframe):
|
||||
pass
|
||||
|
||||
class BitBakeNoneServer():
|
||||
# remove this when you're done with debugging
|
||||
# allow_reuse_address = True
|
||||
|
||||
def __init__(self):
|
||||
self._idlefuns = {}
|
||||
self.commands = BitBakeServerCommands(self)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.commands.cooker = cooker
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
def idle_commands(self, delay):
|
||||
#print "Idle queue length %s" % len(self._idlefuns)
|
||||
#print "Idle timeout, running idle functions"
|
||||
#if len(self._idlefuns) == 0:
|
||||
nextsleep = delay
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
#print "Idle function returned %s" % (retval)
|
||||
if retval is False:
|
||||
del self._idlefuns[function]
|
||||
elif retval is True:
|
||||
nextsleep = None
|
||||
elif nextsleep is None:
|
||||
continue
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.commands.runCommand(["stateShutdown"])
|
||||
pass
|
||||
if nextsleep is not None:
|
||||
#print "Sleeping for %s (%s)" % (nextsleep, delay)
|
||||
signal.signal(signal.SIGCHLD, chldhandler)
|
||||
time.sleep(nextsleep)
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
|
||||
def server_exit(self):
|
||||
# Tell idle functions we're exiting
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, True)
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, server):
|
||||
self.server = server.server
|
||||
self.connection = self.server.commands
|
||||
self.events = bb.server.none.BBUIEventQueue(self.server)
|
||||
for event in bb.event.ui_queue:
|
||||
self.events.queue_event(event)
|
||||
|
||||
def terminate(self):
|
||||
try:
|
||||
self.events.system_quit()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.connection.terminateServer()
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServer(object):
|
||||
def initServer(self):
|
||||
self.server = BitBakeNoneServer()
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def detach(self):
|
||||
return
|
||||
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeServerConnection(self)
|
||||
return self.connection
|
||||
|
||||
def launchUI(self, uifunc, *args):
|
||||
return bb.cooker.server_main(self.cooker, uifunc, *args)
|
||||
|
||||
@@ -29,18 +29,14 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import select
|
||||
from Queue import Empty
|
||||
from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
|
||||
|
||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||
from multiprocessing import Event, Process, util, Queue, Pipe, queues
|
||||
|
||||
logger = logging.getLogger('BitBake')
|
||||
|
||||
class ServerCommunicator():
|
||||
def __init__(self, connection, event_handle):
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
self.event_handle = event_handle
|
||||
|
||||
def runCommand(self, command):
|
||||
# @todo try/except
|
||||
@@ -56,8 +52,6 @@ class ServerCommunicator():
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def getEventHandle(self):
|
||||
return self.event_handle.value
|
||||
|
||||
class EventAdapter():
|
||||
"""
|
||||
@@ -74,35 +68,30 @@ class EventAdapter():
|
||||
print("EventAdapter puked: %s" % str(err))
|
||||
|
||||
|
||||
class ProcessServer(Process, BaseImplServer):
|
||||
class ProcessServer(Process):
|
||||
profile_filename = "profile.log"
|
||||
profile_processed_filename = "profile.log.processed"
|
||||
|
||||
def __init__(self, command_channel, event_queue, featurelist):
|
||||
BaseImplServer.__init__(self)
|
||||
Process.__init__(self, args=(featurelist))
|
||||
def __init__(self, command_channel, event_queue):
|
||||
Process.__init__(self)
|
||||
self.command_channel = command_channel
|
||||
self.event_queue = event_queue
|
||||
self.event = EventAdapter(event_queue)
|
||||
self.featurelist = featurelist
|
||||
self._idlefunctions = {}
|
||||
self.quit = False
|
||||
|
||||
self.keep_running = Event()
|
||||
self.keep_running.set()
|
||||
self.event_handle = multiprocessing.Value("i")
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefunctions[function] = data
|
||||
|
||||
def run(self):
|
||||
for event in bb.event.ui_queue:
|
||||
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()
|
||||
|
||||
self.event_handle = bb.event.register_UIHhandler(self)
|
||||
bb.cooker.server_main(self.cooker, self.main)
|
||||
|
||||
def main(self):
|
||||
@@ -115,37 +104,37 @@ class ProcessServer(Process, BaseImplServer):
|
||||
command = self.command_channel.recv()
|
||||
self.runCommand(command)
|
||||
|
||||
self.idle_commands(.1, [self.event_queue._reader, self.command_channel])
|
||||
self.idle_commands(.1)
|
||||
except Exception:
|
||||
logger.exception('Running command %s', command)
|
||||
|
||||
self.event_queue.close()
|
||||
bb.event.unregister_UIHhandler(self.event_handle.value)
|
||||
self.event_queue.cancel_join_thread()
|
||||
bb.event.unregister_UIHhandler(self.event_handle)
|
||||
self.command_channel.close()
|
||||
self.cooker.shutdown(True)
|
||||
self.cooker.stop()
|
||||
self.idle_commands(.1)
|
||||
|
||||
def idle_commands(self, delay, fds = []):
|
||||
def idle_commands(self, delay):
|
||||
nextsleep = delay
|
||||
|
||||
for function, data in self._idlefuns.items():
|
||||
for function, data in self._idlefunctions.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
if retval is False:
|
||||
del self._idlefuns[function]
|
||||
del self._idlefunctions[function]
|
||||
elif retval is True:
|
||||
nextsleep = None
|
||||
elif nextsleep is None:
|
||||
continue
|
||||
else:
|
||||
fds = fds + retval
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception:
|
||||
logger.exception('Running idle function')
|
||||
|
||||
if nextsleep is not None:
|
||||
select.select(fds,[],[],nextsleep)
|
||||
time.sleep(nextsleep)
|
||||
|
||||
def runCommand(self, command):
|
||||
"""
|
||||
@@ -156,33 +145,80 @@ class ProcessServer(Process, BaseImplServer):
|
||||
def stop(self):
|
||||
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.events = self.event_queue
|
||||
def bootstrap_2_6_6(self):
|
||||
"""Pulled from python 2.6.6. Needed to ensure we have the fix from
|
||||
http://bugs.python.org/issue5313 when running on python version 2.6.2
|
||||
or lower."""
|
||||
|
||||
def terminate(self):
|
||||
def flushevents():
|
||||
while True:
|
||||
try:
|
||||
event = self.event_queue.get(block=False)
|
||||
except (Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
try:
|
||||
self._children = set()
|
||||
self._counter = itertools.count(1)
|
||||
try:
|
||||
sys.stdin.close()
|
||||
sys.stdin = open(os.devnull)
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
multiprocessing._current_process = self
|
||||
util._finalizer_registry.clear()
|
||||
util._run_after_forkers()
|
||||
util.info('child process calling self.run()')
|
||||
try:
|
||||
self.run()
|
||||
exitcode = 0
|
||||
finally:
|
||||
util._exit_function()
|
||||
except SystemExit as e:
|
||||
if not e.args:
|
||||
exitcode = 1
|
||||
elif type(e.args[0]) is int:
|
||||
exitcode = e.args[0]
|
||||
else:
|
||||
sys.stderr.write(e.args[0] + '\n')
|
||||
sys.stderr.flush()
|
||||
exitcode = 1
|
||||
except:
|
||||
exitcode = 1
|
||||
import traceback
|
||||
sys.stderr.write('Process %s:\n' % self.name)
|
||||
sys.stderr.flush()
|
||||
traceback.print_exc()
|
||||
|
||||
util.info('process exiting with exitcode %d' % exitcode)
|
||||
return exitcode
|
||||
|
||||
# Python versions 2.6.0 through 2.6.2 suffer from a multiprocessing bug
|
||||
# which can result in a bitbake server hang during the parsing process
|
||||
if (2, 6, 0) <= sys.version_info < (2, 6, 3):
|
||||
_bootstrap = bootstrap_2_6_6
|
||||
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.procserver = server.server
|
||||
self.connection = ServerCommunicator(server.ui_channel)
|
||||
self.events = server.event_queue
|
||||
|
||||
def terminate(self, force = False):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
self.procserver.stop()
|
||||
|
||||
while self.procserver.is_alive():
|
||||
flushevents()
|
||||
self.procserver.join(0.1)
|
||||
|
||||
self.ui_channel.close()
|
||||
self.event_queue.close()
|
||||
if force:
|
||||
self.procserver.join(0.5)
|
||||
if self.procserver.is_alive():
|
||||
self.procserver.terminate()
|
||||
self.procserver.join()
|
||||
else:
|
||||
self.procserver.join()
|
||||
while True:
|
||||
try:
|
||||
event = self.server.event_queue.get(block=False)
|
||||
except (Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
self.server.ui_channel.close()
|
||||
self.server.event_queue.close()
|
||||
if force:
|
||||
sys.exit(1)
|
||||
|
||||
# Wrap Queue to provide API which isn't server implementation specific
|
||||
class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
@@ -199,7 +235,7 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
return None
|
||||
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
class BitBakeServer(object):
|
||||
def initServer(self):
|
||||
# establish communication channels. We use bidirectional pipes for
|
||||
# ui <--> server command/response pairs
|
||||
@@ -207,17 +243,28 @@ class BitBakeServer(BitBakeBaseServer):
|
||||
#
|
||||
self.ui_channel, self.server_channel = Pipe()
|
||||
self.event_queue = ProcessEventQueue(0)
|
||||
manager = Manager()
|
||||
self.featurelist = manager.list()
|
||||
self.serverImpl = ProcessServer(self.server_channel, self.event_queue, self.featurelist)
|
||||
|
||||
def detach(self):
|
||||
self.serverImpl.start()
|
||||
self.server = ProcessServer(self.server_channel, self.event_queue)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.cooker = cooker
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def establishConnection(self, featureset):
|
||||
for f in featureset:
|
||||
self.featurelist.append(f)
|
||||
self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
|
||||
signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate())
|
||||
def detach(self):
|
||||
self.server.start()
|
||||
return
|
||||
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeServerConnection(self)
|
||||
signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate(force=True))
|
||||
return self.connection
|
||||
|
||||
def launchUI(self, uifunc, *args):
|
||||
return bb.cooker.server_main(self.cooker, uifunc, *args)
|
||||
|
||||
|
||||
@@ -35,79 +35,103 @@ import bb
|
||||
import xmlrpclib, sys
|
||||
from bb import daemonize
|
||||
from bb.ui import uievent
|
||||
import hashlib, time
|
||||
import socket
|
||||
import os, signal
|
||||
import threading
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
DEBUG = False
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
import inspect, select, httplib
|
||||
import inspect, select
|
||||
|
||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||
if sys.hexversion < 0x020600F0:
|
||||
print("Sorry, python 2.6 or later is required for bitbake's XMLRPC mode")
|
||||
sys.exit(1)
|
||||
|
||||
##
|
||||
# The xmlrpclib.Transport class has undergone various changes in Python 2.7
|
||||
# which break BitBake's XMLRPC implementation.
|
||||
# To work around this we subclass Transport and have a copy/paste of method
|
||||
# implementations from Python 2.6.6's xmlrpclib.
|
||||
#
|
||||
# Upstream Python bug is #8194 (http://bugs.python.org/issue8194)
|
||||
# This bug is relevant for Python 2.7.0 and 2.7.1 but was fixed for
|
||||
# Python > 2.7.2
|
||||
##
|
||||
|
||||
class BBTransport(xmlrpclib.Transport):
|
||||
def __init__(self, timeout):
|
||||
self.timeout = timeout
|
||||
self.connection_token = None
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
def request(self, host, handler, request_body, verbose=0):
|
||||
h = self.make_connection(host)
|
||||
if verbose:
|
||||
h.set_debuglevel(1)
|
||||
|
||||
self.send_request(h, handler, request_body)
|
||||
self.send_host(h, host)
|
||||
self.send_user_agent(h)
|
||||
self.send_content(h, request_body)
|
||||
|
||||
errcode, errmsg, headers = h.getreply()
|
||||
|
||||
if errcode != 200:
|
||||
raise ProtocolError(
|
||||
host + handler,
|
||||
errcode, errmsg,
|
||||
headers
|
||||
)
|
||||
|
||||
self.verbose = verbose
|
||||
|
||||
try:
|
||||
sock = h._conn.sock
|
||||
except AttributeError:
|
||||
sock = None
|
||||
|
||||
return self._parse_response(h.getfile(), sock)
|
||||
|
||||
# Modified from default to pass timeout to HTTPConnection
|
||||
def make_connection(self, host):
|
||||
#return an existing connection if possible. This allows
|
||||
#HTTP/1.1 keep-alive.
|
||||
if self._connection and host == self._connection[0]:
|
||||
return self._connection[1]
|
||||
import httplib
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
return httplib.HTTP(host)
|
||||
|
||||
# create a HTTP connection object from a host descriptor
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
#store the host argument along with the connection object
|
||||
self._connection = host, httplib.HTTPConnection(chost, timeout=self.timeout)
|
||||
return self._connection[1]
|
||||
def _parse_response(self, file, sock):
|
||||
p, u = self.getparser()
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection_token = token
|
||||
while 1:
|
||||
if sock:
|
||||
response = sock.recv(1024)
|
||||
else:
|
||||
response = file.read(1024)
|
||||
if not response:
|
||||
break
|
||||
if self.verbose:
|
||||
print "body:", repr(response)
|
||||
p.feed(response)
|
||||
|
||||
def send_content(self, h, body):
|
||||
if self.connection_token:
|
||||
h.putheader("Bitbake-token", self.connection_token)
|
||||
xmlrpclib.Transport.send_content(self, h, body)
|
||||
file.close()
|
||||
p.close()
|
||||
|
||||
def _create_server(host, port, timeout = 60):
|
||||
t = BBTransport(timeout)
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), transport=t, allow_none=True)
|
||||
return s, t
|
||||
return u.close()
|
||||
|
||||
def _create_server(host, port):
|
||||
# Python 2.7.0 and 2.7.1 have a buggy Transport implementation
|
||||
# For those versions of Python, and only those versions, use our
|
||||
# own copy/paste BBTransport class.
|
||||
if (2, 7, 0) <= sys.version_info < (2, 7, 2):
|
||||
t = BBTransport()
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), transport=t, allow_none=True)
|
||||
else:
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), allow_none=True)
|
||||
|
||||
return s
|
||||
|
||||
class BitBakeServerCommands():
|
||||
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.has_client = False
|
||||
|
||||
def registerEventHandler(self, host, port, featureset = []):
|
||||
def registerEventHandler(self, host, port):
|
||||
"""
|
||||
Register a remote UI Event Handler
|
||||
"""
|
||||
s, t = _create_server(host, port)
|
||||
s = _create_server(host, port)
|
||||
|
||||
# we don't allow connections if the cooker is running
|
||||
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
|
||||
return bb.event.register_UIHhandler(s)
|
||||
|
||||
def unregisterEventHandler(self, handlerNum):
|
||||
"""
|
||||
@@ -119,10 +143,7 @@ class BitBakeServerCommands():
|
||||
"""
|
||||
Run a cooker command on the server
|
||||
"""
|
||||
return self.cooker.command.runCommand(command, self.server.readonly)
|
||||
|
||||
def getEventHandle(self):
|
||||
return self.event_handle
|
||||
return self.cooker.command.runCommand(command)
|
||||
|
||||
def terminateServer(self):
|
||||
"""
|
||||
@@ -132,64 +153,13 @@ class BitBakeServerCommands():
|
||||
print("Server (cooker) exiting")
|
||||
return
|
||||
|
||||
def addClient(self):
|
||||
if self.has_client:
|
||||
return None
|
||||
token = hashlib.md5(str(time.time())).hexdigest()
|
||||
self.server.set_connection_token(token)
|
||||
self.has_client = True
|
||||
return token
|
||||
def ping(self):
|
||||
"""
|
||||
Dummy method which can be used to check the server is still alive
|
||||
"""
|
||||
return True
|
||||
|
||||
def removeClient(self):
|
||||
if self.has_client:
|
||||
self.server.set_connection_token(None)
|
||||
self.has_client = False
|
||||
if self.server.single_use:
|
||||
self.server.quit = True
|
||||
|
||||
# This request handler checks if the request has a "Bitbake-token" header
|
||||
# field (this comes from the client side) and compares it with its internal
|
||||
# "Bitbake-token" field (this comes from the server). If the two are not
|
||||
# equal, it is assumed that a client is trying to connect to the server
|
||||
# while another client is connected to the server. In this case, a 503 error
|
||||
# ("service unavailable") is returned to the client.
|
||||
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
def __init__(self, request, client_address, server):
|
||||
self.server = server
|
||||
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
||||
|
||||
def do_POST(self):
|
||||
try:
|
||||
remote_token = self.headers["Bitbake-token"]
|
||||
except:
|
||||
remote_token = None
|
||||
if remote_token != self.server.connection_token and remote_token != "observer":
|
||||
self.report_503()
|
||||
else:
|
||||
if remote_token == "observer":
|
||||
self.server.readonly = True
|
||||
else:
|
||||
self.server.readonly = False
|
||||
SimpleXMLRPCRequestHandler.do_POST(self)
|
||||
|
||||
def report_503(self):
|
||||
self.send_response(503)
|
||||
response = 'No more client allowed'
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
|
||||
class XMLRPCProxyServer(BaseImplServer):
|
||||
""" not a real working server, but a stub for a proxy server connection
|
||||
|
||||
"""
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||
# remove this when you're done with debugging
|
||||
# allow_reuse_address = True
|
||||
|
||||
@@ -197,22 +167,17 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
BaseImplServer.__init__(self)
|
||||
SimpleXMLRPCServer.__init__(self, interface,
|
||||
requestHandler=BitBakeXMLRPCRequestHandler,
|
||||
requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=False, allow_none=True)
|
||||
self._idlefuns = {}
|
||||
self.host, self.port = self.socket.getsockname()
|
||||
self.connection_token = None
|
||||
#self.register_introspection_functions()
|
||||
self.commands = BitBakeServerCommands(self)
|
||||
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)
|
||||
self.cooker = cooker
|
||||
self.commands.cooker = cooker
|
||||
|
||||
def autoregister_all_functions(self, context, prefix):
|
||||
@@ -225,9 +190,12 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
if name.startswith(prefix):
|
||||
self.register_function(method, name[len(prefix):])
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
def serve_forever(self):
|
||||
# Start the actual XMLRPC server
|
||||
bb.cooker.server_main(self.cooker, self._serve_forever)
|
||||
|
||||
def _serve_forever(self):
|
||||
@@ -235,9 +203,12 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
Serve Requests. Overloaded to honor a quit command
|
||||
"""
|
||||
self.quit = False
|
||||
self.timeout = 0 # Run Idle calls for our first callback
|
||||
while not self.quit:
|
||||
fds = [self]
|
||||
nextsleep = 0.1
|
||||
#print "Idle queue length %s" % len(self._idlefuns)
|
||||
self.handle_request()
|
||||
#print "Idle timeout, running idle functions"
|
||||
nextsleep = None
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
@@ -245,59 +216,42 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
del self._idlefuns[function]
|
||||
elif retval is True:
|
||||
nextsleep = 0
|
||||
else:
|
||||
fds = fds + retval
|
||||
elif nextsleep is 0:
|
||||
continue
|
||||
elif nextsleep is None:
|
||||
nextsleep = retval
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
socktimeout = self.socket.gettimeout() or nextsleep
|
||||
socktimeout = min(socktimeout, nextsleep)
|
||||
# Mirror what BaseServer handle_request would do
|
||||
fd_sets = select.select(fds, [], [], socktimeout)
|
||||
if fd_sets[0] and self in fd_sets[0]:
|
||||
self._handle_request_noblock()
|
||||
|
||||
if nextsleep is None and len(self._idlefuns) > 0:
|
||||
nextsleep = 0
|
||||
self.timeout = nextsleep
|
||||
# Tell idle functions we're exiting
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, True)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.server_close()
|
||||
return
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection_token = token
|
||||
class BitbakeServerInfo():
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||
def __init__(self, serverImpl, clientinfo=("localhost", 0), observer_only = False, featureset = []):
|
||||
self.connection, self.transport = _create_server(serverImpl.host, serverImpl.port)
|
||||
self.clientinfo = clientinfo
|
||||
self.serverImpl = serverImpl
|
||||
self.observer_only = observer_only
|
||||
self.featureset = featureset
|
||||
|
||||
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.featureset)
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, serverinfo, clientinfo=("localhost", 0)):
|
||||
self.connection = _create_server(serverinfo.host, serverinfo.port)
|
||||
self.events = uievent.BBUIEventQueue(self.connection, clientinfo)
|
||||
for event in bb.event.ui_queue:
|
||||
self.events.queue_event(event)
|
||||
return self
|
||||
|
||||
def removeClient(self):
|
||||
if not self.observer_only:
|
||||
self.connection.removeClient()
|
||||
|
||||
def terminate(self):
|
||||
# Don't wait for server indefinitely
|
||||
@@ -308,58 +262,34 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.connection.removeClient()
|
||||
self.connection.terminateServer()
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
class BitBakeServer(object):
|
||||
def initServer(self, interface = ("localhost", 0)):
|
||||
self.interface = interface
|
||||
self.serverImpl = XMLRPCServer(interface)
|
||||
self.server = BitBakeXMLRPCServer(interface)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
self.serverinfo = BitbakeServerInfo(self.server.host, self.server.port)
|
||||
|
||||
def detach(self):
|
||||
daemonize.createDaemon(self.serverImpl.serve_forever, "bitbake-cookerdaemon.log")
|
||||
daemonize.createDaemon(self.server.serve_forever, "bitbake-cookerdaemon.log")
|
||||
del self.cooker
|
||||
del self.server
|
||||
|
||||
def establishConnection(self, featureset):
|
||||
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, self.interface, False, featureset)
|
||||
return self.connection.connect()
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeServerConnection(self.serverinfo)
|
||||
return self.connection
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection.transport.set_connection_token(token)
|
||||
def launchUI(self, uifunc, *args):
|
||||
return uifunc(*args)
|
||||
|
||||
class BitBakeXMLRPCClient(BitBakeBaseServer):
|
||||
|
||||
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
|
||||
|
||||
def saveConnectionDetails(self, remote):
|
||||
self.remote = remote
|
||||
|
||||
def establishConnection(self, featureset):
|
||||
# The format of "remote" must be "server:port"
|
||||
try:
|
||||
[host, port] = self.remote.split(":")
|
||||
port = int(port)
|
||||
except:
|
||||
return None
|
||||
# We need our IP for the server connection. We get the IP
|
||||
# by trying to connect with the server
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect((host, port))
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
return None
|
||||
try:
|
||||
self.serverImpl = XMLRPCProxyServer(host, port)
|
||||
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only, featureset)
|
||||
return self.connection.connect()
|
||||
except Exception as e:
|
||||
bb.fatal("Could not connect to server at %s:%s (%s)" % (host, port, str(e)))
|
||||
|
||||
def endSession(self):
|
||||
self.connection.removeClient()
|
||||
|
||||
@@ -24,7 +24,7 @@ def init(d):
|
||||
break
|
||||
else:
|
||||
logger.error("Invalid signature generator '%s', using default 'noop'\n"
|
||||
"Available generators: %s", desired,
|
||||
"Available generators: %s",
|
||||
', '.join(obj.name for obj in siggens))
|
||||
return SignatureGenerator(d)
|
||||
|
||||
@@ -91,13 +91,13 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
basehash = {}
|
||||
|
||||
for task in tasklist:
|
||||
data = lookupcache[task]
|
||||
data = d.getVar(task, False)
|
||||
lookupcache[task] = data
|
||||
|
||||
if data is None:
|
||||
bb.error("Task %s from %s seems to be empty?!" % (task, fn))
|
||||
data = ''
|
||||
|
||||
gendeps[task] -= self.basewhitelist
|
||||
newdeps = gendeps[task]
|
||||
seen = set()
|
||||
while newdeps:
|
||||
@@ -107,18 +107,26 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
for dep in nextdeps:
|
||||
if dep in self.basewhitelist:
|
||||
continue
|
||||
gendeps[dep] -= self.basewhitelist
|
||||
newdeps |= gendeps[dep]
|
||||
newdeps -= seen
|
||||
|
||||
alldeps = sorted(seen)
|
||||
for dep in alldeps:
|
||||
alldeps = seen - self.basewhitelist
|
||||
|
||||
for dep in sorted(alldeps):
|
||||
data = data + dep
|
||||
var = lookupcache[dep]
|
||||
if var is not None:
|
||||
if dep in lookupcache:
|
||||
var = lookupcache[dep]
|
||||
elif dep[-1] == ']':
|
||||
vf = dep[:-1].split('[')
|
||||
var = d.getVarFlag(vf[0], vf[1], False)
|
||||
lookupcache[dep] = var
|
||||
else:
|
||||
var = d.getVar(dep, False)
|
||||
lookupcache[dep] = var
|
||||
if var:
|
||||
data = data + str(var)
|
||||
self.basehash[fn + "." + task] = hashlib.md5(data).hexdigest()
|
||||
taskdeps[task] = alldeps
|
||||
taskdeps[task] = sorted(alldeps)
|
||||
|
||||
self.taskdeps[fn] = taskdeps
|
||||
self.gendeps[fn] = gendeps
|
||||
@@ -192,10 +200,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
||||
return h
|
||||
|
||||
def set_taskdata(self, hashes, deps, checksums):
|
||||
def set_taskdata(self, hashes, deps):
|
||||
self.runtaskdeps = deps
|
||||
self.taskhash = hashes
|
||||
self.file_checksum_values = checksums
|
||||
|
||||
def dump_sigtask(self, fn, task, stampbase, runtime):
|
||||
k = fn + "." + task
|
||||
@@ -241,7 +248,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
os.fsync(fd)
|
||||
os.chmod(tmpfile, 0664)
|
||||
os.rename(tmpfile, sigfile)
|
||||
except (OSError, IOError) as err:
|
||||
except (OSError, IOError), err:
|
||||
try:
|
||||
os.unlink(tmpfile)
|
||||
except OSError:
|
||||
@@ -323,12 +330,12 @@ def compare_sigfiles(a, b, recursecb = None):
|
||||
return changed, added, removed
|
||||
|
||||
if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
|
||||
output.append("basewhitelist changed from '%s' to '%s'" % (a_data['basewhitelist'], b_data['basewhitelist']))
|
||||
output.append("basewhitelist changed from %s to %s" % (a_data['basewhitelist'], b_data['basewhitelist']))
|
||||
if a_data['basewhitelist'] and b_data['basewhitelist']:
|
||||
output.append("changed items: %s" % a_data['basewhitelist'].symmetric_difference(b_data['basewhitelist']))
|
||||
|
||||
if 'taskwhitelist' in a_data and a_data['taskwhitelist'] != b_data['taskwhitelist']:
|
||||
output.append("taskwhitelist changed from '%s' to '%s'" % (a_data['taskwhitelist'], b_data['taskwhitelist']))
|
||||
output.append("taskwhitelist changed from %s to %s" % (a_data['taskwhitelist'], b_data['taskwhitelist']))
|
||||
if a_data['taskwhitelist'] and b_data['taskwhitelist']:
|
||||
output.append("changed items: %s" % a_data['taskwhitelist'].symmetric_difference(b_data['taskwhitelist']))
|
||||
|
||||
@@ -341,7 +348,7 @@ def compare_sigfiles(a, b, recursecb = None):
|
||||
changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'], a_data['basewhitelist'] & b_data['basewhitelist'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
output.append("List of dependencies for variable %s changed from '%s' to '%s'" % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
|
||||
output.append("List of dependencies for variable %s changed from %s to %s" % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
|
||||
if a_data['gendeps'][dep] and b_data['gendeps'][dep]:
|
||||
output.append("changed items: %s" % a_data['gendeps'][dep].symmetric_difference(b_data['gendeps'][dep]))
|
||||
if added:
|
||||
@@ -355,7 +362,7 @@ def compare_sigfiles(a, b, recursecb = None):
|
||||
changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
|
||||
output.append("Variable %s value changed from %s to %s" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
|
||||
|
||||
changed, added, removed = dict_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
|
||||
if changed:
|
||||
|
||||
@@ -390,17 +390,6 @@ class TaskData:
|
||||
reasons.append("%s PROVIDES %s but was skipped: %s" % (skipitem.pn, item, skipitem.skipreason))
|
||||
return reasons
|
||||
|
||||
def get_close_matches(self, item, provider_list):
|
||||
import difflib
|
||||
if self.skiplist:
|
||||
skipped = []
|
||||
for fn in self.skiplist:
|
||||
skipped.append(self.skiplist[fn].pn)
|
||||
full_list = provider_list + skipped
|
||||
else:
|
||||
full_list = provider_list
|
||||
return difflib.get_close_matches(item, full_list, cutoff=0.7)
|
||||
|
||||
def add_provider(self, cfgData, dataCache, item):
|
||||
try:
|
||||
self.add_provider_internal(cfgData, dataCache, item)
|
||||
@@ -422,7 +411,7 @@ class TaskData:
|
||||
return
|
||||
|
||||
if not item in dataCache.providers:
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item), close_matches=self.get_close_matches(item, dataCache.providers.keys())), cfgData)
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item)), cfgData)
|
||||
raise bb.providers.NoProvider(item)
|
||||
|
||||
if self.have_build_target(item):
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# BitBake Test for codeparser.py
|
||||
#
|
||||
@@ -26,9 +24,6 @@ import bb
|
||||
|
||||
logger = logging.getLogger('BitBake.TestCodeParser')
|
||||
|
||||
# bb.data references bb.parse but can't directly import due to circular dependencies.
|
||||
# Hack around it for now :(
|
||||
import bb.parse
|
||||
import bb.data
|
||||
|
||||
class ReferenceTest(unittest.TestCase):
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# BitBake Tests for Copy-on-Write (cow.py)
|
||||
#
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# BitBake Tests for the Data Store (data.py/data_smart.py)
|
||||
#
|
||||
@@ -27,21 +25,21 @@ import bb.data
|
||||
class DataExpansions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.d = bb.data.init()
|
||||
self.d["foo"] = "value_of_foo"
|
||||
self.d["bar"] = "value_of_bar"
|
||||
self.d["value_of_foo"] = "value_of_'value_of_foo'"
|
||||
self.d["foo"] = "value of foo"
|
||||
self.d["bar"] = "value of bar"
|
||||
self.d["value of foo"] = "value of 'value of foo'"
|
||||
|
||||
def test_one_var(self):
|
||||
val = self.d.expand("${foo}")
|
||||
self.assertEqual(str(val), "value_of_foo")
|
||||
self.assertEqual(str(val), "value of foo")
|
||||
|
||||
def test_indirect_one_var(self):
|
||||
val = self.d.expand("${${foo}}")
|
||||
self.assertEqual(str(val), "value_of_'value_of_foo'")
|
||||
self.assertEqual(str(val), "value of 'value of foo'")
|
||||
|
||||
def test_indirect_and_another(self):
|
||||
val = self.d.expand("${${foo}} ${bar}")
|
||||
self.assertEqual(str(val), "value_of_'value_of_foo' value_of_bar")
|
||||
self.assertEqual(str(val), "value of 'value of foo' value of bar")
|
||||
|
||||
def test_python_snippet(self):
|
||||
val = self.d.expand("${@5*12}")
|
||||
@@ -49,11 +47,11 @@ class DataExpansions(unittest.TestCase):
|
||||
|
||||
def test_expand_in_python_snippet(self):
|
||||
val = self.d.expand("${@'boo ' + '${foo}'}")
|
||||
self.assertEqual(str(val), "boo value_of_foo")
|
||||
self.assertEqual(str(val), "boo value of foo")
|
||||
|
||||
def test_python_snippet_getvar(self):
|
||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
||||
self.assertEqual(str(val), "value_of_foo value_of_bar")
|
||||
self.assertEqual(str(val), "value of foo value of bar")
|
||||
|
||||
def test_python_snippet_syntax_error(self):
|
||||
self.d.setVar("FOO", "${@foo = 5}")
|
||||
@@ -70,7 +68,7 @@ class DataExpansions(unittest.TestCase):
|
||||
|
||||
def test_value_containing_value(self):
|
||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
||||
self.assertEqual(str(val), "value_of_foo value_of_bar")
|
||||
self.assertEqual(str(val), "value of foo value of bar")
|
||||
|
||||
def test_reference_undefined_var(self):
|
||||
val = self.d.expand("${undefinedvar} meh")
|
||||
@@ -109,7 +107,7 @@ class DataExpansions(unittest.TestCase):
|
||||
|
||||
def test_rename(self):
|
||||
self.d.renameVar("foo", "newfoo")
|
||||
self.assertEqual(self.d.getVar("newfoo"), "value_of_foo")
|
||||
self.assertEqual(self.d.getVar("newfoo"), "value of foo")
|
||||
self.assertEqual(self.d.getVar("foo"), None)
|
||||
|
||||
def test_deletion(self):
|
||||
@@ -118,17 +116,17 @@ class DataExpansions(unittest.TestCase):
|
||||
|
||||
def test_keys(self):
|
||||
keys = self.d.keys()
|
||||
self.assertEqual(keys, ['value_of_foo', 'foo', 'bar'])
|
||||
self.assertEqual(keys, ['value of foo', 'foo', 'bar'])
|
||||
|
||||
class TestNestedExpansions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.d = bb.data.init()
|
||||
self.d["foo"] = "foo"
|
||||
self.d["bar"] = "bar"
|
||||
self.d["value_of_foobar"] = "187"
|
||||
self.d["value of foobar"] = "187"
|
||||
|
||||
def test_refs(self):
|
||||
val = self.d.expand("${value_of_${foo}${bar}}")
|
||||
val = self.d.expand("${value of ${foo}${bar}}")
|
||||
self.assertEqual(str(val), "187")
|
||||
|
||||
#def test_python_refs(self):
|
||||
@@ -154,11 +152,11 @@ class TestNestedExpansions(unittest.TestCase):
|
||||
# self.assertEqual(str(val), str(depth + 1))
|
||||
|
||||
def test_mixed(self):
|
||||
val = self.d.expand("${value_of_${@('${foo}'+'bar')[0:3]}${${@'BAR'.lower()}}}")
|
||||
val = self.d.expand("${value of ${@('${foo}'+'bar')[0:3]}${${@'BAR'.lower()}}}")
|
||||
self.assertEqual(str(val), "187")
|
||||
|
||||
def test_runtime(self):
|
||||
val = self.d.expand("${${@'value_of' + '_f'+'o'+'o'+'b'+'a'+'r'}}")
|
||||
val = self.d.expand("${${@'value of' + ' f'+'o'+'o'+'b'+'a'+'r'}}")
|
||||
self.assertEqual(str(val), "187")
|
||||
|
||||
class TestMemoize(unittest.TestCase):
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# BitBake Tests for the Fetcher (fetch2/)
|
||||
#
|
||||
@@ -23,252 +21,11 @@ import unittest
|
||||
import tempfile
|
||||
import subprocess
|
||||
import os
|
||||
from bb.fetch2 import URI
|
||||
import bb
|
||||
|
||||
class URITest(unittest.TestCase):
|
||||
test_uris = {
|
||||
"http://www.google.com/index.html" : {
|
||||
'uri': 'http://www.google.com/index.html',
|
||||
'scheme': 'http',
|
||||
'hostname': 'www.google.com',
|
||||
'port': None,
|
||||
'hostport': 'www.google.com',
|
||||
'path': '/index.html',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': False
|
||||
},
|
||||
"http://www.google.com/index.html;param1=value1" : {
|
||||
'uri': 'http://www.google.com/index.html;param1=value1',
|
||||
'scheme': 'http',
|
||||
'hostname': 'www.google.com',
|
||||
'port': None,
|
||||
'hostport': 'www.google.com',
|
||||
'path': '/index.html',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {
|
||||
'param1': 'value1'
|
||||
},
|
||||
'relative': False
|
||||
},
|
||||
"http://www.example.com:8080/index.html" : {
|
||||
'uri': 'http://www.example.com:8080/index.html',
|
||||
'scheme': 'http',
|
||||
'hostname': 'www.example.com',
|
||||
'port': 8080,
|
||||
'hostport': 'www.example.com:8080',
|
||||
'path': '/index.html',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': False
|
||||
},
|
||||
"cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : {
|
||||
'uri': 'cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg',
|
||||
'scheme': 'cvs',
|
||||
'hostname': 'cvs.handhelds.org',
|
||||
'port': None,
|
||||
'hostport': 'cvs.handhelds.org',
|
||||
'path': '/cvs',
|
||||
'userinfo': 'anoncvs',
|
||||
'username': 'anoncvs',
|
||||
'password': '',
|
||||
'params': {
|
||||
'module': 'familiar/dist/ipkg'
|
||||
},
|
||||
'relative': False
|
||||
},
|
||||
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg": {
|
||||
'uri': 'cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg',
|
||||
'scheme': 'cvs',
|
||||
'hostname': 'cvs.handhelds.org',
|
||||
'port': None,
|
||||
'hostport': 'cvs.handhelds.org',
|
||||
'path': '/cvs',
|
||||
'userinfo': 'anoncvs:anonymous',
|
||||
'username': 'anoncvs',
|
||||
'password': 'anonymous',
|
||||
'params': {
|
||||
'tag': 'V0-99-81',
|
||||
'module': 'familiar/dist/ipkg'
|
||||
},
|
||||
'relative': False
|
||||
},
|
||||
"file://example.diff": { # NOTE: Not RFC compliant!
|
||||
'uri': 'file:example.diff',
|
||||
'scheme': 'file',
|
||||
'hostname': '',
|
||||
'port': None,
|
||||
'hostport': '',
|
||||
'path': 'example.diff',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': True
|
||||
},
|
||||
"file:example.diff": { # NOTE: RFC compliant version of the former
|
||||
'uri': 'file:example.diff',
|
||||
'scheme': 'file',
|
||||
'hostname': '',
|
||||
'port': None,
|
||||
'hostport': '',
|
||||
'path': 'example.diff',
|
||||
'userinfo': '',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': True
|
||||
},
|
||||
"file:///tmp/example.diff": {
|
||||
'uri': 'file:///tmp/example.diff',
|
||||
'scheme': 'file',
|
||||
'hostname': '',
|
||||
'port': None,
|
||||
'hostport': '',
|
||||
'path': '/tmp/example.diff',
|
||||
'userinfo': '',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': False
|
||||
},
|
||||
"git:///path/example.git": {
|
||||
'uri': 'git:///path/example.git',
|
||||
'scheme': 'git',
|
||||
'hostname': '',
|
||||
'port': None,
|
||||
'hostport': '',
|
||||
'path': '/path/example.git',
|
||||
'userinfo': '',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': False
|
||||
},
|
||||
"git:path/example.git": {
|
||||
'uri': 'git:path/example.git',
|
||||
'scheme': 'git',
|
||||
'hostname': '',
|
||||
'port': None,
|
||||
'hostport': '',
|
||||
'path': 'path/example.git',
|
||||
'userinfo': '',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': True
|
||||
},
|
||||
"git://example.net/path/example.git": {
|
||||
'uri': 'git://example.net/path/example.git',
|
||||
'scheme': 'git',
|
||||
'hostname': 'example.net',
|
||||
'port': None,
|
||||
'hostport': 'example.net',
|
||||
'path': '/path/example.git',
|
||||
'userinfo': '',
|
||||
'userinfo': '',
|
||||
'username': '',
|
||||
'password': '',
|
||||
'params': {},
|
||||
'relative': False
|
||||
}
|
||||
}
|
||||
|
||||
def test_uri(self):
|
||||
for test_uri, ref in self.test_uris.items():
|
||||
uri = URI(test_uri)
|
||||
|
||||
self.assertEqual(str(uri), ref['uri'])
|
||||
|
||||
# expected attributes
|
||||
self.assertEqual(uri.scheme, ref['scheme'])
|
||||
|
||||
self.assertEqual(uri.userinfo, ref['userinfo'])
|
||||
self.assertEqual(uri.username, ref['username'])
|
||||
self.assertEqual(uri.password, ref['password'])
|
||||
|
||||
self.assertEqual(uri.hostname, ref['hostname'])
|
||||
self.assertEqual(uri.port, ref['port'])
|
||||
self.assertEqual(uri.hostport, ref['hostport'])
|
||||
|
||||
self.assertEqual(uri.path, ref['path'])
|
||||
self.assertEqual(uri.params, ref['params'])
|
||||
|
||||
self.assertEqual(uri.relative, ref['relative'])
|
||||
|
||||
def test_dict(self):
|
||||
for test in self.test_uris.values():
|
||||
uri = URI()
|
||||
|
||||
self.assertEqual(uri.scheme, '')
|
||||
self.assertEqual(uri.userinfo, '')
|
||||
self.assertEqual(uri.username, '')
|
||||
self.assertEqual(uri.password, '')
|
||||
self.assertEqual(uri.hostname, '')
|
||||
self.assertEqual(uri.port, None)
|
||||
self.assertEqual(uri.path, '')
|
||||
self.assertEqual(uri.params, {})
|
||||
|
||||
|
||||
uri.scheme = test['scheme']
|
||||
self.assertEqual(uri.scheme, test['scheme'])
|
||||
|
||||
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'])
|
||||
|
||||
uri.port = test['port']
|
||||
self.assertEqual(uri.port, test['port'])
|
||||
self.assertEqual(uri.hostport, test['hostport'])
|
||||
|
||||
uri.path = test['path']
|
||||
self.assertEqual(uri.path, test['path'])
|
||||
|
||||
uri.params = test['params']
|
||||
self.assertEqual(uri.params, test['params'])
|
||||
|
||||
self.assertEqual(str(uri)+str(uri.relative), str(test['uri'])+str(test['relative']))
|
||||
|
||||
self.assertEqual(str(uri), test['uri'])
|
||||
|
||||
uri.params = {}
|
||||
self.assertEqual(uri.params, {})
|
||||
self.assertEqual(str(uri), (str(uri).split(";"))[0])
|
||||
|
||||
class FetcherTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.d = bb.data.init()
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.dldir = os.path.join(self.tempdir, "download")
|
||||
os.mkdir(self.dldir)
|
||||
self.d.setVar("DL_DIR", self.dldir)
|
||||
self.unpackdir = os.path.join(self.tempdir, "unpacked")
|
||||
os.mkdir(self.unpackdir)
|
||||
persistdir = os.path.join(self.tempdir, "persistdata")
|
||||
self.d.setVar("PERSISTENT_DIR", persistdir)
|
||||
|
||||
def tearDown(self):
|
||||
bb.utils.prunedir(self.tempdir)
|
||||
|
||||
class MirrorUriTest(FetcherTest):
|
||||
|
||||
replaceuris = {
|
||||
("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "http://somewhere.org/somedir/")
|
||||
: "http://somewhere.org/somedir/git2_git.invalid.infradead.org.mtd-utils.git.tar.gz",
|
||||
@@ -309,6 +66,87 @@ class MirrorUriTest(FetcherTest):
|
||||
"https://.*/.* file:///someotherpath/downloads/ \n" \
|
||||
"http://.*/.* file:///someotherpath/downloads/ \n"
|
||||
|
||||
def setUp(self):
|
||||
self.d = bb.data.init()
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.dldir = os.path.join(self.tempdir, "download")
|
||||
os.mkdir(self.dldir)
|
||||
self.d.setVar("DL_DIR", self.dldir)
|
||||
self.unpackdir = os.path.join(self.tempdir, "unpacked")
|
||||
os.mkdir(self.unpackdir)
|
||||
persistdir = os.path.join(self.tempdir, "persistdata")
|
||||
self.d.setVar("PERSISTENT_DIR", persistdir)
|
||||
|
||||
def tearDown(self):
|
||||
bb.utils.prunedir(self.tempdir)
|
||||
|
||||
def test_fetch(self):
|
||||
fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.1.tar.gz"), 57892)
|
||||
self.d.setVar("BB_NO_NETWORK", "1")
|
||||
fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
fetcher.unpack(self.unpackdir)
|
||||
self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.0/")), 9)
|
||||
self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.1/")), 9)
|
||||
|
||||
def test_fetch_mirror(self):
|
||||
self.d.setVar("MIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake")
|
||||
fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
|
||||
def test_fetch_premirror(self):
|
||||
self.d.setVar("PREMIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake")
|
||||
fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
|
||||
def gitfetcher(self, url1, url2):
|
||||
def checkrevision(self, fetcher):
|
||||
fetcher.unpack(self.unpackdir)
|
||||
revision = subprocess.check_output("git rev-parse HEAD", shell=True, cwd=self.unpackdir + "/git").strip()
|
||||
self.assertEqual(revision, "270a05b0b4ba0959fe0624d2a4885d7b70426da5")
|
||||
|
||||
self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "1")
|
||||
self.d.setVar("SRCREV", "270a05b0b4ba0959fe0624d2a4885d7b70426da5")
|
||||
fetcher = bb.fetch.Fetch([url1], self.d)
|
||||
fetcher.download()
|
||||
checkrevision(self, fetcher)
|
||||
# Wipe out the dldir clone and the unpacked source, turn off the network and check mirror tarball works
|
||||
bb.utils.prunedir(self.dldir + "/git2/")
|
||||
bb.utils.prunedir(self.unpackdir)
|
||||
self.d.setVar("BB_NO_NETWORK", "1")
|
||||
fetcher = bb.fetch.Fetch([url2], self.d)
|
||||
fetcher.download()
|
||||
checkrevision(self, fetcher)
|
||||
|
||||
def test_gitfetch(self):
|
||||
url1 = url2 = "git://git.openembedded.org/bitbake"
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror(self):
|
||||
url1 = "git://git.openembedded.org/bitbake"
|
||||
url2 = "git://someserver.org/bitbake"
|
||||
self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n")
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror2(self):
|
||||
url1 = url2 = "git://someserver.org/bitbake"
|
||||
self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n")
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror3(self):
|
||||
realurl = "git://git.openembedded.org/bitbake"
|
||||
dummyurl = "git://someserver.org/bitbake"
|
||||
self.sourcedir = self.unpackdir.replace("unpacked", "sourcemirror.git")
|
||||
os.chdir(self.tempdir)
|
||||
subprocess.check_output("git clone %s %s 2> /dev/null" % (realurl, self.sourcedir), shell=True)
|
||||
self.d.setVar("PREMIRRORS", "%s git://%s;protocol=file \n" % (dummyurl, self.sourcedir))
|
||||
self.gitfetcher(dummyurl, dummyurl)
|
||||
|
||||
def test_urireplace(self):
|
||||
for k, v in self.replaceuris.items():
|
||||
ud = bb.fetch.FetchData(k[0], self.d)
|
||||
@@ -330,85 +168,13 @@ 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 FetcherNetworkTest(FetcherTest):
|
||||
|
||||
if os.environ.get("BB_SKIP_NETTESTS") == "yes":
|
||||
print("Unset BB_SKIP_NETTESTS to run network tests")
|
||||
else:
|
||||
def test_fetch(self):
|
||||
fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.1.tar.gz"), 57892)
|
||||
self.d.setVar("BB_NO_NETWORK", "1")
|
||||
fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
fetcher.unpack(self.unpackdir)
|
||||
self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.0/")), 9)
|
||||
self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.1/")), 9)
|
||||
|
||||
def test_fetch_mirror(self):
|
||||
self.d.setVar("MIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake")
|
||||
fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
|
||||
def test_fetch_premirror(self):
|
||||
self.d.setVar("PREMIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake")
|
||||
fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
|
||||
fetcher.download()
|
||||
self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
|
||||
|
||||
def gitfetcher(self, url1, url2):
|
||||
def checkrevision(self, fetcher):
|
||||
fetcher.unpack(self.unpackdir)
|
||||
revision = bb.process.run("git rev-parse HEAD", shell=True, cwd=self.unpackdir + "/git")[0].strip()
|
||||
self.assertEqual(revision, "270a05b0b4ba0959fe0624d2a4885d7b70426da5")
|
||||
|
||||
self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "1")
|
||||
self.d.setVar("SRCREV", "270a05b0b4ba0959fe0624d2a4885d7b70426da5")
|
||||
fetcher = bb.fetch.Fetch([url1], self.d)
|
||||
fetcher.download()
|
||||
checkrevision(self, fetcher)
|
||||
# Wipe out the dldir clone and the unpacked source, turn off the network and check mirror tarball works
|
||||
bb.utils.prunedir(self.dldir + "/git2/")
|
||||
bb.utils.prunedir(self.unpackdir)
|
||||
self.d.setVar("BB_NO_NETWORK", "1")
|
||||
fetcher = bb.fetch.Fetch([url2], self.d)
|
||||
fetcher.download()
|
||||
checkrevision(self, fetcher)
|
||||
|
||||
def test_gitfetch(self):
|
||||
url1 = url2 = "git://git.openembedded.org/bitbake"
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror(self):
|
||||
url1 = "git://git.openembedded.org/bitbake"
|
||||
url2 = "git://someserver.org/bitbake"
|
||||
self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n")
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror2(self):
|
||||
url1 = url2 = "git://someserver.org/bitbake"
|
||||
self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n")
|
||||
self.gitfetcher(url1, url2)
|
||||
|
||||
def test_gitfetch_premirror3(self):
|
||||
realurl = "git://git.openembedded.org/bitbake"
|
||||
dummyurl = "git://someserver.org/bitbake"
|
||||
self.sourcedir = self.unpackdir.replace("unpacked", "sourcemirror.git")
|
||||
os.chdir(self.tempdir)
|
||||
bb.process.run("git clone %s %s 2> /dev/null" % (realurl, self.sourcedir), shell=True)
|
||||
self.d.setVar("PREMIRRORS", "%s git://%s;protocol=file \n" % (dummyurl, self.sourcedir))
|
||||
self.gitfetcher(dummyurl, dummyurl)
|
||||
|
||||
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):
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# BitBake Tests for utils.py
|
||||
#
|
||||
|
||||
@@ -25,31 +25,29 @@ import bb.cache
|
||||
import bb.cooker
|
||||
import bb.providers
|
||||
import bb.utils
|
||||
from bb.cooker import state, BBCooker
|
||||
from bb.cookerdata import CookerConfiguration, ConfigParameters
|
||||
from bb.cooker import state
|
||||
import bb.fetch2
|
||||
|
||||
class Tinfoil:
|
||||
def __init__(self, output=sys.stdout):
|
||||
def __init__(self):
|
||||
# Needed to avoid deprecation warnings with python 2.6
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
# Set up logging
|
||||
self.logger = logging.getLogger('BitBake')
|
||||
console = logging.StreamHandler(output)
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console = logging.StreamHandler(sys.stdout)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
self.logger.addHandler(console)
|
||||
|
||||
self.config = CookerConfiguration()
|
||||
configparams = TinfoilConfigParameters(parse_only=True)
|
||||
self.config.setConfigParameters(configparams)
|
||||
self.config.setServerRegIdleCallback(self.register_idle_function)
|
||||
self.cooker = BBCooker(self.config)
|
||||
self.config_data = self.cooker.data
|
||||
initialenv = os.environ.copy()
|
||||
bb.utils.clean_environment()
|
||||
self.config = TinfoilConfig(parse_only=True)
|
||||
self.cooker = bb.cooker.BBCooker(self.config,
|
||||
self.register_idle_function,
|
||||
initialenv)
|
||||
self.config_data = self.cooker.configuration.data
|
||||
bb.providers.logger.setLevel(logging.ERROR)
|
||||
self.cooker_data = None
|
||||
|
||||
@@ -71,26 +69,30 @@ class Tinfoil:
|
||||
self.logger.setLevel(logging.INFO)
|
||||
sys.stderr.write("done.\n")
|
||||
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
self.cooker_data = self.cooker.status
|
||||
|
||||
def prepare(self, config_only = False):
|
||||
if not self.cooker_data:
|
||||
if config_only:
|
||||
self.cooker.parseConfiguration()
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
self.cooker_data = self.cooker.status
|
||||
else:
|
||||
self.parseRecipes()
|
||||
|
||||
class TinfoilConfigParameters(ConfigParameters):
|
||||
|
||||
class TinfoilConfig(object):
|
||||
def __init__(self, **options):
|
||||
self.initial_options = options
|
||||
super(TinfoilConfigParameters, self).__init__()
|
||||
self.pkgs_to_build = []
|
||||
self.debug_domains = []
|
||||
self.extra_assume_provided = []
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.debug = 0
|
||||
self.__dict__.update(options)
|
||||
|
||||
def parseCommandLine(self):
|
||||
class DummyOptions:
|
||||
def __init__(self, initial_options):
|
||||
for key, val in initial_options.items():
|
||||
setattr(self, key, val)
|
||||
def __getattr__(self, attribute):
|
||||
try:
|
||||
return super(TinfoilConfig, self).__getattribute__(attribute)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return DummyOptions(self.initial_options), None
|
||||
|
||||
@@ -30,8 +30,7 @@ import shlex
|
||||
import re
|
||||
import logging
|
||||
import sys
|
||||
import signal
|
||||
import time
|
||||
from bb.ui.crumbs.template import TemplateMgr
|
||||
from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
|
||||
from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
|
||||
from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
|
||||
@@ -48,7 +47,6 @@ from bb.ui.crumbs.hig.deployimagedialog import DeployImageDialog
|
||||
from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
|
||||
from bb.ui.crumbs.hig.imageselectiondialog import ImageSelectionDialog
|
||||
from bb.ui.crumbs.hig.parsingwarningsdialog import ParsingWarningsDialog
|
||||
from bb.ui.crumbs.hig.propertydialog import PropertyDialog
|
||||
|
||||
hobVer = 20120808
|
||||
|
||||
@@ -57,7 +55,7 @@ class Configuration:
|
||||
|
||||
@classmethod
|
||||
def parse_proxy_string(cls, proxy):
|
||||
pattern = "^\s*((http|https|ftp|socks|cvs)://)?((\S+):(\S+)@)?([^\s:]+)(:(\d+))?/?"
|
||||
pattern = "^\s*((http|https|ftp|git|cvs)://)?((\S+):(\S+)@)?([^\s:]+)(:(\d+))?/?"
|
||||
match = re.search(pattern, proxy)
|
||||
if match:
|
||||
return match.group(2), match.group(4), match.group(5), match.group(6), match.group(8)
|
||||
@@ -109,8 +107,6 @@ class Configuration:
|
||||
self.extra_setting = {}
|
||||
self.toolchain_build = False
|
||||
self.image_fstypes = ""
|
||||
self.image_size = None
|
||||
self.image_packages = []
|
||||
# bblayers.conf
|
||||
self.layers = []
|
||||
# image/recipes/packages
|
||||
@@ -127,7 +123,7 @@ class Configuration:
|
||||
"http" : [None, None, None, "", ""], # protocol : [prot, user, passwd, host, port]
|
||||
"https" : [None, None, None, "", ""],
|
||||
"ftp" : [None, None, None, "", ""],
|
||||
"socks" : [None, None, None, "", ""],
|
||||
"git" : [None, None, None, "", ""],
|
||||
"cvs" : [None, None, None, "", ""],
|
||||
}
|
||||
|
||||
@@ -184,57 +180,99 @@ class Configuration:
|
||||
self.default_task = params["default_task"]
|
||||
|
||||
# proxy settings
|
||||
self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" \
|
||||
or params["ftp_proxy"] != "" or params["socks_proxy"] != "" \
|
||||
self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" or params["ftp_proxy"] != "" \
|
||||
or params["git_proxy_host"] != "" or params["git_proxy_port"] != "" \
|
||||
or params["cvs_proxy_host"] != "" or params["cvs_proxy_port"] != ""
|
||||
self.split_proxy("http", params["http_proxy"])
|
||||
self.split_proxy("https", params["https_proxy"])
|
||||
self.split_proxy("ftp", params["ftp_proxy"])
|
||||
self.split_proxy("socks", params["socks_proxy"])
|
||||
self.split_proxy("git", params["git_proxy_host"] + ":" + params["git_proxy_port"])
|
||||
self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"])
|
||||
|
||||
def save(self, handler, defaults=False):
|
||||
def load(self, template):
|
||||
self.curr_mach = template.getVar("MACHINE")
|
||||
self.curr_package_format = " ".join(template.getVar("PACKAGE_CLASSES").split("package_")).strip()
|
||||
self.curr_distro = template.getVar("DISTRO")
|
||||
self.dldir = template.getVar("DL_DIR")
|
||||
self.sstatedir = template.getVar("SSTATE_DIR")
|
||||
self.sstatemirror = template.getVar("SSTATE_MIRRORS")
|
||||
try:
|
||||
self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1])
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.bbthread = int(template.getVar("BB_NUMBER_THREADS"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
|
||||
except:
|
||||
pass
|
||||
# image_overhead_factor is read-only.
|
||||
self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE")
|
||||
self.curr_sdk_machine = template.getVar("SDKMACHINE")
|
||||
self.conf_version = template.getVar("CONF_VERSION")
|
||||
self.lconf_version = template.getVar("LCONF_VERSION")
|
||||
self.extra_setting = eval(template.getVar("EXTRA_SETTING"))
|
||||
self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD"))
|
||||
self.image_fstypes = template.getVar("IMAGE_FSTYPES")
|
||||
# bblayers.conf
|
||||
handler.set_var_in_file("BBLAYERS", self.layers, "bblayers.conf")
|
||||
self.layers = template.getVar("BBLAYERS").split()
|
||||
# image/recipes/packages
|
||||
self.selected_image = template.getVar("__SELECTED_IMAGE__")
|
||||
self.selected_recipes = template.getVar("DEPENDS").split()
|
||||
self.selected_packages = template.getVar("IMAGE_INSTALL").split()
|
||||
# proxy
|
||||
self.enable_proxy = eval(template.getVar("enable_proxy"))
|
||||
self.same_proxy = eval(template.getVar("use_same_proxy"))
|
||||
self.split_proxy("http", template.getVar("http_proxy"))
|
||||
self.split_proxy("https", template.getVar("https_proxy"))
|
||||
self.split_proxy("ftp", template.getVar("ftp_proxy"))
|
||||
self.split_proxy("git", template.getVar("GIT_PROXY_HOST") + ":" + template.getVar("GIT_PROXY_PORT"))
|
||||
self.split_proxy("cvs", template.getVar("CVS_PROXY_HOST") + ":" + template.getVar("CVS_PROXY_PORT"))
|
||||
|
||||
def save(self, template, defaults=False):
|
||||
template.setVar("VERSION", "%s" % hobVer)
|
||||
# bblayers.conf
|
||||
template.setVar("BBLAYERS", " ".join(self.layers))
|
||||
# local.conf
|
||||
if not defaults:
|
||||
handler.early_assign_var_in_file("MACHINE", self.curr_mach, "local.conf")
|
||||
handler.set_var_in_file("DISTRO", self.curr_distro, "local.conf")
|
||||
handler.set_var_in_file("DL_DIR", self.dldir, "local.conf")
|
||||
handler.set_var_in_file("SSTATE_DIR", self.sstatedir, "local.conf")
|
||||
sstate_mirror_list = self.sstatemirror.split("\\n ")
|
||||
sstate_mirror_list_modified = []
|
||||
for mirror in sstate_mirror_list:
|
||||
if mirror != "":
|
||||
mirror = mirror + "\\n"
|
||||
sstate_mirror_list_modified.append(mirror)
|
||||
handler.set_var_in_file("SSTATE_MIRRORS", sstate_mirror_list_modified, "local.conf")
|
||||
handler.set_var_in_file("PARALLEL_MAKE", "-j %s" % self.pmake, "local.conf")
|
||||
handler.set_var_in_file("BB_NUMBER_THREADS", self.bbthread, "local.conf")
|
||||
handler.set_var_in_file("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]), "local.conf")
|
||||
handler.set_var_in_file("IMAGE_ROOTFS_SIZE", self.image_rootfs_size, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_EXTRA_SPACE", self.image_extra_size, "local.conf")
|
||||
handler.set_var_in_file("INCOMPATIBLE_LICENSE", self.incompat_license, "local.conf")
|
||||
handler.set_var_in_file("SDKMACHINE", self.curr_sdk_machine, "local.conf")
|
||||
handler.set_var_in_file("CONF_VERSION", self.conf_version, "local.conf")
|
||||
handler.set_var_in_file("LCONF_VERSION", self.lconf_version, "bblayers.conf")
|
||||
handler.set_extra_config(self.extra_setting)
|
||||
handler.set_var_in_file("TOOLCHAIN_BUILD", self.toolchain_build, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_FSTYPES", self.image_fstypes, "local.conf")
|
||||
template.setVar("MACHINE", self.curr_mach)
|
||||
template.setVar("DISTRO", self.curr_distro)
|
||||
template.setVar("DL_DIR", self.dldir)
|
||||
template.setVar("SSTATE_DIR", self.sstatedir)
|
||||
template.setVar("SSTATE_MIRRORS", self.sstatemirror)
|
||||
template.setVar("PARALLEL_MAKE", "-j %s" % self.pmake)
|
||||
template.setVar("BB_NUMBER_THREADS", self.bbthread)
|
||||
template.setVar("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]))
|
||||
template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size)
|
||||
template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size)
|
||||
template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license)
|
||||
template.setVar("SDKMACHINE", self.curr_sdk_machine)
|
||||
template.setVar("CONF_VERSION", self.conf_version)
|
||||
template.setVar("LCONF_VERSION", self.lconf_version)
|
||||
template.setVar("EXTRA_SETTING", self.extra_setting)
|
||||
template.setVar("TOOLCHAIN_BUILD", self.toolchain_build)
|
||||
template.setVar("IMAGE_FSTYPES", self.image_fstypes)
|
||||
if not defaults:
|
||||
# image/recipes/packages
|
||||
handler.set_var_in_file("__SELECTED_IMAGE__", self.selected_image, "local.conf")
|
||||
handler.set_var_in_file("DEPENDS", self.selected_recipes, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_INSTALL", self.user_selected_packages, "local.conf")
|
||||
template.setVar("__SELECTED_IMAGE__", self.selected_image)
|
||||
template.setVar("DEPENDS", self.selected_recipes)
|
||||
template.setVar("IMAGE_INSTALL", self.user_selected_packages)
|
||||
# proxy
|
||||
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")
|
||||
template.setVar("enable_proxy", self.enable_proxy)
|
||||
template.setVar("use_same_proxy", self.same_proxy)
|
||||
template.setVar("http_proxy", self.combine_proxy("http"))
|
||||
template.setVar("https_proxy", self.combine_proxy("https"))
|
||||
template.setVar("ftp_proxy", self.combine_proxy("ftp"))
|
||||
template.setVar("GIT_PROXY_HOST", self.combine_host_only("git"))
|
||||
template.setVar("GIT_PROXY_PORT", self.combine_port_only("git"))
|
||||
template.setVar("CVS_PROXY_HOST", self.combine_host_only("cvs"))
|
||||
template.setVar("CVS_PROXY_PORT", self.combine_port_only("cvs"))
|
||||
|
||||
def __str__(self):
|
||||
s = "VERSION: '%s', BBLAYERS: '%s', MACHINE: '%s', DISTRO: '%s', DL_DIR: '%s'," % \
|
||||
@@ -247,8 +285,8 @@ class Configuration:
|
||||
(self.lconf_version, self.extra_setting, self.toolchain_build, self.image_fstypes, self.selected_image)
|
||||
s += "DEPENDS: '%s', IMAGE_INSTALL: '%s', enable_proxy: '%s', use_same_proxy: '%s', http_proxy: '%s', " % \
|
||||
(self.selected_recipes, self.user_selected_packages, self.enable_proxy, self.same_proxy, self.combine_proxy("http"))
|
||||
s += "https_proxy: '%s', ftp_proxy: '%s', all_proxy: '%s', CVS_PROXY_HOST: '%s', CVS_PROXY_PORT: '%s'" % \
|
||||
(self.combine_proxy("https"), self.combine_proxy("ftp"), self.combine_proxy("socks"),
|
||||
s += "https_proxy: '%s', ftp_proxy: '%s', GIT_PROXY_HOST: '%s', GIT_PROXY_PORT: '%s', CVS_PROXY_HOST: '%s', CVS_PROXY_PORT: '%s'" % \
|
||||
(self.combine_proxy("https"), self.combine_proxy("ftp"),self.combine_host_only("git"), self.combine_port_only("git"),
|
||||
self.combine_host_only("cvs"), self.combine_port_only("cvs"))
|
||||
return s
|
||||
|
||||
@@ -379,6 +417,8 @@ class Builder(gtk.Window):
|
||||
# handler
|
||||
self.handler = hobHandler
|
||||
|
||||
self.template = None
|
||||
|
||||
# logger
|
||||
self.logger = logging.getLogger("BitBake")
|
||||
self.consolelog = None
|
||||
@@ -441,10 +481,9 @@ 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/*.bb")
|
||||
self.initiate_new_build_async()
|
||||
self.handler.set_config_filter(hob_conf_filter)
|
||||
|
||||
signal.signal(signal.SIGINT, self.event_handle_SIGINT)
|
||||
self.initiate_new_build_async()
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_title("Hob")
|
||||
@@ -513,7 +552,7 @@ class Builder(gtk.Window):
|
||||
if not self.display_sanity_check:
|
||||
func()
|
||||
else:
|
||||
self.sanity_check_post_func = func
|
||||
sanity_check_post_func = func
|
||||
|
||||
def generate_configuration(self):
|
||||
if not self.sanity_checked:
|
||||
@@ -521,11 +560,12 @@ class Builder(gtk.Window):
|
||||
self.handler.generate_configuration()
|
||||
|
||||
def initiate_new_build_async(self):
|
||||
self.configuration.selected_image = None
|
||||
self.switch_page(self.MACHINE_SELECTION)
|
||||
self.handler.init_cooker()
|
||||
self.handler.set_extra_inherit("image_types")
|
||||
self.generate_configuration()
|
||||
if self.load_template(TemplateMgr.convert_to_template_pathfilename("default", ".hob/")) == False:
|
||||
self.show_sanity_check_page()
|
||||
self.handler.init_cooker()
|
||||
self.handler.set_extra_inherit("image_types")
|
||||
self.generate_configuration()
|
||||
|
||||
def update_config_async(self):
|
||||
self.switch_page(self.MACHINE_SELECTION)
|
||||
@@ -596,16 +636,6 @@ class Builder(gtk.Window):
|
||||
toolchain_packages,
|
||||
self.configuration.default_task)
|
||||
|
||||
def generate_new_image(self, image, description):
|
||||
base_image = self.configuration.initial_selected_image
|
||||
if base_image == self.recipe_model.__custom_image__:
|
||||
base_image = None
|
||||
packages = self.package_model.get_selected_packages()
|
||||
self.handler.generate_new_image(image, base_image, packages, description)
|
||||
|
||||
def ensure_dir(self, directory):
|
||||
self.handler.ensure_dir(directory)
|
||||
|
||||
def get_parameters_sync(self):
|
||||
return self.handler.get_parameters()
|
||||
|
||||
@@ -618,6 +648,62 @@ class Builder(gtk.Window):
|
||||
def cancel_parse_sync(self):
|
||||
self.handler.cancel_parse()
|
||||
|
||||
def load_template(self, path):
|
||||
if not os.path.isfile(path):
|
||||
return False
|
||||
|
||||
self.template = TemplateMgr()
|
||||
# check compatibility
|
||||
tempVer = self.template.getVersion(path)
|
||||
if not tempVer or int(tempVer) < hobVer:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
return False
|
||||
|
||||
try:
|
||||
self.template.load(path)
|
||||
self.configuration.load(self.template)
|
||||
except Exception as e:
|
||||
self.show_error_dialog("Hob Exception - %s" % (str(e)))
|
||||
self.reset()
|
||||
finally:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
|
||||
for layer in self.configuration.layers:
|
||||
if not os.path.exists(layer+'/conf/layer.conf'):
|
||||
return False
|
||||
|
||||
self.save_defaults() # remember layers and settings
|
||||
self.update_config_async()
|
||||
return True
|
||||
|
||||
def save_template(self, path, defaults=False):
|
||||
if path.rfind("/") == -1:
|
||||
filename = "default"
|
||||
path = "."
|
||||
else:
|
||||
filename = path[path.rfind("/") + 1:len(path)]
|
||||
path = path[0:path.rfind("/")]
|
||||
|
||||
self.template = TemplateMgr()
|
||||
try:
|
||||
self.template.open(filename, path)
|
||||
self.configuration.save(self.template, defaults)
|
||||
|
||||
self.template.save()
|
||||
except Exception as e:
|
||||
self.show_error_dialog("Hob Exception - %s" % (str(e)))
|
||||
self.reset()
|
||||
finally:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
|
||||
def save_defaults(self):
|
||||
if not os.path.exists(".hob/"):
|
||||
os.mkdir(".hob/")
|
||||
self.save_template(".hob/default", True)
|
||||
|
||||
def switch_page(self, next_step):
|
||||
# Main Workflow (Business Logic)
|
||||
self.nb.set_current_page(self.__step2page__[next_step])
|
||||
@@ -684,27 +770,17 @@ class Builder(gtk.Window):
|
||||
self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
|
||||
self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
|
||||
self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
|
||||
self.handler.set_socks_proxy(self.configuration.combine_proxy("socks"))
|
||||
self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git"))
|
||||
self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
|
||||
elif self.configuration.enable_proxy == False:
|
||||
self.handler.set_http_proxy("")
|
||||
self.handler.set_https_proxy("")
|
||||
self.handler.set_ftp_proxy("")
|
||||
self.handler.set_socks_proxy("")
|
||||
self.handler.set_git_proxy("", "")
|
||||
self.handler.set_cvs_proxy("", "")
|
||||
|
||||
def set_user_config_extra(self):
|
||||
self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
|
||||
self.handler.set_extra_size(self.configuration.image_extra_size)
|
||||
self.handler.set_incompatible_license(self.configuration.incompat_license)
|
||||
self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
|
||||
self.handler.set_image_fstypes(self.configuration.image_fstypes)
|
||||
self.handler.set_extra_config(self.configuration.extra_setting)
|
||||
self.handler.set_extra_inherit("packageinfo image_types")
|
||||
self.set_user_config_proxies()
|
||||
|
||||
def set_user_config(self):
|
||||
self.handler.reset_cooker()
|
||||
self.handler.init_cooker()
|
||||
# set bb layers
|
||||
self.handler.set_bblayers(self.configuration.layers)
|
||||
# set local configuration
|
||||
@@ -716,7 +792,15 @@ class Builder(gtk.Window):
|
||||
self.handler.set_sstate_mirrors(self.configuration.sstatemirror)
|
||||
self.handler.set_pmake(self.configuration.pmake)
|
||||
self.handler.set_bbthreads(self.configuration.bbthread)
|
||||
self.set_user_config_extra()
|
||||
self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
|
||||
self.handler.set_extra_size(self.configuration.image_extra_size)
|
||||
self.handler.set_incompatible_license(self.configuration.incompat_license)
|
||||
self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
|
||||
self.handler.set_image_fstypes(self.configuration.image_fstypes)
|
||||
self.handler.set_extra_config(self.configuration.extra_setting)
|
||||
self.handler.set_extra_inherit("packageinfo")
|
||||
self.handler.set_extra_inherit("image_types")
|
||||
self.set_user_config_proxies()
|
||||
|
||||
def update_recipe_model(self, selected_image, selected_recipes):
|
||||
self.recipe_model.set_selected_image(selected_image)
|
||||
@@ -953,12 +1037,10 @@ class Builder(gtk.Window):
|
||||
fraction = 0.9
|
||||
elif self.current_step == self.IMAGE_GENERATING:
|
||||
fraction = 1.0
|
||||
version = ""
|
||||
self.parameters.image_names = []
|
||||
selected_image = self.recipe_model.get_selected_image()
|
||||
if selected_image == self.recipe_model.__custom_image__:
|
||||
if self.configuration.initial_selected_image != selected_image:
|
||||
version = self.recipe_model.get_custom_image_version()
|
||||
version = self.recipe_model.get_custom_image_version()
|
||||
linkname = 'hob-image' + version+ "-" + self.configuration.curr_mach
|
||||
else:
|
||||
linkname = selected_image + '-' + self.configuration.curr_mach
|
||||
@@ -1091,12 +1173,6 @@ class Builder(gtk.Window):
|
||||
else:
|
||||
gtk.main_quit()
|
||||
|
||||
def event_handle_SIGINT(self, signal, frame):
|
||||
for w in gtk.window_list_toplevels():
|
||||
if w.get_modal():
|
||||
w.response(gtk.RESPONSE_DELETE_EVENT)
|
||||
sys.exit(0)
|
||||
|
||||
def build_packages(self):
|
||||
_, all_recipes = self.recipe_model.get_selected_recipes()
|
||||
if not all_recipes:
|
||||
@@ -1140,37 +1216,11 @@ class Builder(gtk.Window):
|
||||
|
||||
self.fast_generate_image_async(True)
|
||||
|
||||
def show_recipe_property_dialog(self, properties):
|
||||
information = {}
|
||||
dialog = PropertyDialog(title = properties["name"] +' '+ "properties",
|
||||
parent = self,
|
||||
information = properties,
|
||||
flags = gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.DIALOG_NO_SEPARATOR)
|
||||
def show_binb_dialog(self, binb):
|
||||
markup = "<b>Brought in by:</b>\n%s" % binb
|
||||
ptip = PersistentTooltip(markup, self)
|
||||
|
||||
dialog.set_modal(False)
|
||||
|
||||
button = dialog.add_button("Close", gtk.RESPONSE_NO)
|
||||
HobAltButton.style_button(button)
|
||||
button.connect("clicked", lambda w: dialog.destroy())
|
||||
|
||||
dialog.run()
|
||||
|
||||
def show_packages_property_dialog(self, properties):
|
||||
information = {}
|
||||
dialog = PropertyDialog(title = properties["name"] +' '+ "properties",
|
||||
parent = self,
|
||||
information = properties,
|
||||
flags = gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.DIALOG_NO_SEPARATOR)
|
||||
|
||||
dialog.set_modal(False)
|
||||
|
||||
button = dialog.add_button("Close", gtk.RESPONSE_NO)
|
||||
HobAltButton.style_button(button)
|
||||
button.connect("clicked", lambda w: dialog.destroy())
|
||||
|
||||
dialog.run()
|
||||
ptip.show()
|
||||
|
||||
def show_layer_selection_dialog(self):
|
||||
dialog = LayerSelectionDialog(title = "Layers",
|
||||
@@ -1188,11 +1238,45 @@ class Builder(gtk.Window):
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration.layers = dialog.layers
|
||||
self.save_defaults() # remember layers
|
||||
# DO refresh layers
|
||||
if dialog.layers_changed:
|
||||
self.update_config_async()
|
||||
dialog.destroy()
|
||||
|
||||
def show_load_template_dialog(self):
|
||||
dialog = gtk.FileChooserDialog("Load Template Files", self,
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN)
|
||||
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
|
||||
HobAltButton.style_button(button)
|
||||
button = dialog.add_button("Open", gtk.RESPONSE_YES)
|
||||
HobButton.style_button(button)
|
||||
filter = gtk.FileFilter()
|
||||
filter.set_name("Hob Files")
|
||||
filter.add_pattern("*.hob")
|
||||
dialog.add_filter(filter)
|
||||
|
||||
response = dialog.run()
|
||||
path = None
|
||||
if response == gtk.RESPONSE_YES:
|
||||
path = dialog.get_filename()
|
||||
dialog.destroy()
|
||||
return response == gtk.RESPONSE_YES, path
|
||||
|
||||
def show_save_template_dialog(self):
|
||||
dialog = gtk.FileChooserDialog("Save Template Files", self,
|
||||
gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
|
||||
HobAltButton.style_button(button)
|
||||
button = dialog.add_button("Save", gtk.RESPONSE_YES)
|
||||
HobButton.style_button(button)
|
||||
dialog.set_current_name("hob")
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_YES:
|
||||
path = dialog.get_filename()
|
||||
self.save_template(path)
|
||||
dialog.destroy()
|
||||
|
||||
def get_image_extension(self):
|
||||
image_extension = {}
|
||||
for type in self.parameters.image_types:
|
||||
@@ -1251,7 +1335,7 @@ class Builder(gtk.Window):
|
||||
settings_changed = False
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration = dialog.configuration
|
||||
self.configuration.save(self.handler, True) # remember settings
|
||||
self.save_defaults() # remember settings
|
||||
settings_changed = dialog.settings_changed
|
||||
dialog.destroy()
|
||||
return response == gtk.RESPONSE_YES, settings_changed
|
||||
@@ -1279,7 +1363,7 @@ class Builder(gtk.Window):
|
||||
settings_changed = False
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration = dialog.configuration
|
||||
self.configuration.save(self.handler, True) # remember settings
|
||||
self.save_defaults() # remember settings
|
||||
settings_changed = dialog.settings_changed
|
||||
if dialog.proxy_settings_changed:
|
||||
self.set_user_config_proxies()
|
||||
@@ -1464,13 +1548,3 @@ class Builder(gtk.Window):
|
||||
self.consolelog.setFormatter(format)
|
||||
|
||||
self.logger.addHandler(self.consolelog)
|
||||
|
||||
def get_topdir(self):
|
||||
return self.handler.get_topdir()
|
||||
|
||||
def wait(self, delay):
|
||||
time_start = time.time()
|
||||
time_end = time_start + delay
|
||||
while time_end > time.time():
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration()
|
||||
|
||||
@@ -131,7 +131,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.rootfs_size_spinner = None
|
||||
self.extra_size_spinner = None
|
||||
self.gplv3_checkbox = None
|
||||
self.sdk_checkbox = None
|
||||
self.toolchain_checkbox = None
|
||||
self.image_types_checkbuttons = {}
|
||||
|
||||
self.md5 = self.config_md5()
|
||||
@@ -208,7 +208,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.all_distros[ i ] = "Default"
|
||||
if self.configuration.curr_distro == "defaultsetup":
|
||||
self.configuration.curr_distro = "Default"
|
||||
distro_widget, self.distro_combo = self.gen_combo_widget(self.configuration.curr_distro, self.all_distros,"<b>Distro</b>" + "*" + tooltip)
|
||||
distro_widget, self.distro_combo = self.gen_combo_widget(self.configuration.curr_distro, self.all_distros, tooltip)
|
||||
distro_vbox.pack_start(label, expand=False, fill=False)
|
||||
distro_vbox.pack_start(distro_widget, expand=False, fill=False)
|
||||
main_vbox.pack_start(distro_vbox, expand=False, fill=False)
|
||||
@@ -219,7 +219,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(table, expand=False, fill=False)
|
||||
|
||||
tooltip = "Image file system types you want."
|
||||
info = HobInfoButton("<b>Image types</b>" + "*" + tooltip, self)
|
||||
info = HobInfoButton(tooltip, self)
|
||||
label = self.gen_label_widget("Image types:")
|
||||
align = gtk.Alignment(0, 0.5, 0, 0)
|
||||
table.attach(align, 0, 4, 0, 1)
|
||||
@@ -234,10 +234,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
article = ""
|
||||
if image_type.startswith(("a", "e", "i", "o", "u")):
|
||||
article = "n"
|
||||
if image_type == "live":
|
||||
self.image_types_checkbuttons[image_type].set_tooltip_text("Build iso and hddimg images")
|
||||
else:
|
||||
self.image_types_checkbuttons[image_type].set_tooltip_text("Build a%s %s image" % (article, image_type))
|
||||
self.image_types_checkbuttons[image_type].set_tooltip_text("Build a%s %s image" % (article, image_type))
|
||||
table.attach(self.image_types_checkbuttons[image_type], j - 1, j + 3, i, i + 1)
|
||||
if image_type in self.configuration.image_fstypes.split():
|
||||
self.image_types_checkbuttons[image_type].set_active(True)
|
||||
@@ -260,23 +257,23 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
tooltip_combo = "Selects the package format used to generate rootfs."
|
||||
tooltip_extra = "Selects extra package formats to build"
|
||||
pkgfmt_widget, self.rootfs_combo, self.check_hbox = self.gen_pkgfmt_widget(self.configuration.curr_package_format, self.all_package_formats,"<b>Root file system package format</b>" + "*" + tooltip_combo,"<b>Additional package formats</b>" + "*" + tooltip_extra)
|
||||
pkgfmt_widget, self.rootfs_combo, self.check_hbox = self.gen_pkgfmt_widget(self.configuration.curr_package_format, self.all_package_formats, tooltip_combo, tooltip_extra)
|
||||
sub_vbox.pack_start(pkgfmt_widget, expand=False, fill=False)
|
||||
|
||||
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Image size</span>'), expand=False, fill=False)
|
||||
sub_vbox = gtk.VBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("Image basic size (in MB)")
|
||||
tooltip = "Defines the size for the generated image. The OpenEmbedded build system determines the final size for the generated image using an algorithm that takes into account the initial disk space used for the generated image, the Image basic size value, and the Additional free space value.\n\nFor more information, check the <a href=\"http://www.yoctoproject.org/docs/current/poky-ref-manual/poky-ref-manual.html#var-IMAGE_ROOTFS_SIZE\">Yocto Project Reference Manual</a>."
|
||||
rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536,"<b>Image basic size</b>" + "*" + tooltip)
|
||||
tooltip = "Sets the basic size of your target image.\nThis is the basic size of your target image unless your selected package size exceeds this value or you select \'Image Extra Size\'."
|
||||
rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(rootfs_size_widget, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("Additional free space (in MB)")
|
||||
tooltip = "Sets extra free disk space to be added to the generated image. Use this variable when you want to ensure that a specific amount of free disk space is available on a device after an image is installed and running."
|
||||
extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536,"<b>Additional free space</b>" + "*" + tooltip)
|
||||
tooltip = "Sets the extra free space of your target image.\nBy default, the system reserves 30% of your image size as free space. If your image contains zypper, it brings in 50MB more space. The maximum free space is 64GB."
|
||||
extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(extra_size_widget, expand=False, fill=False)
|
||||
|
||||
@@ -289,17 +286,16 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.gplv3_checkbox.set_active(False)
|
||||
advanced_vbox.pack_start(self.gplv3_checkbox, expand=False, fill=False)
|
||||
|
||||
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">SDK</span>'), expand=False, fill=False)
|
||||
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Toolchain</span>'), expand=False, fill=False)
|
||||
sub_hbox = gtk.HBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_hbox, expand=False, fill=False)
|
||||
self.sdk_checkbox = gtk.CheckButton("Populate SDK")
|
||||
tooltip = "Check this box to generate an SDK tarball that consists of the cross-toolchain and a sysroot that contains development packages for your image."
|
||||
self.sdk_checkbox.set_tooltip_text(tooltip)
|
||||
self.sdk_checkbox.set_active(self.configuration.toolchain_build)
|
||||
sub_hbox.pack_start(self.sdk_checkbox, expand=False, fill=False)
|
||||
self.toolchain_checkbox = gtk.CheckButton("Build toolchain")
|
||||
self.toolchain_checkbox.set_tooltip_text("Check this box to build the related toolchain with your image")
|
||||
self.toolchain_checkbox.set_active(self.configuration.toolchain_build)
|
||||
sub_hbox.pack_start(self.toolchain_checkbox, expand=False, fill=False)
|
||||
|
||||
tooltip = "Select the host platform for which you want to run the toolchain contained in the SDK tarball."
|
||||
sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines,"<b>Populate SDK</b>" + "*" + tooltip)
|
||||
tooltip = "Selects the host platform for which you want to run the toolchain"
|
||||
sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines, tooltip)
|
||||
sub_hbox.pack_start(sdk_machine_widget, expand=False, fill=False)
|
||||
|
||||
return advanced_vbox
|
||||
@@ -334,7 +330,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.configuration.incompat_license = " ".join(self.configuration.incompat_license or [])
|
||||
self.configuration.incompat_license = self.configuration.incompat_license.strip()
|
||||
|
||||
self.configuration.toolchain_build = self.sdk_checkbox.get_active()
|
||||
self.configuration.toolchain_build = self.toolchain_checkbox.get_active()
|
||||
self.configuration.curr_sdk_machine = self.sdk_machine_combo.get_active_text()
|
||||
md5 = self.config_md5()
|
||||
self.settings_changed = (self.md5 != md5)
|
||||
|
||||
@@ -84,7 +84,7 @@ class ImageSelectionDialog (CrumbsDialog):
|
||||
open_button.connect("clicked", self.select_path_cb, self, entry)
|
||||
table.attach(open_button, 9, 10, 0, 1)
|
||||
|
||||
self.image_table = HobViewTable(self.__columns__, "Images")
|
||||
self.image_table = HobViewTable(self.__columns__)
|
||||
self.image_table.set_size_request(-1, 300)
|
||||
self.image_table.connect("toggled", self.toggled_cb)
|
||||
self.image_table.connect_group_selection(self.table_selected_cb)
|
||||
|
||||
@@ -105,10 +105,8 @@ class ParsingWarningsDialog (CrumbsDialog):
|
||||
def create_visual_elements(self):
|
||||
self.set_size_request(350, 350)
|
||||
self.heading_label = gtk.Label()
|
||||
self.heading_label.set_alignment(0, 0)
|
||||
self.heading_label.set_alignment(0.1, 0)
|
||||
self.warning_label = gtk.Label()
|
||||
self.warning_label.set_selectable(True)
|
||||
self.warning_label.set_alignment(0, 0)
|
||||
self.textWindow = gtk.ScrolledWindow()
|
||||
|
||||
table = gtk.Table(1, 10, False)
|
||||
@@ -157,7 +155,7 @@ class ParsingWarningsDialog (CrumbsDialog):
|
||||
self.vbox.pack_start(self.heading_label, expand=False, fill=False)
|
||||
self.vbox.pack_start(self.warning_label, expand=False, fill=False)
|
||||
self.vbox.pack_start(self.textWindow, expand=False, fill=False)
|
||||
cancel_button = self.add_button("Close", gtk.RESPONSE_CANCEL)
|
||||
cancel_button = self.add_button("Cancel", gtk.RESPONSE_CANCEL)
|
||||
HobAltButton.style_button(cancel_button)
|
||||
|
||||
self.refresh_components()
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011-2013 Intel Corporation
|
||||
#
|
||||
# Authored by Andrei Dinu <andrei.adrianx.dinu@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import string
|
||||
import gtk
|
||||
import gobject
|
||||
import os
|
||||
import tempfile
|
||||
import glib
|
||||
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||
from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
|
||||
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
|
||||
from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
|
||||
|
||||
"""
|
||||
The following are convenience classes for implementing GNOME HIG compliant
|
||||
BitBake GUI's
|
||||
In summary: spacing = 12px, border-width = 6px
|
||||
"""
|
||||
|
||||
class PropertyDialog(CrumbsDialog):
|
||||
|
||||
def __init__(self, title, parent, information, flags, buttons=None):
|
||||
|
||||
super(PropertyDialog, self).__init__(title, parent, flags, buttons)
|
||||
|
||||
self.properties = information
|
||||
|
||||
if len(self.properties) == 10:
|
||||
self.create_recipe_visual_elements()
|
||||
elif len(self.properties) == 5:
|
||||
self.create_package_visual_elements()
|
||||
else:
|
||||
self.create_information_visual_elements()
|
||||
|
||||
|
||||
def create_information_visual_elements(self):
|
||||
|
||||
HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("icons/"))
|
||||
ICON_PACKAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
|
||||
|
||||
self.set_resizable(False)
|
||||
|
||||
self.table = gtk.Table(1,1,False)
|
||||
self.table.set_row_spacings(0)
|
||||
self.table.set_col_spacings(0)
|
||||
|
||||
self.image = gtk.Image()
|
||||
self.image.set_from_file(ICON_PACKAGES_DISPLAY_FILE)
|
||||
self.image.set_property("xalign",0)
|
||||
#self.vbox.add(self.image)
|
||||
|
||||
image_info = self.properties.split("*")[0]
|
||||
info = self.properties.split("*")[1]
|
||||
|
||||
vbox = gtk.VBox(True, spacing=30)
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_line_wrap(False)
|
||||
self.label_short.set_markup(image_info)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.info_label = gtk.Label()
|
||||
self.info_label.set_line_wrap(True)
|
||||
self.info_label.set_markup(info)
|
||||
self.info_label.set_property("yalign", 0.5)
|
||||
|
||||
self.table.attach(self.image, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=5,ypadding=5)
|
||||
self.table.attach(self.label_short, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=5)
|
||||
self.table.attach(self.info_label, 0,1,1,2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=10)
|
||||
|
||||
self.vbox.add(self.table)
|
||||
self.connect('delete-event', lambda w, e: self.destroy() or True)
|
||||
|
||||
def treeViewTooltip( self, widget, e, tooltips, cell, emptyText="" ):
|
||||
try:
|
||||
(path,col,x,y) = widget.get_path_at_pos( int(e.x), int(e.y) )
|
||||
it = widget.get_model().get_iter(path)
|
||||
value = widget.get_model().get_value(it,cell)
|
||||
if value in self.tooltip_items:
|
||||
tooltips.set_tip(widget, self.tooltip_items[value])
|
||||
tooltips.enable()
|
||||
else:
|
||||
tooltips.set_tip(widget, emptyText)
|
||||
except:
|
||||
tooltips.set_tip(widget, emptyText)
|
||||
|
||||
|
||||
def create_package_visual_elements(self):
|
||||
|
||||
name = self.properties['name']
|
||||
binb = self.properties['binb']
|
||||
size = self.properties['size']
|
||||
recipe = self.properties['recipe']
|
||||
file_list = self.properties['files_list']
|
||||
|
||||
file_list = file_list.strip("{}'")
|
||||
files_temp = ''
|
||||
paths_temp = ''
|
||||
files_binb = []
|
||||
paths_binb = []
|
||||
|
||||
self.tooltip_items = {}
|
||||
|
||||
self.set_resizable(False)
|
||||
|
||||
#cleaning out the recipe variable
|
||||
recipe = recipe.split("+")[0]
|
||||
|
||||
vbox = gtk.VBox(True,spacing = 0)
|
||||
|
||||
###################################### NAME ROW + COL #################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_size_request(300,-1)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
###################################### SIZE ROW + COL ######################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_size_request(300,-1)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Size: </span>" + size)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
##################################### RECIPE ROW + COL #########################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_size_request(300,-1)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Recipe: </span>" + recipe)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
##################################### BINB ROW + COL #######################################
|
||||
|
||||
if binb != '':
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.label_info = gtk.Label()
|
||||
self.label_info.set_size_request(300,-1)
|
||||
self.label_info.set_selectable(True)
|
||||
self.label_info.set_line_wrap(True)
|
||||
self.label_info.set_markup(binb)
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
#################################### FILES BROUGHT BY PACKAGES ###################################
|
||||
|
||||
if file_list != '':
|
||||
|
||||
self.textWindow = gtk.ScrolledWindow()
|
||||
self.textWindow.set_shadow_type(gtk.SHADOW_IN)
|
||||
self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
self.textWindow.set_size_request(100, 170)
|
||||
|
||||
sstatemirrors_store = gtk.ListStore(str)
|
||||
|
||||
self.sstatemirrors_tv = gtk.TreeView()
|
||||
self.sstatemirrors_tv.set_rules_hint(True)
|
||||
self.sstatemirrors_tv.set_headers_visible(True)
|
||||
self.textWindow.add(self.sstatemirrors_tv)
|
||||
|
||||
self.cell1 = gtk.CellRendererText()
|
||||
col1 = gtk.TreeViewColumn('Package files', self.cell1)
|
||||
col1.set_cell_data_func(self.cell1, self.regex_field)
|
||||
self.sstatemirrors_tv.append_column(col1)
|
||||
|
||||
for items in file_list.split(']'):
|
||||
if len(items) > 1:
|
||||
paths_temp = items.split(":")[0]
|
||||
paths_binb.append(paths_temp.strip(" ,'"))
|
||||
files_temp = items.split(":")[1]
|
||||
files_binb.append(files_temp.strip(" ['"))
|
||||
|
||||
unsorted_list = []
|
||||
|
||||
for items in range(len(paths_binb)):
|
||||
if len(files_binb[items]) > 1:
|
||||
for aduse in (files_binb[items].split(",")):
|
||||
unsorted_list.append(paths_binb[items].split(name)[len(paths_binb[items].split(name))-1] + '/' + aduse.strip(" '"))
|
||||
|
||||
|
||||
unsorted_list.sort()
|
||||
for items in unsorted_list:
|
||||
temp = items
|
||||
while len(items) > 35:
|
||||
items = items[:len(items)/2] + "" + items[len(items)/2+1:]
|
||||
if len(items) == 35:
|
||||
items = items[:len(items)/2] + "..." + items[len(items)/2+3:]
|
||||
self.tooltip_items[items] = temp
|
||||
|
||||
sstatemirrors_store.append([str(items)])
|
||||
|
||||
|
||||
self.sstatemirrors_tv.set_model(sstatemirrors_store)
|
||||
|
||||
tips = gtk.Tooltips()
|
||||
tips.set_tip(self.sstatemirrors_tv, "")
|
||||
self.sstatemirrors_tv.connect("motion-notify-event", self.treeViewTooltip, tips, 0)
|
||||
self.sstatemirrors_tv.set_events(gtk.gdk.POINTER_MOTION_MASK)
|
||||
|
||||
self.vbox.add(self.textWindow)
|
||||
|
||||
self.vbox.show_all()
|
||||
|
||||
|
||||
def regex_field(self, column, cell, model, iter):
|
||||
cell.set_property('text', model.get_value(iter, 0))
|
||||
return
|
||||
|
||||
|
||||
def create_recipe_visual_elements(self):
|
||||
|
||||
summary = self.properties['summary']
|
||||
name = self.properties['name']
|
||||
version = self.properties['version']
|
||||
revision = self.properties['revision']
|
||||
binb = self.properties['binb']
|
||||
group = self.properties['group']
|
||||
license = self.properties['license']
|
||||
homepage = self.properties['homepage']
|
||||
bugtracker = self.properties['bugtracker']
|
||||
description = self.properties['description']
|
||||
|
||||
self.set_resizable(False)
|
||||
|
||||
#cleaning out the version variable and also the summary
|
||||
version = version.split(":")[1]
|
||||
if len(version) > 30:
|
||||
version = version.split("+")[0]
|
||||
else:
|
||||
version = version.split("-")[0]
|
||||
license = license.replace("&" , "and")
|
||||
if (homepage == ''):
|
||||
homepage = 'unknown'
|
||||
if (bugtracker == ''):
|
||||
bugtracker = 'unknown'
|
||||
summary = summary.split("+")[0]
|
||||
|
||||
#calculating the rows needed for the table
|
||||
binb_items_count = len(binb.split(','))
|
||||
binb_items = binb.split(',')
|
||||
|
||||
vbox = gtk.VBox(False,spacing = 0)
|
||||
|
||||
######################################## SUMMARY LABEL #########################################
|
||||
|
||||
if summary != '':
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_width_chars(37)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<b>" + summary + "</b>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
########################################## NAME ROW + COL #######################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
####################################### VERSION ROW + COL ####################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Version: </span>" + version)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
##################################### REVISION ROW + COL #####################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Revision: </span>" + revision)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
################################## GROUP ROW + COL ############################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Group: </span>" + group)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
################################# HOMEPAGE ROW + COL ############################################
|
||||
|
||||
if homepage != 'unknown':
|
||||
self.label_info = gtk.Label()
|
||||
self.label_info.set_selectable(True)
|
||||
self.label_info.set_line_wrap(True)
|
||||
if len(homepage) > 35:
|
||||
self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:35] + "..." + "</a>")
|
||||
else:
|
||||
self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:60] + "</a>")
|
||||
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<b>Homepage: </b>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
################################# BUGTRACKER ROW + COL ###########################################
|
||||
|
||||
if bugtracker != 'unknown':
|
||||
self.label_info = gtk.Label()
|
||||
self.label_info.set_selectable(True)
|
||||
self.label_info.set_line_wrap(True)
|
||||
if len(bugtracker) > 35:
|
||||
self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:35] + "..." + "</a>")
|
||||
else:
|
||||
self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:60] + "</a>")
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<b>Bugtracker: </b>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
################################# LICENSE ROW + COL ############################################
|
||||
|
||||
self.label_info = gtk.Label()
|
||||
self.label_info.set_selectable(True)
|
||||
self.label_info.set_line_wrap(True)
|
||||
self.label_info.set_markup(license)
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">License: </span>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
################################### BINB ROW+COL #############################################
|
||||
|
||||
if binb != '':
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
self.vbox.add(self.label_short)
|
||||
self.label_info = gtk.Label()
|
||||
self.label_info.set_selectable(True)
|
||||
self.label_info.set_width_chars(36)
|
||||
if len(binb) > 200:
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
|
||||
scrolled_window.set_size_request(100,100)
|
||||
self.label_info.set_markup(binb)
|
||||
self.label_info.set_padding(6,6)
|
||||
self.label_info.set_alignment(0,0)
|
||||
self.label_info.set_line_wrap(True)
|
||||
scrolled_window.add_with_viewport(self.label_info)
|
||||
self.vbox.add(scrolled_window)
|
||||
else:
|
||||
self.label_info.set_markup(binb)
|
||||
self.label_info.set_property("xalign", 0)
|
||||
self.label_info.set_line_wrap(True)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
################################ DESCRIPTION TAG ROW #################################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Description </span>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
################################ DESCRIPTION INFORMATION ROW ##########################################
|
||||
|
||||
hbox = gtk.HBox(True,spacing = 0)
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_width_chars(36)
|
||||
if len(description) > 200:
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
|
||||
scrolled_window.set_size_request(100,100)
|
||||
self.label_short.set_markup(description)
|
||||
self.label_short.set_padding(6,6)
|
||||
self.label_short.set_alignment(0,0)
|
||||
self.label_short.set_line_wrap(True)
|
||||
scrolled_window.add_with_viewport(self.label_short)
|
||||
self.vbox.add(scrolled_window)
|
||||
else:
|
||||
self.label_short.set_markup(description)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
self.vbox.show_all()
|
||||
@@ -1,51 +0,0 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
|
||||
class RetrieveImageDialog (gtk.FileChooserDialog):
|
||||
"""
|
||||
This class is used to create a dialog that permits to retrieve
|
||||
a custom image saved previously from Hob.
|
||||
"""
|
||||
def __init__(self, directory,title, parent, flags, buttons=None):
|
||||
super(RetrieveImageDialog, self).__init__(title, None, gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||
self.directory = directory
|
||||
|
||||
# create visual elements on the dialog
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_show_hidden(True)
|
||||
self.set_default_response(gtk.RESPONSE_OK)
|
||||
self.set_current_folder(self.directory)
|
||||
|
||||
vbox = self.get_children()[0].get_children()[0].get_children()[0]
|
||||
for child in vbox.get_children()[0].get_children()[0].get_children()[0].get_children():
|
||||
vbox.get_children()[0].get_children()[0].get_children()[0].remove(child)
|
||||
|
||||
label1 = gtk.Label()
|
||||
label1.set_text("File system" + self.directory)
|
||||
label1.show()
|
||||
vbox.get_children()[0].get_children()[0].get_children()[0].pack_start(label1, expand=False, fill=False, padding=0)
|
||||
vbox.get_children()[0].get_children()[1].get_children()[0].hide()
|
||||
|
||||
self.get_children()[0].get_children()[1].get_children()[0].set_label("Select")
|
||||
@@ -1,160 +0,0 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
import glib
|
||||
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
|
||||
from bb.ui.crumbs.hobwidget import HobButton
|
||||
|
||||
class SaveImageDialog (CrumbsDialog):
|
||||
"""
|
||||
This class is used to create a dialog that permits to save
|
||||
a custom image in a predefined directory.
|
||||
"""
|
||||
def __init__(self, directory, name, description, title, parent, flags, buttons=None):
|
||||
super(SaveImageDialog, self).__init__(title, parent, flags, buttons)
|
||||
self.directory = directory
|
||||
self.builder = parent
|
||||
self.name_field = name
|
||||
self.description_field = description
|
||||
|
||||
# create visual elements on the dialog
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_default_response(gtk.RESPONSE_OK)
|
||||
self.vbox.set_border_width(6)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("<b>Name</b>")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
content = "Image recipe names should be all lowercase and include only alphanumeric\n"
|
||||
content += "characters. The only special character you can use is the ASCII hyphen (-)."
|
||||
sub_label.set_markup(content)
|
||||
self.name_entry = gtk.Entry()
|
||||
self.name_entry.set_text(self.name_field)
|
||||
self.name_entry.set_size_request(350,30)
|
||||
self.name_entry.connect("changed", self.name_entry_changed)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(self.name_entry, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("<b>Description</b> (optional)")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
sub_label.set_markup("The description should be less than 150 characters long.")
|
||||
self.description_entry = gtk.TextView()
|
||||
description_buffer = self.description_entry.get_buffer()
|
||||
description_buffer.set_text(self.description_field)
|
||||
description_buffer.connect("insert-text", self.limit_description_length)
|
||||
self.description_entry.set_wrap_mode(gtk.WRAP_WORD)
|
||||
self.description_entry.set_size_request(350,50)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(self.description_entry, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("Your image recipe will be saved to:")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
sub_label.set_markup(self.directory)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
|
||||
table = gtk.Table(1, 4, True)
|
||||
|
||||
cancel_button = gtk.Button()
|
||||
cancel_button.set_label("Cancel")
|
||||
cancel_button.connect("clicked", self.cancel_button_cb)
|
||||
cancel_button.set_size_request(110, 30)
|
||||
|
||||
self.save_button = gtk.Button()
|
||||
self.save_button.set_label("Save")
|
||||
self.save_button.connect("clicked", self.save_button_cb)
|
||||
self.save_button.set_size_request(110, 30)
|
||||
if self.name_entry.get_text() == '':
|
||||
self.save_button.set_sensitive(False)
|
||||
|
||||
table.attach(cancel_button, 2, 3, 0, 1)
|
||||
table.attach(self.save_button, 3, 4, 0, 1)
|
||||
self.vbox.pack_end(table, expand=False, fill=False)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def limit_description_length(self, textbuffer, iter, text, length):
|
||||
buffer_bounds = textbuffer.get_bounds()
|
||||
entire_text = textbuffer.get_text(*buffer_bounds)
|
||||
entire_text += text
|
||||
if len(entire_text)>150 or text=="\n":
|
||||
textbuffer.emit_stop_by_name("insert-text")
|
||||
|
||||
def name_entry_changed(self, entry):
|
||||
text = entry.get_text()
|
||||
if text == '':
|
||||
self.save_button.set_sensitive(False)
|
||||
else:
|
||||
self.save_button.set_sensitive(True)
|
||||
|
||||
def cancel_button_cb(self, button):
|
||||
self.destroy()
|
||||
|
||||
def save_button_cb(self, button):
|
||||
text = self.name_entry.get_text()
|
||||
new_text = text.replace("-","")
|
||||
description_buffer = self.description_entry.get_buffer()
|
||||
description = description_buffer.get_text(description_buffer.get_start_iter(),description_buffer.get_end_iter())
|
||||
if new_text.islower() and new_text.isalnum():
|
||||
self.builder.image_details_page.image_saved = True
|
||||
self.builder.customized = False
|
||||
self.builder.generate_new_image(self.directory+text, description)
|
||||
self.builder.recipe_model.set_in_list(text, description)
|
||||
self.builder.recipe_model.set_selected_image(text)
|
||||
self.builder.image_details_page.show_page(self.builder.IMAGE_GENERATED)
|
||||
self.builder.image_details_page.name_field_template = text
|
||||
self.builder.image_details_page.description_field_template = description
|
||||
self.destroy()
|
||||
else:
|
||||
self.show_invalid_input_error_dialog()
|
||||
|
||||
def show_invalid_input_error_dialog(self):
|
||||
lbl = "<b>Invalid characters in image recipe name</b>\n"
|
||||
msg = "Image recipe names should be all lowercase and\n"
|
||||
msg += "include only alphanumeric characters. The only\n"
|
||||
msg += "special character you can use is the ASCII hyphen (-)."
|
||||
lbl = lbl + "\n%s\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)
|
||||
|
||||
res = dialog.run()
|
||||
self.name_entry.grab_focus()
|
||||
dialog.destroy()
|
||||
@@ -120,3 +120,97 @@ class SettingsUIHelper():
|
||||
|
||||
hbox.show_all()
|
||||
return hbox, entry
|
||||
|
||||
def gen_mirror_entry_widget(self, content, index, match_content=""):
|
||||
hbox = gtk.HBox(False)
|
||||
entry = gtk.Entry()
|
||||
content = content[:-2]
|
||||
entry.set_text(content)
|
||||
entry.set_size_request(350,30)
|
||||
|
||||
entry_match = gtk.Entry()
|
||||
entry_match.set_text(match_content)
|
||||
entry_match.set_size_request(100,30)
|
||||
|
||||
table = gtk.Table(2, 5, False)
|
||||
table.set_row_spacings(12)
|
||||
table.set_col_spacings(6)
|
||||
hbox.pack_start(table, expand=True, fill=True)
|
||||
|
||||
label_configuration = gtk.Label("Configuration")
|
||||
label_configuration.set_alignment(0.0,0.5)
|
||||
label_mirror_url = gtk.Label("Mirror URL")
|
||||
label_mirror_url.set_alignment(0.0,0.5)
|
||||
label_match = gtk.Label("Match")
|
||||
label_match.set_alignment(0.0,0.5)
|
||||
label_replace_with = gtk.Label("Replace with")
|
||||
label_replace_with.set_alignment(0.0,0.5)
|
||||
|
||||
combo = gtk.combo_box_new_text()
|
||||
combo.append_text("Standard")
|
||||
combo.append_text("Custom")
|
||||
if match_content == "":
|
||||
combo.set_active(0)
|
||||
else:
|
||||
combo.set_active(1)
|
||||
combo.connect("changed", self.on_combo_changed, index)
|
||||
combo.set_size_request(100,30)
|
||||
|
||||
delete_button = HobAltButton("Delete")
|
||||
delete_button.connect("clicked", self.delete_cb, index, entry)
|
||||
if content == "" and index == 0 and len(self.sstatemirrors_list) == 1:
|
||||
delete_button.set_sensitive(False)
|
||||
delete_button.set_size_request(100, 30)
|
||||
|
||||
entry_match.connect("changed", self.insert_entry_match_cb, index)
|
||||
entry.connect("changed", self.insert_entry_cb, index, delete_button)
|
||||
|
||||
if match_content == "":
|
||||
table.attach(label_configuration, 1, 2, 0, 1, xoptions=gtk.SHRINK|gtk.FILL)
|
||||
table.attach(label_mirror_url, 2, 3, 0, 1, xoptions=gtk.SHRINK|gtk.FILL)
|
||||
table.attach(combo, 1, 2, 1, 2, xoptions=gtk.SHRINK)
|
||||
table.attach(entry, 2, 3, 1, 2, xoptions=gtk.SHRINK)
|
||||
table.attach(delete_button, 3, 4, 1, 2, xoptions=gtk.SHRINK)
|
||||
else:
|
||||
table.attach(label_configuration, 1, 2, 0, 1, xoptions=gtk.SHRINK|gtk.FILL)
|
||||
table.attach(label_match, 2, 3, 0, 1, xoptions=gtk.SHRINK|gtk.FILL)
|
||||
table.attach(label_replace_with, 3, 4, 0, 1, xoptions=gtk.SHRINK|gtk.FILL)
|
||||
table.attach(combo, 1, 2, 1, 2, xoptions=gtk.SHRINK)
|
||||
table.attach(entry_match, 2, 3, 1, 2, xoptions=gtk.SHRINK)
|
||||
table.attach(entry, 3, 4, 1, 2, xoptions=gtk.SHRINK)
|
||||
table.attach(delete_button, 4, 5, 1, 2, xoptions=gtk.SHRINK)
|
||||
|
||||
hbox.show_all()
|
||||
return hbox
|
||||
|
||||
def insert_entry_match_cb(self, entry_match, index):
|
||||
self.sstatemirrors_list[index][2] = entry_match.get_text()
|
||||
|
||||
def insert_entry_cb(self, entry, index, button):
|
||||
self.sstatemirrors_list[index][1] = entry.get_text()
|
||||
if entry.get_text() == "" and index == 0:
|
||||
button.set_sensitive(False)
|
||||
else:
|
||||
button.set_sensitive(True)
|
||||
|
||||
def on_combo_changed(self, combo, index):
|
||||
if combo.get_active_text() == "Standard":
|
||||
self.sstatemirrors_list[index][0] = 0
|
||||
self.sstatemirrors_list[index][2] = "file://(.*)"
|
||||
else:
|
||||
self.sstatemirrors_list[index][0] = 1
|
||||
self.refresh_shared_state_page()
|
||||
|
||||
def delete_cb(self, button, index, entry):
|
||||
if index == 0 and len(self.sstatemirrors_list)==1:
|
||||
entry.set_text("")
|
||||
else:
|
||||
self.sstatemirrors_list.pop(index)
|
||||
self.refresh_shared_state_page()
|
||||
|
||||
def add_mirror(self, button):
|
||||
tooltip = "Select the pre-built mirror that will speed your build"
|
||||
index = len(self.sstatemirrors_list)
|
||||
sm_list = [0, "", "file://(.*)"]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
self.refresh_shared_state_page()
|
||||
|
||||
@@ -50,13 +50,6 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
TEST_NETWORK_FAILED,
|
||||
TEST_NETWORK_CANCELED) = range(6)
|
||||
|
||||
TARGETS = [
|
||||
("MY_TREE_MODEL_ROW", gtk.TARGET_SAME_WIDGET, 0),
|
||||
("text/plain", 0, 1),
|
||||
("TEXT", 0, 2),
|
||||
("STRING", 0, 3),
|
||||
]
|
||||
|
||||
def __init__(self, title, configuration, all_image_types,
|
||||
all_package_formats, all_distros, all_sdk_machines,
|
||||
max_threads, parent, flags, handler, buttons=None):
|
||||
@@ -91,8 +84,6 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.proxy_settings_changed = False
|
||||
self.handler = handler
|
||||
self.proxy_test_ran = False
|
||||
self.selected_mirror_row = 0
|
||||
self.new_mirror = False
|
||||
|
||||
# create visual elements on the dialog
|
||||
self.create_visual_elements()
|
||||
@@ -161,13 +152,13 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.ftp_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.ftp_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
|
||||
self.socks_proxy.set_text(self.configuration.combine_host_only("socks"))
|
||||
self.socks_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.socks_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.socks_proxy_port.set_text(self.configuration.combine_port_only("socks"))
|
||||
self.socks_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.socks_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.socks_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.git_proxy.set_text(self.configuration.combine_host_only("git"))
|
||||
self.git_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.git_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.git_proxy_port.set_text(self.configuration.combine_port_only("git"))
|
||||
self.git_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.git_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
self.git_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
|
||||
self.cvs_proxy.set_text(self.configuration.combine_host_only("cvs"))
|
||||
self.cvs_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
|
||||
@@ -201,12 +192,12 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
if self.configuration.same_proxy:
|
||||
self.configuration.split_proxy("https", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
|
||||
self.configuration.split_proxy("ftp", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
|
||||
self.configuration.split_proxy("socks", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
|
||||
self.configuration.split_proxy("git", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
|
||||
self.configuration.split_proxy("cvs", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
|
||||
else:
|
||||
self.configuration.split_proxy("https", self.https_proxy.get_text() + ":" + self.https_proxy_port.get_text())
|
||||
self.configuration.split_proxy("ftp", self.ftp_proxy.get_text() + ":" + self.ftp_proxy_port.get_text())
|
||||
self.configuration.split_proxy("socks", self.socks_proxy.get_text() + ":" + self.socks_proxy_port.get_text())
|
||||
self.configuration.split_proxy("git", self.git_proxy.get_text() + ":" + self.git_proxy_port.get_text())
|
||||
self.configuration.split_proxy("cvs", self.cvs_proxy.get_text() + ":" + self.cvs_proxy_port.get_text())
|
||||
|
||||
def response_cb(self, dialog, response_id):
|
||||
@@ -228,7 +219,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.configuration.sstatedir = self.sstatedir_text.get_text()
|
||||
self.configuration.sstatemirror = ""
|
||||
for mirror in self.sstatemirrors_list:
|
||||
if mirror[1] != "" and mirror[2].startswith("file://"):
|
||||
if mirror[1] != "":
|
||||
if mirror[1].endswith("\\1"):
|
||||
smirror = mirror[2] + " " + mirror[1] + " \\n "
|
||||
else:
|
||||
@@ -260,7 +251,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
tooltip = "Sets the number of threads that BitBake tasks can simultaneously run. See the <a href=\""
|
||||
tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
|
||||
tooltip += "poky-ref-manual.html#var-BB_NUMBER_THREADS\">Poky reference manual</a> for information"
|
||||
bbthread_widget, self.bb_spinner = self.gen_spinner_widget(self.configuration.bbthread, 1, self.max_threads,"<b>BitBake prallalel threads</b>" + "*" + tooltip)
|
||||
bbthread_widget, self.bb_spinner = self.gen_spinner_widget(self.configuration.bbthread, 1, self.max_threads, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(bbthread_widget, expand=False, fill=False)
|
||||
|
||||
@@ -270,7 +261,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
tooltip = "Sets the maximum number of threads the host can use during the build. See the <a href=\""
|
||||
tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
|
||||
tooltip += "poky-ref-manual.html#var-PARALLEL_MAKE\">Poky reference manual</a> for information"
|
||||
pmake_widget, self.pmake_spinner = self.gen_spinner_widget(self.configuration.pmake, 1, self.max_threads,"<b>Make parallel threads</b>" + "*" + tooltip)
|
||||
pmake_widget, self.pmake_spinner = self.gen_spinner_widget(self.configuration.pmake, 1, self.max_threads, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(pmake_widget, expand=False, fill=False)
|
||||
|
||||
@@ -279,7 +270,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("Downloads directory")
|
||||
tooltip = "Select a folder that caches the upstream project source code"
|
||||
dldir_widget, self.dldir_text = self.gen_entry_widget(self.configuration.dldir, self,"<b>Downloaded source code</b>" + "*" + tooltip)
|
||||
dldir_widget, self.dldir_text = self.gen_entry_widget(self.configuration.dldir, self, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(dldir_widget, expand=False, fill=False)
|
||||
|
||||
@@ -293,10 +284,10 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False, padding=24)
|
||||
content = "<span>Shared state directory</span>"
|
||||
tooltip = "Select a folder that caches your prebuilt results"
|
||||
label = self.gen_label_info_widget(content,"<b>Shared state directory</b>" + "*" + tooltip)
|
||||
label = self.gen_label_info_widget(content, tooltip)
|
||||
sstatedir_widget, self.sstatedir_text = self.gen_entry_widget(self.configuration.sstatedir, self)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sstatedir_widget, expand=False, fill=False, padding=6)
|
||||
sub_vbox.pack_start(sstatedir_widget, expand=False, fill=False, padding=12)
|
||||
|
||||
content = "<span weight=\"bold\">Shared state mirrors</span>"
|
||||
tooltip = "URLs pointing to pre-built mirrors that will speed your build. "
|
||||
@@ -305,224 +296,58 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
tooltip += "For more information on shared state mirrors, check the <a href=\""
|
||||
tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
|
||||
tooltip += "poky-ref-manual.html#shared-state\">Yocto Project Reference Manual</a>."
|
||||
table = self.gen_label_info_widget(content,"<b>Shared state mirrors</b>" + "*" + tooltip)
|
||||
advanced_vbox.pack_start(table, expand=False, fill=False, padding=6)
|
||||
table = self.gen_label_info_widget(content, tooltip)
|
||||
advanced_vbox.pack_start(table, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False)
|
||||
advanced_vbox.pack_start(sub_vbox, gtk.TRUE, gtk.TRUE, 0)
|
||||
scroll = gtk.ScrolledWindow()
|
||||
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
scroll.add_with_viewport(sub_vbox)
|
||||
scroll.connect('size-allocate', self.scroll_changed)
|
||||
advanced_vbox.pack_start(scroll, gtk.TRUE, gtk.TRUE, 0)
|
||||
searched_string = "file://"
|
||||
|
||||
if self.sstatemirrors_changed == 0:
|
||||
self.sstatemirrors_changed = 1
|
||||
sstatemirrors = self.configuration.sstatemirror
|
||||
if sstatemirrors == "":
|
||||
sm_list = ["Standard", "", "file://(.*)"]
|
||||
sm_list = [ 0, "", "file://(.*)"]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
else:
|
||||
sstatemirrors = [x for x in sstatemirrors.split('\\n')]
|
||||
for sstatemirror in sstatemirrors:
|
||||
while sstatemirrors.find(searched_string) != -1:
|
||||
if sstatemirrors.find(searched_string,1) != -1:
|
||||
sstatemirror = sstatemirrors[:sstatemirrors.find(searched_string,1)]
|
||||
sstatemirrors = sstatemirrors[sstatemirrors.find(searched_string,1):]
|
||||
else:
|
||||
sstatemirror = sstatemirrors
|
||||
sstatemirrors = sstatemirrors[1:]
|
||||
|
||||
sstatemirror_fields = [x for x in sstatemirror.split(' ') if x.strip()]
|
||||
if len(sstatemirror_fields) == 2:
|
||||
if sstatemirror_fields[0] == "file://(.*)" or sstatemirror_fields[0] == "file://.*":
|
||||
sm_list = ["Standard", sstatemirror_fields[1], sstatemirror_fields[0]]
|
||||
else:
|
||||
sm_list = ["Custom", sstatemirror_fields[1], sstatemirror_fields[0]]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
if sstatemirror_fields[0] == "file://(.*)":
|
||||
sm_list = [ 0, sstatemirror_fields[1], "file://(.*)"]
|
||||
else:
|
||||
sm_list = [ 1, sstatemirror_fields[1], sstatemirror_fields[0]]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
|
||||
sstatemirrors_widget, sstatemirrors_store = self.gen_shared_sstate_widget(self.sstatemirrors_list, self)
|
||||
sub_vbox.pack_start(sstatemirrors_widget, expand=True, fill=True)
|
||||
index = 0
|
||||
for mirror in self.sstatemirrors_list:
|
||||
if mirror[0] == 0:
|
||||
sstatemirror_widget = self.gen_mirror_entry_widget(mirror[1], index)
|
||||
else:
|
||||
sstatemirror_widget = self.gen_mirror_entry_widget(mirror[1], index, mirror[2])
|
||||
sub_vbox.pack_start(sstatemirror_widget, expand=False, fill=False, padding=9)
|
||||
index += 1
|
||||
|
||||
table = gtk.Table(1, 10, False)
|
||||
table = gtk.Table(1, 1, False)
|
||||
table.set_col_spacings(6)
|
||||
add_mirror_button = HobAltButton("Add mirror")
|
||||
add_mirror_button = HobAltButton("Add another mirror")
|
||||
add_mirror_button.connect("clicked", self.add_mirror)
|
||||
add_mirror_button.set_size_request(120,30)
|
||||
add_mirror_button.set_size_request(150,30)
|
||||
table.attach(add_mirror_button, 1, 2, 0, 1, xoptions=gtk.SHRINK)
|
||||
|
||||
self.delete_button = HobAltButton("Delete mirror")
|
||||
self.delete_button.connect("clicked", self.delete_cb)
|
||||
self.delete_button.set_size_request(120, 30)
|
||||
table.attach(self.delete_button, 3, 4, 0, 1, xoptions=gtk.SHRINK)
|
||||
|
||||
advanced_vbox.pack_start(table, expand=False, fill=False, padding=6)
|
||||
advanced_vbox.pack_start(table, expand=False, fill=False, padding=9)
|
||||
|
||||
return advanced_vbox
|
||||
|
||||
def gen_shared_sstate_widget(self, sstatemirrors_list, window):
|
||||
hbox = gtk.HBox(False)
|
||||
|
||||
sstatemirrors_store = gtk.ListStore(str, str, str)
|
||||
for sstatemirror in sstatemirrors_list:
|
||||
sstatemirrors_store.append(sstatemirror)
|
||||
|
||||
self.sstatemirrors_tv = gtk.TreeView()
|
||||
self.sstatemirrors_tv.set_rules_hint(True)
|
||||
self.sstatemirrors_tv.set_headers_visible(True)
|
||||
tree_selection = self.sstatemirrors_tv.get_selection()
|
||||
tree_selection.set_mode(gtk.SELECTION_SINGLE)
|
||||
|
||||
# Allow enable drag and drop of rows including row move
|
||||
self.sstatemirrors_tv.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
|
||||
self.TARGETS,
|
||||
gtk.gdk.ACTION_DEFAULT|
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
self.sstatemirrors_tv.enable_model_drag_dest(self.TARGETS,
|
||||
gtk.gdk.ACTION_DEFAULT)
|
||||
self.sstatemirrors_tv.connect("drag_data_get", self.drag_data_get_cb)
|
||||
self.sstatemirrors_tv.connect("drag_data_received", self.drag_data_received_cb)
|
||||
|
||||
|
||||
self.scroll = gtk.ScrolledWindow()
|
||||
self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
self.scroll.set_shadow_type(gtk.SHADOW_IN)
|
||||
self.scroll.connect('size-allocate', self.scroll_changed)
|
||||
self.scroll.add(self.sstatemirrors_tv)
|
||||
|
||||
#list store for cell renderer
|
||||
m = gtk.ListStore(gobject.TYPE_STRING)
|
||||
m.append(["Standard"])
|
||||
m.append(["Custom"])
|
||||
|
||||
cell0 = gtk.CellRendererCombo()
|
||||
cell0.set_property("model",m)
|
||||
cell0.set_property("text-column", 0)
|
||||
cell0.set_property("editable", True)
|
||||
cell0.set_property("has-entry", False)
|
||||
col0 = gtk.TreeViewColumn("Configuration")
|
||||
col0.pack_start(cell0, False)
|
||||
col0.add_attribute(cell0, "text", 0)
|
||||
col0.set_cell_data_func(cell0, self.configuration_field)
|
||||
self.sstatemirrors_tv.append_column(col0)
|
||||
|
||||
cell0.connect("edited", self.combo_changed, sstatemirrors_store)
|
||||
|
||||
self.cell1 = gtk.CellRendererText()
|
||||
self.cell1.set_padding(5,2)
|
||||
col1 = gtk.TreeViewColumn('Regex', self.cell1)
|
||||
col1.set_cell_data_func(self.cell1, self.regex_field)
|
||||
self.sstatemirrors_tv.append_column(col1)
|
||||
|
||||
self.cell1.connect("edited", self.regex_changed, sstatemirrors_store)
|
||||
|
||||
cell2 = gtk.CellRendererText()
|
||||
cell2.set_padding(5,2)
|
||||
cell2.set_property("editable", True)
|
||||
col2 = gtk.TreeViewColumn('URL', cell2)
|
||||
col2.set_cell_data_func(cell2, self.url_field)
|
||||
self.sstatemirrors_tv.append_column(col2)
|
||||
|
||||
cell2.connect("edited", self.url_changed, sstatemirrors_store)
|
||||
|
||||
self.sstatemirrors_tv.set_model(sstatemirrors_store)
|
||||
self.sstatemirrors_tv.set_cursor(self.selected_mirror_row)
|
||||
hbox.pack_start(self.scroll, expand=True, fill=True)
|
||||
hbox.show_all()
|
||||
|
||||
return hbox, sstatemirrors_store
|
||||
|
||||
def drag_data_get_cb(self, treeview, context, selection, target_id, etime):
|
||||
treeselection = treeview.get_selection()
|
||||
model, iter = treeselection.get_selected()
|
||||
data = model.get_string_from_iter(iter)
|
||||
selection.set(selection.target, 8, data)
|
||||
|
||||
def drag_data_received_cb(self, treeview, context, x, y, selection, info, etime):
|
||||
model = treeview.get_model()
|
||||
data = []
|
||||
tree_iter = model.get_iter_from_string(selection.data)
|
||||
data.append(model.get_value(tree_iter, 0))
|
||||
data.append(model.get_value(tree_iter, 1))
|
||||
data.append(model.get_value(tree_iter, 2))
|
||||
|
||||
drop_info = treeview.get_dest_row_at_pos(x, y)
|
||||
if drop_info:
|
||||
path, position = drop_info
|
||||
iter = model.get_iter(path)
|
||||
if (position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
|
||||
model.insert_before(iter, data)
|
||||
else:
|
||||
model.insert_after(iter, data)
|
||||
else:
|
||||
model.append(data)
|
||||
if context.action == gtk.gdk.ACTION_MOVE:
|
||||
context.finish(True, True, etime)
|
||||
return
|
||||
|
||||
def delete_cb(self, button):
|
||||
selection = self.sstatemirrors_tv.get_selection()
|
||||
tree_model, tree_iter = selection.get_selected()
|
||||
index = int(tree_model.get_string_from_iter(tree_iter))
|
||||
if index == 0:
|
||||
self.selected_mirror_row = index
|
||||
else:
|
||||
self.selected_mirror_row = index - 1
|
||||
self.sstatemirrors_list.pop(index)
|
||||
self.refresh_shared_state_page()
|
||||
if not self.sstatemirrors_list:
|
||||
self.delete_button.set_sensitive(False)
|
||||
|
||||
def add_mirror(self, button):
|
||||
self.new_mirror = True
|
||||
tooltip = "Select the pre-built mirror that will speed your build"
|
||||
index = len(self.sstatemirrors_list)
|
||||
self.selected_mirror_row = index
|
||||
sm_list = ["Standard", "", "file://(.*)"]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
self.refresh_shared_state_page()
|
||||
|
||||
def scroll_changed(self, widget, event, data=None):
|
||||
if self.new_mirror == True:
|
||||
adj = widget.get_vadjustment()
|
||||
adj.set_value(adj.upper - adj.page_size)
|
||||
self.new_mirror = False
|
||||
|
||||
def combo_changed(self, widget, path, text, model):
|
||||
model[path][0] = text
|
||||
selection = self.sstatemirrors_tv.get_selection()
|
||||
tree_model, tree_iter = selection.get_selected()
|
||||
index = int(tree_model.get_string_from_iter(tree_iter))
|
||||
self.sstatemirrors_list[index][0] = text
|
||||
|
||||
def regex_changed(self, cell, path, new_text, user_data):
|
||||
user_data[path][2] = new_text
|
||||
selection = self.sstatemirrors_tv.get_selection()
|
||||
tree_model, tree_iter = selection.get_selected()
|
||||
index = int(tree_model.get_string_from_iter(tree_iter))
|
||||
self.sstatemirrors_list[index][2] = new_text
|
||||
return
|
||||
|
||||
def url_changed(self, cell, path, new_text, user_data):
|
||||
if new_text!="Enter the mirror URL" and new_text!="Match regex and replace it with this URL":
|
||||
user_data[path][1] = new_text
|
||||
selection = self.sstatemirrors_tv.get_selection()
|
||||
tree_model, tree_iter = selection.get_selected()
|
||||
index = int(tree_model.get_string_from_iter(tree_iter))
|
||||
self.sstatemirrors_list[index][1] = new_text
|
||||
return
|
||||
|
||||
def configuration_field(self, column, cell, model, iter):
|
||||
cell.set_property('text', model.get_value(iter, 0))
|
||||
if model.get_value(iter, 0) == "Standard":
|
||||
self.cell1.set_property("sensitive", False)
|
||||
self.cell1.set_property("editable", False)
|
||||
else:
|
||||
self.cell1.set_property("sensitive", True)
|
||||
self.cell1.set_property("editable", True)
|
||||
return
|
||||
|
||||
def regex_field(self, column, cell, model, iter):
|
||||
cell.set_property('text', model.get_value(iter, 2))
|
||||
return
|
||||
|
||||
def url_field(self, column, cell, model, iter):
|
||||
text = model.get_value(iter, 1)
|
||||
if text == "":
|
||||
if model.get_value(iter, 0) == "Standard":
|
||||
text = "Enter the mirror URL"
|
||||
else:
|
||||
text = "Match regex and replace it with this URL"
|
||||
cell.set_property('text', text)
|
||||
return
|
||||
|
||||
def refresh_shared_state_page(self):
|
||||
page_num = self.nb.get_current_page()
|
||||
self.nb.remove_page(page_num);
|
||||
@@ -548,13 +373,13 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
|
||||
self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
|
||||
self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
|
||||
self.handler.set_socks_proxy(self.configuration.combine_proxy("socks"))
|
||||
self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git"))
|
||||
self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
|
||||
elif self.configuration.enable_proxy == False:
|
||||
self.handler.set_http_proxy("")
|
||||
self.handler.set_https_proxy("")
|
||||
self.handler.set_ftp_proxy("")
|
||||
self.handler.set_socks_proxy("")
|
||||
self.handler.set_git_proxy("", "")
|
||||
self.handler.set_cvs_proxy("", "")
|
||||
self.proxy_test_ran = True
|
||||
self.proxy_test_running = True
|
||||
@@ -620,7 +445,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("<span weight=\"bold\">Set the proxies used when fetching source code</span>")
|
||||
tooltip = "Set the proxies used when fetching source code. A blank field uses a direct internet connection."
|
||||
info = HobInfoButton("<span weight=\"bold\">Set the proxies used when fetching source code</span>" + "*" + tooltip, self)
|
||||
info = HobInfoButton(tooltip, self)
|
||||
hbox = gtk.HBox(False, 12)
|
||||
hbox.pack_start(label, expand=True, fill=True)
|
||||
hbox.pack_start(info, expand=False, fill=False)
|
||||
@@ -666,11 +491,11 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.same_proxy_addresses.append(self.ftp_proxy)
|
||||
self.same_proxy_ports.append(self.ftp_proxy_port)
|
||||
|
||||
self.socks_proxy, self.socks_proxy_port, self.socks_proxy_details = self.gen_proxy_entry_widget(
|
||||
"socks", self, True, 3)
|
||||
proxy_test_focus += [self.socks_proxy, self.socks_proxy_port]
|
||||
self.same_proxy_addresses.append(self.socks_proxy)
|
||||
self.same_proxy_ports.append(self.socks_proxy_port)
|
||||
self.git_proxy, self.git_proxy_port, self.git_proxy_details = self.gen_proxy_entry_widget(
|
||||
"git", self, True, 3)
|
||||
proxy_test_focus += [self.git_proxy, self.git_proxy_port]
|
||||
self.same_proxy_addresses.append(self.git_proxy)
|
||||
self.same_proxy_ports.append(self.git_proxy_port)
|
||||
|
||||
self.cvs_proxy, self.cvs_proxy_port, self.cvs_proxy_details = self.gen_proxy_entry_widget(
|
||||
"cvs", self, True, 4)
|
||||
@@ -868,7 +693,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
advanced_vbox.pack_start(sub_vbox, expand=True, fill=True)
|
||||
label = self.gen_label_widget("<span weight=\"bold\">Add your own variables:</span>")
|
||||
tooltip = "These are key/value pairs for your extra settings. Click \'Add\' and then directly edit the key and the value"
|
||||
setting_widget, self.setting_store = self.gen_editable_settings(self.configuration.extra_setting,"<b>Add your own variables</b>" + "*" + tooltip)
|
||||
setting_widget, self.setting_store = self.gen_editable_settings(self.configuration.extra_setting, tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(setting_widget, expand=True, fill=True)
|
||||
|
||||
@@ -891,3 +716,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.handler.disconnect(self.proxy_test_passed_id)
|
||||
self.handler.disconnect(self.proxy_test_failed_id)
|
||||
super(SimpleSettingsDialog, self).destroy()
|
||||
|
||||
def scroll_changed(self, widget, event, data=None):
|
||||
adj = widget.get_vadjustment()
|
||||
adj.set_value(adj.upper - adj.page_size)
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
import gobject
|
||||
import logging
|
||||
import ast
|
||||
from bb.ui.crumbs.runningbuild import RunningBuild
|
||||
|
||||
class HobHandler(gobject.GObject):
|
||||
@@ -147,9 +146,7 @@ class HobHandler(gobject.GObject):
|
||||
elif next_command == self.SUB_MATCH_CLASS:
|
||||
self.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"])
|
||||
elif next_command == self.SUB_PARSE_CONFIG:
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.runCommand(["parseConfigurationFiles", "conf/.hob.conf", ""])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
self.runCommand(["parseConfigurationFiles", "", ""])
|
||||
elif next_command == self.SUB_GNERATE_TGTS:
|
||||
self.runCommand(["generateTargetsTree", "classes/image.bbclass", []])
|
||||
elif next_command == self.SUB_GENERATE_PKGINFO:
|
||||
@@ -167,15 +164,17 @@ class HobHandler(gobject.GObject):
|
||||
self.clear_busy()
|
||||
self.building = True
|
||||
targets = [self.image]
|
||||
if self.package_queue:
|
||||
self.runCommand(["setVariable", "LINGUAS_INSTALL", ""])
|
||||
self.runCommand(["setVariable", "PACKAGE_INSTALL", " ".join(self.package_queue)])
|
||||
if self.toolchain_packages:
|
||||
self.set_var_in_file("TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages), "local.conf")
|
||||
self.runCommand(["setVariable", "TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages)])
|
||||
targets.append(self.toolchain)
|
||||
if targets[0] == "hob-image":
|
||||
self.set_var_in_file("LINGUAS_INSTALL", "", "local.conf")
|
||||
hobImage = self.runCommand(["matchFile", "hob-image.bb"])
|
||||
if self.base_image != "Start with an empty image recipe":
|
||||
if self.base_image != "Create your own image":
|
||||
baseImage = self.runCommand(["matchFile", self.base_image + ".bb"])
|
||||
version = self.runCommand(["generateNewImage", hobImage, baseImage, self.package_queue, True, ""])
|
||||
version = self.runCommand(["generateNewImage", hobImage, baseImage, self.package_queue])
|
||||
targets[0] += version
|
||||
self.recipe_model.set_custom_image_version(version)
|
||||
|
||||
@@ -201,10 +200,6 @@ class HobHandler(gobject.GObject):
|
||||
self.run_next_command()
|
||||
|
||||
elif isinstance(event, bb.event.SanityCheckPassed):
|
||||
reparse = self.runCommand(["getVariable", "BB_INVALIDCONF"]) or None
|
||||
if reparse is True:
|
||||
self.set_var_in_file("BB_INVALIDCONF", False, "local.conf")
|
||||
self.runCommand(["parseConfigurationFiles", "conf/.hob.conf", ""])
|
||||
self.run_next_command()
|
||||
|
||||
elif isinstance(event, bb.event.SanityCheckFailed):
|
||||
@@ -299,98 +294,83 @@ class HobHandler(gobject.GObject):
|
||||
|
||||
def init_cooker(self):
|
||||
self.runCommand(["initCooker"])
|
||||
self.runCommand(["createConfigFile", ".hob.conf"])
|
||||
|
||||
def reset_cooker(self):
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.runCommand(["resetCooker"])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
|
||||
def set_extra_inherit(self, bbclass):
|
||||
inherits = self.runCommand(["getVariable", "INHERIT"]) or ""
|
||||
inherits = inherits + " " + bbclass
|
||||
self.set_var_in_file("INHERIT", inherits, ".hob.conf")
|
||||
self.runCommand(["setVariable", "INHERIT", inherits])
|
||||
|
||||
def set_bblayers(self, bblayers):
|
||||
self.set_var_in_file("BBLAYERS", " ".join(bblayers), "bblayers.conf")
|
||||
self.runCommand(["setVariable", "BBLAYERS_HOB", " ".join(bblayers)])
|
||||
|
||||
def set_machine(self, machine):
|
||||
if machine:
|
||||
self.early_assign_var_in_file("MACHINE", machine, "local.conf")
|
||||
self.runCommand(["setVariable", "MACHINE_HOB", machine])
|
||||
|
||||
def set_sdk_machine(self, sdk_machine):
|
||||
self.set_var_in_file("SDKMACHINE", sdk_machine, "local.conf")
|
||||
self.runCommand(["setVariable", "SDKMACHINE_HOB", sdk_machine])
|
||||
|
||||
def set_image_fstypes(self, image_fstypes):
|
||||
self.set_var_in_file("IMAGE_FSTYPES", image_fstypes, "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_FSTYPES", image_fstypes])
|
||||
|
||||
def set_distro(self, distro):
|
||||
self.set_var_in_file("DISTRO", distro, "local.conf")
|
||||
self.runCommand(["setVariable", "DISTRO_HOB", distro])
|
||||
|
||||
def set_package_format(self, format):
|
||||
package_classes = ""
|
||||
for pkgfmt in format.split():
|
||||
package_classes += ("package_%s" % pkgfmt + " ")
|
||||
self.set_var_in_file("PACKAGE_CLASSES", package_classes, "local.conf")
|
||||
self.runCommand(["setVariable", "PACKAGE_CLASSES_HOB", package_classes])
|
||||
|
||||
def set_bbthreads(self, threads):
|
||||
self.set_var_in_file("BB_NUMBER_THREADS", threads, "local.conf")
|
||||
self.runCommand(["setVariable", "BB_NUMBER_THREADS_HOB", threads])
|
||||
|
||||
def set_pmake(self, threads):
|
||||
pmake = "-j %s" % threads
|
||||
self.set_var_in_file("PARALLEL_MAKE", pmake, "local.conf")
|
||||
self.runCommand(["setVariable", "PARALLEL_MAKE_HOB", pmake])
|
||||
|
||||
def set_dl_dir(self, directory):
|
||||
self.set_var_in_file("DL_DIR", directory, "local.conf")
|
||||
self.runCommand(["setVariable", "DL_DIR_HOB", directory])
|
||||
|
||||
def set_sstate_dir(self, directory):
|
||||
self.set_var_in_file("SSTATE_DIR", directory, "local.conf")
|
||||
self.runCommand(["setVariable", "SSTATE_DIR_HOB", directory])
|
||||
|
||||
def set_sstate_mirrors(self, url):
|
||||
self.set_var_in_file("SSTATE_MIRRORS", url, "local.conf")
|
||||
self.runCommand(["setVariable", "SSTATE_MIRRORS_HOB", url])
|
||||
|
||||
def set_extra_size(self, image_extra_size):
|
||||
self.set_var_in_file("IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size), "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size)])
|
||||
|
||||
def set_rootfs_size(self, image_rootfs_size):
|
||||
self.set_var_in_file("IMAGE_ROOTFS_SIZE", str(image_rootfs_size), "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_ROOTFS_SIZE", str(image_rootfs_size)])
|
||||
|
||||
def set_incompatible_license(self, incompat_license):
|
||||
self.set_var_in_file("INCOMPATIBLE_LICENSE", incompat_license, "local.conf")
|
||||
|
||||
def set_extra_setting(self, extra_setting):
|
||||
self.set_var_in_file("EXTRA_SETTING", extra_setting, "local.conf")
|
||||
self.runCommand(["setVariable", "INCOMPATIBLE_LICENSE_HOB", incompat_license])
|
||||
|
||||
def set_extra_config(self, extra_setting):
|
||||
old_extra_setting = ast.literal_eval(self.runCommand(["getVariable", "EXTRA_SETTING"]) or "{}")
|
||||
if extra_setting:
|
||||
self.set_var_in_file("EXTRA_SETTING", extra_setting, "local.conf")
|
||||
else:
|
||||
self.remove_var_from_file("EXTRA_SETTING")
|
||||
|
||||
#remove not needed settings from conf
|
||||
for key in old_extra_setting:
|
||||
if key not in extra_setting:
|
||||
self.remove_var_from_file(key)
|
||||
for key in extra_setting.keys():
|
||||
value = extra_setting[key]
|
||||
self.set_var_in_file(key, value, "local.conf")
|
||||
self.runCommand(["setVariable", key, value])
|
||||
|
||||
def set_config_filter(self, config_filter):
|
||||
self.runCommand(["setConfFilter", config_filter])
|
||||
|
||||
def set_http_proxy(self, http_proxy):
|
||||
self.set_var_in_file("http_proxy", http_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "http_proxy", http_proxy])
|
||||
|
||||
def set_https_proxy(self, https_proxy):
|
||||
self.set_var_in_file("https_proxy", https_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "https_proxy", https_proxy])
|
||||
|
||||
def set_ftp_proxy(self, ftp_proxy):
|
||||
self.set_var_in_file("ftp_proxy", ftp_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "ftp_proxy", ftp_proxy])
|
||||
|
||||
def set_socks_proxy(self, socks_proxy):
|
||||
self.set_var_in_file("all_proxy", socks_proxy, "local.conf")
|
||||
def set_git_proxy(self, host, port):
|
||||
self.runCommand(["setVariable", "GIT_PROXY_HOST", host])
|
||||
self.runCommand(["setVariable", "GIT_PROXY_PORT", port])
|
||||
|
||||
def set_cvs_proxy(self, host, port):
|
||||
self.set_var_in_file("CVS_PROXY_HOST", host, "local.conf")
|
||||
self.set_var_in_file("CVS_PROXY_PORT", port, "local.conf")
|
||||
self.runCommand(["setVariable", "CVS_PROXY_HOST", host])
|
||||
self.runCommand(["setVariable", "CVS_PROXY_PORT", port])
|
||||
|
||||
def request_package_info(self):
|
||||
self.commands_async.append(self.SUB_GENERATE_PKGINFO)
|
||||
@@ -438,13 +418,6 @@ class HobHandler(gobject.GObject):
|
||||
self.commands_async.append(self.SUB_BUILD_IMAGE)
|
||||
self.run_next_command(self.GENERATE_IMAGE)
|
||||
|
||||
def generate_new_image(self, image, base_image, package_queue, description):
|
||||
base_image = self.runCommand(["matchFile", self.base_image + ".bb"])
|
||||
self.runCommand(["generateNewImage", image, base_image, package_queue, False, description])
|
||||
|
||||
def ensure_dir(self, directory):
|
||||
self.runCommand(["ensureDir", directory])
|
||||
|
||||
def build_succeeded_async(self):
|
||||
self.building = False
|
||||
|
||||
@@ -454,12 +427,12 @@ class HobHandler(gobject.GObject):
|
||||
self.building = False
|
||||
|
||||
def cancel_parse(self):
|
||||
self.runCommand(["stateForceShutdown"])
|
||||
self.runCommand(["stateStop"])
|
||||
|
||||
def cancel_build(self, force=False):
|
||||
if force:
|
||||
# Force the cooker to stop as quickly as possible
|
||||
self.runCommand(["stateForceShutdown"])
|
||||
self.runCommand(["stateStop"])
|
||||
else:
|
||||
# Wait for tasks to complete before shutting down, this helps
|
||||
# leave the workdir in a usable state
|
||||
@@ -471,9 +444,6 @@ class HobHandler(gobject.GObject):
|
||||
def get_logfile(self):
|
||||
return self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
|
||||
|
||||
def get_topdir(self):
|
||||
return self.runCommand(["getVariable", "TOPDIR"]) or ""
|
||||
|
||||
def _remove_redundant(self, string):
|
||||
ret = []
|
||||
for i in string.split():
|
||||
@@ -481,28 +451,6 @@ class HobHandler(gobject.GObject):
|
||||
ret.append(i)
|
||||
return " ".join(ret)
|
||||
|
||||
def set_var_in_file(self, var, val, default_file=None):
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.server.runCommand(["setVarFile", var, val, default_file, "set"])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
|
||||
def early_assign_var_in_file(self, var, val, default_file=None):
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.server.runCommand(["setVarFile", var, val, default_file, "earlyAssign"])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
|
||||
def remove_var_from_file(self, var):
|
||||
self.server.runCommand(["removeVarFile", var])
|
||||
|
||||
def append_var_in_file(self, var, val, default_file=None):
|
||||
self.server.runCommand(["setVarFile", var, val, default_file, "append"])
|
||||
|
||||
def append_to_bbfiles(self, val):
|
||||
bbfiles = self.runCommand(["getVariable", "BBFILES", "False"]) or ""
|
||||
bbfiles = bbfiles.split()
|
||||
if val not in bbfiles:
|
||||
self.append_var_in_file("BBFILES", val, "local.conf")
|
||||
|
||||
def get_parameters(self):
|
||||
# retrieve the parameters from bitbake
|
||||
params = {}
|
||||
@@ -611,7 +559,9 @@ class HobHandler(gobject.GObject):
|
||||
|
||||
params["default_task"] = self.runCommand(["getVariable", "BB_DEFAULT_TASK"]) or "build"
|
||||
|
||||
params["socks_proxy"] = self.runCommand(["getVariable", "all_proxy"]) or ""
|
||||
params["git_proxy_host"] = self.runCommand(["getVariable", "GIT_PROXY_HOST"]) or ""
|
||||
params["git_proxy_port"] = self.runCommand(["getVariable", "GIT_PROXY_PORT"]) or ""
|
||||
|
||||
params["http_proxy"] = self.runCommand(["getVariable", "http_proxy"]) or ""
|
||||
params["ftp_proxy"] = self.runCommand(["getVariable", "ftp_proxy"]) or ""
|
||||
params["https_proxy"] = self.runCommand(["getVariable", "https_proxy"]) or ""
|
||||
|
||||
@@ -27,15 +27,14 @@ from bb.ui.crumbs.hobpages import HobPage
|
||||
#
|
||||
# PackageListModel
|
||||
#
|
||||
class PackageListModel(gtk.ListStore):
|
||||
class PackageListModel(gtk.TreeStore):
|
||||
"""
|
||||
This class defines an gtk.ListStore subclass which will convert the output
|
||||
of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also
|
||||
This class defines an gtk.TreeStore subclass which will convert the output
|
||||
of the bb.event.TargetsTreeGenerated event into a gtk.TreeStore whilst also
|
||||
providing convenience functions to access gtk.TreeModel subclasses which
|
||||
provide filtered views of the data.
|
||||
"""
|
||||
|
||||
(COL_NAME, COL_VER, COL_REV, COL_RNM, COL_SEC, COL_SUM, COL_RDEP, COL_RPROV, COL_SIZE, COL_RCP, COL_BINB, COL_INC, COL_FADE_INC, COL_FONT, COL_FLIST) = range(15)
|
||||
(COL_NAME, COL_VER, COL_REV, COL_RNM, COL_SEC, COL_SUM, COL_RDEP, COL_RPROV, COL_SIZE, COL_BINB, COL_INC, COL_FADE_INC, COL_FONT) = range(13)
|
||||
|
||||
__gsignals__ = {
|
||||
"package-selection-changed" : (gobject.SIGNAL_RUN_LAST,
|
||||
@@ -46,9 +45,15 @@ class PackageListModel(gtk.ListStore):
|
||||
__toolchain_required_packages__ = ["packagegroup-core-standalone-sdk-target", "packagegroup-core-standalone-sdk-target-dbg"]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.contents = None
|
||||
self.images = None
|
||||
self.pkgs_size = 0
|
||||
self.pn_path = {}
|
||||
self.pkg_path = {}
|
||||
self.rprov_pkg = {}
|
||||
gtk.ListStore.__init__ (self,
|
||||
gobject.TYPE_STRING,
|
||||
|
||||
gtk.TreeStore.__init__ (self,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
@@ -61,9 +66,8 @@ class PackageListModel(gtk.ListStore):
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.sort_column_id, self.sort_order = PackageListModel.COL_NAME, gtk.SORT_ASCENDING
|
||||
|
||||
|
||||
"""
|
||||
Find the model path for the item_name
|
||||
@@ -71,14 +75,14 @@ class PackageListModel(gtk.ListStore):
|
||||
"""
|
||||
def find_path_for_item(self, item_name):
|
||||
pkg = item_name
|
||||
if item_name not in self.pn_path.keys():
|
||||
if item_name not in self.pkg_path.keys():
|
||||
if item_name not in self.rprov_pkg.keys():
|
||||
return None
|
||||
pkg = self.rprov_pkg[item_name]
|
||||
if pkg not in self.pn_path.keys():
|
||||
if pkg not in self.pkg_path.keys():
|
||||
return None
|
||||
|
||||
return self.pn_path[pkg]
|
||||
return self.pkg_path[pkg]
|
||||
|
||||
def find_item_for_path(self, item_path):
|
||||
return self[item_path][self.COL_NAME]
|
||||
@@ -87,127 +91,25 @@ class PackageListModel(gtk.ListStore):
|
||||
Helper function to determine whether an item is an item specified by filter
|
||||
"""
|
||||
def tree_model_filter(self, model, it, filter):
|
||||
name = model.get_value(it, self.COL_NAME)
|
||||
|
||||
for key in filter.keys():
|
||||
if key == self.COL_NAME:
|
||||
if filter[key] != 'Search packages by name':
|
||||
if name and filter[key] not in name:
|
||||
return False
|
||||
else:
|
||||
if model.get_value(it, key) not in filter[key]:
|
||||
return False
|
||||
self.filtered_nb += 1
|
||||
if model.get_value(it, key) not in filter[key]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
"""
|
||||
Create, if required, and return a filtered gtk.TreeModelSort
|
||||
containing only the items specified by filter
|
||||
"""
|
||||
def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False):
|
||||
def tree_model(self, filter):
|
||||
model = self.filter_new()
|
||||
self.filtered_nb = 0
|
||||
model.set_visible_func(self.tree_model_filter, filter)
|
||||
|
||||
sort = gtk.TreeModelSort(model)
|
||||
sort.connect ('sort-column-changed', self.sort_column_changed_cb)
|
||||
if initial:
|
||||
sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
elif excluded_items_ahead:
|
||||
sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
|
||||
elif included_items_ahead:
|
||||
sort.set_default_sort_func(self.include_item_sort_func, search_data)
|
||||
else:
|
||||
if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
|
||||
sort.set_default_sort_func(self.sort_func, search_data)
|
||||
else:
|
||||
sort.set_sort_column_id(self.sort_column_id, self.sort_order)
|
||||
sort.set_default_sort_func(None)
|
||||
|
||||
sort.set_sort_func(PackageListModel.COL_INC, self.sort_column, PackageListModel.COL_INC)
|
||||
sort.set_sort_func(PackageListModel.COL_SIZE, self.sort_column, PackageListModel.COL_SIZE)
|
||||
sort.set_sort_func(PackageListModel.COL_BINB, self.sort_binb_column)
|
||||
sort.set_sort_func(PackageListModel.COL_RCP, self.sort_column, PackageListModel.COL_RCP)
|
||||
sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
return sort
|
||||
|
||||
def sort_column_changed_cb (self, data):
|
||||
self.sort_column_id, self.sort_order = data.get_sort_column_id ()
|
||||
|
||||
def sort_column(self, model, row1, row2, col):
|
||||
value1 = model.get_value(row1, col)
|
||||
value2 = model.get_value(row2, col)
|
||||
if col==PackageListModel.COL_SIZE:
|
||||
value1 = HobPage._string_to_size(value1)
|
||||
value2 = HobPage._string_to_size(value2)
|
||||
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res!=0:
|
||||
if col==PackageListModel.COL_INC:
|
||||
return -cmp_res
|
||||
else:
|
||||
return cmp_res
|
||||
else:
|
||||
name1 = model.get_value(row1, PackageListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, PackageListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
|
||||
def sort_binb_column(self, model, row1, row2):
|
||||
value1 = model.get_value(row1, PackageListModel.COL_BINB)
|
||||
value2 = model.get_value(row2, PackageListModel.COL_BINB)
|
||||
value1_list = value1.split(', ')
|
||||
value2_list = value2.split(', ')
|
||||
|
||||
value1 = value1_list[0]
|
||||
value2 = value2_list[0]
|
||||
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res==0:
|
||||
cmp_size = cmp(len(value1_list), len(value2_list))
|
||||
if cmp_size==0:
|
||||
name1 = model.get_value(row1, PackageListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, PackageListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
else:
|
||||
return cmp_size
|
||||
else:
|
||||
return cmp_res
|
||||
|
||||
def exclude_item_sort_func(self, model, iter1, iter2, user_data=None):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
else:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_FADE_INC)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_INC)
|
||||
return ((val1 == True) and (val2 == False))
|
||||
|
||||
def include_item_sort_func(self, model, iter1, iter2, user_data=None):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
else:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_INC)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_INC)
|
||||
return ((val1 == False) and (val2 == True))
|
||||
|
||||
def sort_func(self, model, iter1, iter2, user_data):
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
|
||||
def cmp_vals(self, val1, val2, user_data):
|
||||
if val1 is None or val2 is None:
|
||||
return 0
|
||||
elif val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return cmp(val1, val2)
|
||||
|
||||
def convert_vpath_to_path(self, view_model, view_path):
|
||||
# view_model is the model sorted
|
||||
# get the path of the model filtered
|
||||
@@ -219,13 +121,16 @@ class PackageListModel(gtk.ListStore):
|
||||
return path
|
||||
|
||||
def convert_path_to_vpath(self, view_model, path):
|
||||
name = self.find_item_for_path(path)
|
||||
it = view_model.get_iter_first()
|
||||
while it:
|
||||
name = self.find_item_for_path(path)
|
||||
view_name = view_model.get_value(it, PackageListModel.COL_NAME)
|
||||
if view_name == name:
|
||||
view_path = view_model.get_path(it)
|
||||
return view_path
|
||||
child_it = view_model.iter_children(it)
|
||||
while child_it:
|
||||
view_name = view_model.get_value(child_it, self.COL_NAME)
|
||||
if view_name == name:
|
||||
view_path = view_model.get_path(child_it)
|
||||
return view_path
|
||||
child_it = view_model.iter_next(child_it)
|
||||
it = view_model.iter_next(it)
|
||||
return None
|
||||
|
||||
@@ -234,8 +139,11 @@ class PackageListModel(gtk.ListStore):
|
||||
bb.event.PackageInfo event and populates the package list.
|
||||
"""
|
||||
def populate(self, pkginfolist):
|
||||
# First clear the model, in case repopulating
|
||||
self.clear()
|
||||
self.pkgs_size = 0
|
||||
self.pn_path = {}
|
||||
self.pkg_path = {}
|
||||
self.rprov_pkg = {}
|
||||
|
||||
def getpkgvalue(pkgdict, key, pkgname, defaultval = None):
|
||||
value = pkgdict.get('%s_%s' % (key, pkgname), None)
|
||||
@@ -247,6 +155,15 @@ class PackageListModel(gtk.ListStore):
|
||||
pn = pkginfo['PN']
|
||||
pv = pkginfo['PV']
|
||||
pr = pkginfo['PR']
|
||||
if pn in self.pn_path.keys():
|
||||
pniter = self.get_iter(self.pn_path[pn])
|
||||
else:
|
||||
pniter = self.append(None)
|
||||
self.set(pniter, self.COL_NAME, pn + '-' + pv + '-' + pr,
|
||||
self.COL_INC, False)
|
||||
self.pn_path[pn] = self.get_path(pniter)
|
||||
|
||||
# PKG is always present
|
||||
pkg = pkginfo['PKG']
|
||||
pkgv = getpkgvalue(pkginfo, 'PKGV', pkg)
|
||||
pkgr = getpkgvalue(pkginfo, 'PKGR', pkg)
|
||||
@@ -260,12 +177,9 @@ class PackageListModel(gtk.ListStore):
|
||||
rdep = getpkgvalue(pkginfo, 'RDEPENDS', pkg, "")
|
||||
rrec = getpkgvalue(pkginfo, 'RRECOMMENDS', pkg, "")
|
||||
rprov = getpkgvalue(pkginfo, 'RPROVIDES', pkg, "")
|
||||
files_list = getpkgvalue(pkginfo, 'FILES_INFO', pkg, "")
|
||||
for i in rprov.split():
|
||||
self.rprov_pkg[i] = pkg
|
||||
|
||||
recipe = pn + '-' + pv + '-' + pr
|
||||
|
||||
allow_empty = getpkgvalue(pkginfo, 'ALLOW_EMPTY', pkg, "")
|
||||
|
||||
if pkgsize == "0" and not allow_empty:
|
||||
@@ -273,27 +187,15 @@ class PackageListModel(gtk.ListStore):
|
||||
|
||||
# pkgsize is in KB
|
||||
size = HobPage._size_to_string(HobPage._string_to_size(pkgsize + ' KB'))
|
||||
self.set(self.append(), self.COL_NAME, pkg, self.COL_VER, pkgv,
|
||||
|
||||
it = self.append(pniter)
|
||||
self.pkg_path[pkg] = self.get_path(it)
|
||||
self.set(it, self.COL_NAME, pkg, self.COL_VER, pkgv,
|
||||
self.COL_REV, pkgr, self.COL_RNM, pkg_rename,
|
||||
self.COL_SEC, section, self.COL_SUM, summary,
|
||||
self.COL_RDEP, rdep + ' ' + rrec,
|
||||
self.COL_RPROV, rprov, self.COL_SIZE, size,
|
||||
self.COL_RCP, recipe, self.COL_BINB, "",
|
||||
self.COL_INC, False, self.COL_FONT, '10', self.COL_FLIST, files_list)
|
||||
|
||||
self.pn_path = {}
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
pn = self.get_value(it, self.COL_NAME)
|
||||
path = self.get_path(it)
|
||||
self.pn_path[pn] = path
|
||||
it = self.iter_next(it)
|
||||
|
||||
"""
|
||||
Update the model, send out the notification.
|
||||
"""
|
||||
def selection_change_notification(self):
|
||||
self.emit("package-selection-changed")
|
||||
self.COL_BINB, "", self.COL_INC, False, self.COL_FONT, '10')
|
||||
|
||||
"""
|
||||
Check whether the item at item_path is included or not
|
||||
@@ -302,26 +204,54 @@ class PackageListModel(gtk.ListStore):
|
||||
return self[item_path][self.COL_INC]
|
||||
|
||||
"""
|
||||
Add this item, and any of its dependencies, to the image contents
|
||||
Update the model, send out the notification.
|
||||
"""
|
||||
def selection_change_notification(self):
|
||||
self.emit("package-selection-changed")
|
||||
|
||||
"""
|
||||
Mark a certain package as selected.
|
||||
All its dependencies are marked as selected.
|
||||
The recipe provides the package is marked as selected.
|
||||
If user explicitly selects a recipe, all its providing packages are selected
|
||||
"""
|
||||
def include_item(self, item_path, binb=""):
|
||||
if self.path_included(item_path):
|
||||
return
|
||||
|
||||
item_name = self[item_path][self.COL_NAME]
|
||||
item_deps = self[item_path][self.COL_RDEP]
|
||||
item_rdep = self[item_path][self.COL_RDEP]
|
||||
|
||||
self[item_path][self.COL_INC] = True
|
||||
|
||||
it = self.get_iter(item_path)
|
||||
|
||||
# If user explicitly selects a recipe, all its providing packages are selected.
|
||||
if not self[item_path][self.COL_VER] and binb == "User Selected":
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
child_path = self.get_path(child_it)
|
||||
child_included = self.path_included(child_path)
|
||||
if not child_included:
|
||||
self.include_item(child_path, binb="User Selected")
|
||||
child_it = self.iter_next(child_it)
|
||||
return
|
||||
|
||||
# The recipe provides the package is also marked as selected
|
||||
parent_it = self.iter_parent(it)
|
||||
if parent_it:
|
||||
parent_path = self.get_path(parent_it)
|
||||
self[parent_path][self.COL_INC] = True
|
||||
|
||||
item_bin = self[item_path][self.COL_BINB].split(', ')
|
||||
if binb and not binb in item_bin:
|
||||
item_bin.append(binb)
|
||||
self[item_path][self.COL_BINB] = ', '.join(item_bin).lstrip(', ')
|
||||
|
||||
if item_deps:
|
||||
if item_rdep:
|
||||
# Ensure all of the items deps are included and, where appropriate,
|
||||
# add this item to their COL_BINB
|
||||
for dep in item_deps.split(" "):
|
||||
for dep in item_rdep.split(" "):
|
||||
if dep.startswith('('):
|
||||
continue
|
||||
# If the contents model doesn't already contain dep, add it
|
||||
@@ -340,6 +270,12 @@ class PackageListModel(gtk.ListStore):
|
||||
elif not dep_included:
|
||||
self.include_item(dep_path, binb=item_name)
|
||||
|
||||
"""
|
||||
Mark a certain package as de-selected.
|
||||
All other packages that depends on this package are marked as de-selected.
|
||||
If none of the packages provided by the recipe, the recipe should be marked as de-selected.
|
||||
If user explicitly de-select a recipe, all its providing packages are de-selected.
|
||||
"""
|
||||
def exclude_item(self, item_path):
|
||||
if not self.path_included(item_path):
|
||||
return
|
||||
@@ -347,9 +283,37 @@ class PackageListModel(gtk.ListStore):
|
||||
self[item_path][self.COL_INC] = False
|
||||
|
||||
item_name = self[item_path][self.COL_NAME]
|
||||
item_deps = self[item_path][self.COL_RDEP]
|
||||
if item_deps:
|
||||
for dep in item_deps.split(" "):
|
||||
item_rdep = self[item_path][self.COL_RDEP]
|
||||
it = self.get_iter(item_path)
|
||||
|
||||
# If user explicitly de-select a recipe, all its providing packages are de-selected.
|
||||
if not self[item_path][self.COL_VER]:
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
child_path = self.get_path(child_it)
|
||||
child_included = self[child_path][self.COL_INC]
|
||||
if child_included:
|
||||
self.exclude_item(child_path)
|
||||
child_it = self.iter_next(child_it)
|
||||
return
|
||||
|
||||
# If none of the packages provided by the recipe, the recipe should be marked as de-selected.
|
||||
parent_it = self.iter_parent(it)
|
||||
peer_iter = self.iter_children(parent_it)
|
||||
enabled = 0
|
||||
while peer_iter:
|
||||
peer_path = self.get_path(peer_iter)
|
||||
if self[peer_path][self.COL_INC]:
|
||||
enabled = 1
|
||||
break
|
||||
peer_iter = self.iter_next(peer_iter)
|
||||
if not enabled:
|
||||
parent_path = self.get_path(parent_it)
|
||||
self[parent_path][self.COL_INC] = False
|
||||
|
||||
# All packages that depends on this package are de-selected.
|
||||
if item_rdep:
|
||||
for dep in item_rdep.split(" "):
|
||||
if dep.startswith('('):
|
||||
continue
|
||||
dep_path = self.find_path_for_item(dep)
|
||||
@@ -369,40 +333,51 @@ class PackageListModel(gtk.ListStore):
|
||||
self.exclude_item(binb_path)
|
||||
|
||||
"""
|
||||
Empty self.contents by setting the include of each entry to None
|
||||
Package model may be incomplete, therefore when calling the
|
||||
set_selected_packages(), some packages will not be set included.
|
||||
Return the un-set packages list.
|
||||
"""
|
||||
def reset(self):
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
self.set(it,
|
||||
self.COL_INC, False,
|
||||
self.COL_BINB, "")
|
||||
it = self.iter_next(it)
|
||||
def set_selected_packages(self, packagelist, user_selected=False):
|
||||
left = []
|
||||
binb = 'User Selected' if user_selected else ''
|
||||
for pn in packagelist:
|
||||
if pn in self.pkg_path.keys():
|
||||
path = self.pkg_path[pn]
|
||||
self.include_item(item_path=path, binb=binb)
|
||||
else:
|
||||
left.append(pn)
|
||||
|
||||
self.selection_change_notification()
|
||||
|
||||
def get_selected_packages(self):
|
||||
packagelist = []
|
||||
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
if self.get_value(it, self.COL_INC):
|
||||
name = self.get_value(it, self.COL_NAME)
|
||||
packagelist.append(name)
|
||||
it = self.iter_next(it)
|
||||
|
||||
return packagelist
|
||||
return left
|
||||
|
||||
def get_user_selected_packages(self):
|
||||
packagelist = []
|
||||
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
if self.get_value(it, self.COL_INC):
|
||||
binb = self.get_value(it, self.COL_BINB)
|
||||
if binb == "User Selected":
|
||||
name = self.get_value(it, self.COL_NAME)
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
if self.get_value(child_it, self.COL_INC):
|
||||
binb = self.get_value(child_it, self.COL_BINB)
|
||||
if binb == "User Selected":
|
||||
name = self.get_value(child_it, self.COL_NAME)
|
||||
packagelist.append(name)
|
||||
child_it = self.iter_next(child_it)
|
||||
it = self.iter_next(it)
|
||||
|
||||
return packagelist
|
||||
|
||||
def get_selected_packages(self):
|
||||
packagelist = []
|
||||
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
if self.get_value(child_it, self.COL_INC):
|
||||
name = self.get_value(child_it, self.COL_NAME)
|
||||
packagelist.append(name)
|
||||
child_it = self.iter_next(child_it)
|
||||
it = self.iter_next(it)
|
||||
|
||||
return packagelist
|
||||
@@ -413,31 +388,16 @@ class PackageListModel(gtk.ListStore):
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
if self.get_value(it, self.COL_INC):
|
||||
name = self.get_value(it, self.COL_NAME)
|
||||
if name.endswith("-dev") or name.endswith("-dbg"):
|
||||
packagelist.append(name)
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
name = self.get_value(child_it, self.COL_NAME)
|
||||
inc = self.get_value(child_it, self.COL_INC)
|
||||
if inc or name.endswith("-dev") or name.endswith("-dbg"):
|
||||
packagelist.append(name)
|
||||
child_it = self.iter_next(child_it)
|
||||
it = self.iter_next(it)
|
||||
|
||||
return list(set(packagelist + self.__toolchain_required_packages__));
|
||||
|
||||
"""
|
||||
Package model may be incomplete, therefore when calling the
|
||||
set_selected_packages(), some packages will not be set included.
|
||||
Return the un-set packages list.
|
||||
"""
|
||||
def set_selected_packages(self, packagelist, user_selected=False):
|
||||
left = []
|
||||
binb = 'User Selected' if user_selected else ''
|
||||
for pn in packagelist:
|
||||
if pn in self.pn_path.keys():
|
||||
path = self.pn_path[pn]
|
||||
self.include_item(item_path=path, binb=binb)
|
||||
else:
|
||||
left.append(pn)
|
||||
|
||||
self.selection_change_notification()
|
||||
return left
|
||||
|
||||
"""
|
||||
Return the selected package size, unit is B.
|
||||
"""
|
||||
@@ -445,16 +405,37 @@ class PackageListModel(gtk.ListStore):
|
||||
packages_size = 0
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
if self.get_value(it, self.COL_INC):
|
||||
str_size = self.get_value(it, self.COL_SIZE)
|
||||
if not str_size:
|
||||
continue
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
if self.get_value(child_it, self.COL_INC):
|
||||
str_size = self.get_value(child_it, self.COL_SIZE)
|
||||
if not str_size:
|
||||
continue
|
||||
|
||||
packages_size += HobPage._string_to_size(str_size)
|
||||
packages_size += HobPage._string_to_size(str_size)
|
||||
|
||||
child_it = self.iter_next(child_it)
|
||||
it = self.iter_next(it)
|
||||
return packages_size
|
||||
|
||||
"""
|
||||
Empty self.contents by setting the include of each entry to None
|
||||
"""
|
||||
def reset(self):
|
||||
self.pkgs_size = 0
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
self.set(it, self.COL_INC, False)
|
||||
child_it = self.iter_children(it)
|
||||
while child_it:
|
||||
self.set(child_it,
|
||||
self.COL_INC, False,
|
||||
self.COL_BINB, "")
|
||||
child_it = self.iter_next(child_it)
|
||||
it = self.iter_next(it)
|
||||
|
||||
self.selection_change_notification()
|
||||
|
||||
"""
|
||||
Resync the state of included items to a backup column before performing the fadeout visible effect
|
||||
"""
|
||||
@@ -463,6 +444,9 @@ class PackageListModel(gtk.ListStore):
|
||||
while it:
|
||||
active = self.get_value(it, self.COL_INC)
|
||||
self.set(it, self.COL_FADE_INC, active)
|
||||
if self.iter_has_child(it):
|
||||
self.resync_fadeout_column(self.iter_children(it))
|
||||
|
||||
it = self.iter_next(it)
|
||||
|
||||
#
|
||||
@@ -475,10 +459,9 @@ class RecipeListModel(gtk.ListStore):
|
||||
providing convenience functions to access gtk.TreeModel subclasses which
|
||||
provide filtered views of the data.
|
||||
"""
|
||||
(COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_INSTALL, COL_PN, COL_FADE_INC, COL_SUMMARY, COL_VERSION,
|
||||
COL_REVISION, COL_HOMEPAGE, COL_BUGTRACKER, COL_FILE) = range(18)
|
||||
(COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_INSTALL, COL_PN, COL_FADE_INC) = range(12)
|
||||
|
||||
__custom_image__ = "Start with an empty image recipe"
|
||||
__custom_image__ = "Create your own image"
|
||||
|
||||
__gsignals__ = {
|
||||
"recipe-selection-changed" : (gobject.SIGNAL_RUN_LAST,
|
||||
@@ -501,14 +484,7 @@ class RecipeListModel(gtk.ListStore):
|
||||
gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.sort_column_id, self.sort_order = RecipeListModel.COL_NAME, gtk.SORT_ASCENDING
|
||||
gobject.TYPE_BOOLEAN)
|
||||
|
||||
"""
|
||||
Find the model path for the item_name
|
||||
@@ -540,121 +516,39 @@ class RecipeListModel(gtk.ListStore):
|
||||
return False
|
||||
|
||||
for key in filter.keys():
|
||||
if key == self.COL_NAME:
|
||||
if filter[key] != 'Search recipes by name' and filter[key] != 'Search package groups by name':
|
||||
if filter[key] not in name:
|
||||
return False
|
||||
else:
|
||||
if model.get_value(it, key) not in filter[key]:
|
||||
return False
|
||||
self.filtered_nb += 1
|
||||
if model.get_value(it, key) not in filter[key]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def exclude_item_sort_func(self, model, iter1, iter2, user_data=None):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
else:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_FADE_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
return ((val1 == True) and (val2 == False))
|
||||
def exclude_item_sort_func(self, model, iter1, iter2):
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_FADE_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
return ((val1 == True) and (val2 == False))
|
||||
|
||||
def include_item_sort_func(self, model, iter1, iter2, user_data=None):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
else:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
return ((val1 == False) and (val2 == True))
|
||||
|
||||
def sort_func(self, model, iter1, iter2, user_data):
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
|
||||
def cmp_vals(self, val1, val2, user_data):
|
||||
if val1 is None or val2 is None:
|
||||
return 0
|
||||
elif val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return cmp(val1, val2)
|
||||
def include_item_sort_func(self, model, iter1, iter2):
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
return ((val1 == False) and (val2 == True))
|
||||
|
||||
"""
|
||||
Create, if required, and return a filtered gtk.TreeModelSort
|
||||
containing only the items specified by filter
|
||||
containing only the items which are items specified by filter
|
||||
"""
|
||||
def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False):
|
||||
def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=True):
|
||||
model = self.filter_new()
|
||||
self.filtered_nb = 0
|
||||
model.set_visible_func(self.tree_model_filter, filter)
|
||||
|
||||
sort = gtk.TreeModelSort(model)
|
||||
sort.connect ('sort-column-changed', self.sort_column_changed_cb)
|
||||
if initial:
|
||||
if excluded_items_ahead:
|
||||
sort.set_default_sort_func(self.exclude_item_sort_func)
|
||||
elif included_items_ahead:
|
||||
sort.set_default_sort_func(self.include_item_sort_func)
|
||||
else:
|
||||
sort.set_sort_column_id(RecipeListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
elif excluded_items_ahead:
|
||||
sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
|
||||
elif included_items_ahead:
|
||||
sort.set_default_sort_func(self.include_item_sort_func, search_data)
|
||||
else:
|
||||
if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
|
||||
sort.set_default_sort_func(self.sort_func, search_data)
|
||||
else:
|
||||
sort.set_sort_column_id(self.sort_column_id, self.sort_order)
|
||||
sort.set_default_sort_func(None)
|
||||
|
||||
sort.set_sort_func(RecipeListModel.COL_INC, self.sort_column, RecipeListModel.COL_INC)
|
||||
sort.set_sort_func(RecipeListModel.COL_GROUP, self.sort_column, RecipeListModel.COL_GROUP)
|
||||
sort.set_sort_func(RecipeListModel.COL_BINB, self.sort_binb_column)
|
||||
sort.set_sort_func(RecipeListModel.COL_LIC, self.sort_column, RecipeListModel.COL_LIC)
|
||||
return sort
|
||||
|
||||
def sort_column_changed_cb (self, data):
|
||||
self.sort_column_id, self.sort_order = data.get_sort_column_id ()
|
||||
|
||||
def sort_column(self, model, row1, row2, col):
|
||||
value1 = model.get_value(row1, col)
|
||||
value2 = model.get_value(row2, col)
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res!=0:
|
||||
if col==RecipeListModel.COL_INC:
|
||||
return -cmp_res
|
||||
else:
|
||||
return cmp_res
|
||||
else:
|
||||
name1 = model.get_value(row1, RecipeListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, RecipeListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
|
||||
def sort_binb_column(self, model, row1, row2):
|
||||
value1 = model.get_value(row1, RecipeListModel.COL_BINB)
|
||||
value2 = model.get_value(row2, RecipeListModel.COL_BINB)
|
||||
value1_list = value1.split(', ')
|
||||
value2_list = value2.split(', ')
|
||||
|
||||
value1 = value1_list[0]
|
||||
value2 = value2_list[0]
|
||||
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res==0:
|
||||
cmp_size = cmp(len(value1_list), len(value2_list))
|
||||
if cmp_size==0:
|
||||
name1 = model.get_value(row1, RecipeListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, RecipeListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
else:
|
||||
return cmp_size
|
||||
else:
|
||||
return cmp_res
|
||||
|
||||
def convert_vpath_to_path(self, view_model, view_path):
|
||||
filtered_model_path = view_model.convert_path_to_child_path(view_path)
|
||||
filtered_model = view_model.get_model()
|
||||
@@ -683,8 +577,13 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.clear()
|
||||
|
||||
# dummy image for prompt
|
||||
self.set_in_list(self.__custom_image__, "Use 'Edit image recipe' to customize recipes and packages " \
|
||||
"to be included in your image ")
|
||||
self.set(self.append(), self.COL_NAME, self.__custom_image__,
|
||||
self.COL_DESC, "Use 'Edit image' to customize recipes and packages " \
|
||||
"to be included in your image ",
|
||||
self.COL_LIC, "", self.COL_GROUP, "",
|
||||
self.COL_DEPS, "", self.COL_BINB, "",
|
||||
self.COL_TYPE, "image", self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, self.__custom_image__)
|
||||
|
||||
for item in event_model["pn"]:
|
||||
name = item
|
||||
@@ -692,12 +591,6 @@ class RecipeListModel(gtk.ListStore):
|
||||
lic = event_model["pn"][item]["license"]
|
||||
group = event_model["pn"][item]["section"]
|
||||
inherits = event_model["pn"][item]["inherits"]
|
||||
summary = event_model["pn"][item]["summary"]
|
||||
version = event_model["pn"][item]["version"]
|
||||
revision = event_model["pn"][item]["prevision"]
|
||||
homepage = event_model["pn"][item]["homepage"]
|
||||
bugtracker = event_model["pn"][item]["bugtracker"]
|
||||
filename = event_model["pn"][item]["filename"]
|
||||
install = []
|
||||
|
||||
depends = event_model["depends"].get(item, []) + event_model["rdepends-pn"].get(item, [])
|
||||
@@ -719,10 +612,7 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.COL_LIC, lic, self.COL_GROUP, group,
|
||||
self.COL_DEPS, " ".join(depends), self.COL_BINB, "",
|
||||
self.COL_TYPE, atype, self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, " ".join(install), self.COL_PN, item,
|
||||
self.COL_SUMMARY, summary, self.COL_VERSION, version, self.COL_REVISION, revision,
|
||||
self.COL_HOMEPAGE, homepage, self.COL_BUGTRACKER, bugtracker,
|
||||
self.COL_FILE, filename)
|
||||
self.COL_IMG, False, self.COL_INSTALL, " ".join(install), self.COL_PN, item)
|
||||
|
||||
self.pn_path = {}
|
||||
it = self.get_iter_first()
|
||||
@@ -732,23 +622,6 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.pn_path[pn] = path
|
||||
it = self.iter_next(it)
|
||||
|
||||
def set_in_list(self, item, desc):
|
||||
self.set(self.append(), self.COL_NAME, item,
|
||||
self.COL_DESC, desc,
|
||||
self.COL_LIC, "", self.COL_GROUP, "",
|
||||
self.COL_DEPS, "", self.COL_BINB, "",
|
||||
self.COL_TYPE, "image", self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, item,
|
||||
self.COL_SUMMARY, "", self.COL_VERSION, "", self.COL_REVISION, "",
|
||||
self.COL_HOMEPAGE, "", self.COL_BUGTRACKER, "")
|
||||
self.pn_path = {}
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
pn = self.get_value(it, self.COL_NAME)
|
||||
path = self.get_path(it)
|
||||
self.pn_path[pn] = path
|
||||
it = self.iter_next(it)
|
||||
|
||||
"""
|
||||
Update the model, send out the notification.
|
||||
"""
|
||||
@@ -899,6 +772,3 @@ class RecipeListModel(gtk.ListStore):
|
||||
|
||||
def get_custom_image_version(self):
|
||||
return self.custom_image_version
|
||||
|
||||
def is_custom_image(self):
|
||||
return self.get_selected_image() == self.__custom_image__
|
||||
|
||||
@@ -44,6 +44,8 @@ class hic:
|
||||
ICON_PACKAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_hover.png'))
|
||||
ICON_LAYERS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_display.png'))
|
||||
ICON_LAYERS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_hover.png'))
|
||||
ICON_TEMPLATES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('templates/templates_display.png'))
|
||||
ICON_TEMPLATES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('templates/templates_hover.png'))
|
||||
ICON_IMAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_display.png'))
|
||||
ICON_IMAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_hover.png'))
|
||||
ICON_SETTINGS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_display.png'))
|
||||
@@ -83,29 +85,23 @@ class HobViewTable (gtk.VBox):
|
||||
gobject.TYPE_PYOBJECT,)),
|
||||
}
|
||||
|
||||
def __init__(self, columns, name):
|
||||
def __init__(self, columns):
|
||||
gtk.VBox.__init__(self, False, 6)
|
||||
self.table_tree = gtk.TreeView()
|
||||
self.table_tree.set_headers_visible(True)
|
||||
self.table_tree.set_headers_clickable(True)
|
||||
self.table_tree.set_enable_search(True)
|
||||
self.table_tree.set_rules_hint(True)
|
||||
self.table_tree.set_enable_tree_lines(True)
|
||||
self.table_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
||||
self.toggle_columns = []
|
||||
self.table_tree.connect("row-activated", self.row_activated_cb)
|
||||
self.top_bar = None
|
||||
self.tab_name = name
|
||||
|
||||
for i, column in enumerate(columns):
|
||||
col_name = column['col_name']
|
||||
col = gtk.TreeViewColumn(col_name)
|
||||
col = gtk.TreeViewColumn(column['col_name'])
|
||||
col.set_clickable(True)
|
||||
col.set_resizable(True)
|
||||
if self.tab_name.startswith('Included'):
|
||||
if col_name!='Included':
|
||||
col.set_sort_column_id(column['col_id'])
|
||||
else:
|
||||
col.set_sort_column_id(column['col_id'])
|
||||
col.set_sort_column_id(column['col_id'])
|
||||
if 'col_min' in column.keys():
|
||||
col.set_min_width(column['col_min'])
|
||||
if 'col_max' in column.keys():
|
||||
@@ -128,7 +124,7 @@ class HobViewTable (gtk.VBox):
|
||||
self.toggle_id = i
|
||||
col.pack_end(cell, True)
|
||||
col.set_attributes(cell, active=column['col_id'])
|
||||
self.toggle_columns.append(col_name)
|
||||
self.toggle_columns.append(column['col_name'])
|
||||
if 'col_group' in column.keys():
|
||||
col.set_cell_data_func(cell, self.set_group_number_cb)
|
||||
elif column['col_style'] == 'radio toggle':
|
||||
@@ -139,7 +135,7 @@ class HobViewTable (gtk.VBox):
|
||||
self.toggle_id = i
|
||||
col.pack_end(cell, True)
|
||||
col.set_attributes(cell, active=column['col_id'])
|
||||
self.toggle_columns.append(col_name)
|
||||
self.toggle_columns.append(column['col_name'])
|
||||
elif column['col_style'] == 'binb':
|
||||
cell = gtk.CellRendererText()
|
||||
col.pack_start(cell, True)
|
||||
@@ -147,42 +143,10 @@ class HobViewTable (gtk.VBox):
|
||||
if 'col_t_id' in column.keys():
|
||||
col.add_attribute(cell, 'font', column['col_t_id'])
|
||||
|
||||
self.scroll = gtk.ScrolledWindow()
|
||||
self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
self.scroll.add(self.table_tree)
|
||||
|
||||
self.pack_end(self.scroll, True, True, 0)
|
||||
|
||||
def add_no_result_bar(self, entry):
|
||||
color = HobColors.KHAKI
|
||||
self.top_bar = gtk.EventBox()
|
||||
self.top_bar.set_size_request(-1, 70)
|
||||
self.top_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
|
||||
self.top_bar.set_flags(gtk.CAN_DEFAULT)
|
||||
self.top_bar.grab_default()
|
||||
|
||||
no_result_tab = gtk.Table(5, 20, True)
|
||||
self.top_bar.add(no_result_tab)
|
||||
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0.0, 0.5)
|
||||
title = "No results matching your search"
|
||||
label.set_markup("<span size='x-large'><b>%s</b></span>" % title)
|
||||
no_result_tab.attach(label, 1, 14, 1, 4)
|
||||
|
||||
clear_button = HobButton("Clear search")
|
||||
clear_button.set_tooltip_text("Clear search query")
|
||||
clear_button.connect('clicked', self.set_search_entry_clear_cb, entry)
|
||||
no_result_tab.attach(clear_button, 16, 19, 1, 4)
|
||||
|
||||
self.pack_start(self.top_bar, False, True, 12)
|
||||
self.top_bar.show_all()
|
||||
|
||||
def set_search_entry_clear_cb(self, button, search):
|
||||
if search.get_editable() == True:
|
||||
search.set_text("")
|
||||
search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
search.grab_focus()
|
||||
scroll = gtk.ScrolledWindow()
|
||||
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
||||
scroll.add(self.table_tree)
|
||||
self.pack_start(scroll, True, True, 0)
|
||||
|
||||
def display_binb_cb(self, col, cell, model, it, col_id):
|
||||
binb = model.get_value(it, col_id)
|
||||
@@ -208,6 +172,10 @@ class HobViewTable (gtk.VBox):
|
||||
def set_model(self, tree_model):
|
||||
self.table_tree.set_model(tree_model)
|
||||
|
||||
def set_search_entry(self, search_column_id, entry):
|
||||
self.table_tree.set_search_column(search_column_id)
|
||||
self.table_tree.set_search_entry(entry)
|
||||
|
||||
def toggle_default(self):
|
||||
model = self.table_tree.get_model()
|
||||
if not model:
|
||||
@@ -379,13 +347,18 @@ class HobInfoButton(gtk.EventBox):
|
||||
hic.ICON_INFO_DISPLAY_FILE)
|
||||
self.image.show()
|
||||
self.add(self.image)
|
||||
self.tip_markup = tip_markup
|
||||
self.my_parent = parent
|
||||
|
||||
self.set_events(gtk.gdk.BUTTON_RELEASE |
|
||||
gtk.gdk.ENTER_NOTIFY_MASK |
|
||||
gtk.gdk.LEAVE_NOTIFY_MASK)
|
||||
|
||||
self.ptip = PersistentTooltip(tip_markup)
|
||||
|
||||
if parent:
|
||||
self.ptip.set_parent(parent)
|
||||
self.ptip.set_transient_for(parent)
|
||||
self.ptip.set_destroy_with_parent(True)
|
||||
|
||||
self.connect("button-release-event", self.button_release_cb)
|
||||
self.connect("enter-notify-event", self.mouse_in_cb)
|
||||
self.connect("leave-notify-event", self.mouse_out_cb)
|
||||
@@ -395,18 +368,7 @@ class HobInfoButton(gtk.EventBox):
|
||||
PersistentTooltip
|
||||
"""
|
||||
def button_release_cb(self, widget, event):
|
||||
from bb.ui.crumbs.hig.propertydialog import PropertyDialog
|
||||
self.dialog = PropertyDialog(title = '',
|
||||
parent = self.my_parent,
|
||||
information = self.tip_markup,
|
||||
flags = gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.DIALOG_NO_SEPARATOR)
|
||||
|
||||
button = self.dialog.add_button("Close", gtk.RESPONSE_CANCEL)
|
||||
HobAltButton.style_button(button)
|
||||
button.connect("clicked", lambda w: self.dialog.destroy())
|
||||
self.dialog.show_all()
|
||||
self.dialog.run()
|
||||
self.ptip.show()
|
||||
|
||||
"""
|
||||
Change to the prelight image when the mouse enters the widget
|
||||
@@ -487,8 +449,7 @@ class HobNotebook(gtk.Notebook):
|
||||
self.pages = []
|
||||
|
||||
self.search = None
|
||||
self.search_focus = False
|
||||
self.page_changed = False
|
||||
self.search_name = ""
|
||||
|
||||
self.connect("switch-page", self.page_changed_cb)
|
||||
|
||||
@@ -501,10 +462,6 @@ class HobNotebook(gtk.Notebook):
|
||||
else:
|
||||
lbl.set_active(False)
|
||||
|
||||
if self.search:
|
||||
self.page_changed = True
|
||||
self.reset_entry(self.search, page_num)
|
||||
|
||||
def append_page(self, child, tab_label, tab_tooltip=None):
|
||||
label = HobTabLabel(tab_label)
|
||||
if tab_tooltip:
|
||||
@@ -513,22 +470,16 @@ class HobNotebook(gtk.Notebook):
|
||||
self.pages.append(label)
|
||||
gtk.Notebook.append_page(self, child, label)
|
||||
|
||||
def set_entry(self, names, tips):
|
||||
def set_entry(self, name="Search:"):
|
||||
self.search = gtk.Entry()
|
||||
self.search_names = names
|
||||
self.search_tips = tips
|
||||
self.search_name = name
|
||||
style = self.search.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
||||
self.search.set_style(style)
|
||||
self.search.set_text(names[0])
|
||||
self.search.set_tooltip_text(self.search_tips[0])
|
||||
self.search.props.has_tooltip = True
|
||||
|
||||
self.search.set_text(name)
|
||||
self.search.set_editable(False)
|
||||
self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR)
|
||||
self.search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
self.search.connect("icon-release", self.set_search_entry_clear_cb)
|
||||
self.search.set_width_chars(30)
|
||||
self.search.show()
|
||||
|
||||
self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
|
||||
@@ -546,35 +497,25 @@ class HobNotebook(gtk.Notebook):
|
||||
child.set_count(0)
|
||||
|
||||
def set_search_entry_editable_cb(self, search, event):
|
||||
self.search_focus = True
|
||||
search.set_editable(True)
|
||||
text = search.get_text()
|
||||
if text in self.search_names:
|
||||
search.set_text("")
|
||||
search.set_text("")
|
||||
style = self.search.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False)
|
||||
search.set_style(style)
|
||||
|
||||
def set_search_entry_reset_cb(self, search, event):
|
||||
page_num = self.get_current_page()
|
||||
text = search.get_text()
|
||||
if not text:
|
||||
self.reset_entry(search, page_num)
|
||||
|
||||
def reset_entry(self, entry, page_num):
|
||||
def reset_entry(self, entry):
|
||||
style = entry.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
||||
entry.set_style(style)
|
||||
entry.set_text(self.search_names[page_num])
|
||||
entry.set_tooltip_text(self.search_tips[page_num])
|
||||
entry.set_text(self.search_name)
|
||||
entry.set_editable(False)
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
|
||||
def set_search_entry_reset_cb(self, search, event):
|
||||
self.reset_entry(search)
|
||||
|
||||
def set_search_entry_clear_cb(self, search, icon_pos, event):
|
||||
if search.get_editable() == True:
|
||||
search.set_text("")
|
||||
search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
search.grab_focus()
|
||||
|
||||
def set_page(self, title):
|
||||
for child in self.pages:
|
||||
|
||||
@@ -28,7 +28,6 @@ from bb.ui.crumbs.hobcolor import HobColors
|
||||
from bb.ui.crumbs.hobwidget import hic, HobImageButton, HobInfoButton, HobAltButton, HobButton
|
||||
from bb.ui.crumbs.hoblistmodel import RecipeListModel
|
||||
from bb.ui.crumbs.hobpages import HobPage
|
||||
from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
|
||||
|
||||
#
|
||||
# ImageConfigurationPage
|
||||
@@ -36,8 +35,7 @@ from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
|
||||
class ImageConfigurationPage (HobPage):
|
||||
|
||||
__dummy_machine__ = "--select a machine--"
|
||||
__dummy_image__ = "--select an image recipe--"
|
||||
__custom_image__ = "Select from my image recipes"
|
||||
__dummy_image__ = "--select a base image--"
|
||||
|
||||
def __init__(self, builder):
|
||||
super(ImageConfigurationPage, self).__init__(builder, "Image configuration")
|
||||
@@ -49,7 +47,6 @@ class ImageConfigurationPage (HobPage):
|
||||
self.machine_combo_changed_by_manual = True
|
||||
self.stopping = False
|
||||
self.warning_shift = 0
|
||||
self.custom_image_selected = None
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
@@ -58,6 +55,12 @@ class ImageConfigurationPage (HobPage):
|
||||
self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
|
||||
self.toolbar.set_style(gtk.TOOLBAR_BOTH)
|
||||
|
||||
template_button = self.append_toolbar_button(self.toolbar,
|
||||
"Templates",
|
||||
hic.ICON_TEMPLATES_DISPLAY_FILE,
|
||||
hic.ICON_TEMPLATES_HOVER_FILE,
|
||||
"Load a previously saved template",
|
||||
self.template_button_clicked_cb)
|
||||
my_images_button = self.append_toolbar_button(self.toolbar,
|
||||
"Images",
|
||||
hic.ICON_IMAGES_DISPLAY_FILE,
|
||||
@@ -170,12 +173,6 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
return warnings_bar
|
||||
|
||||
def disable_warnings_bar(self):
|
||||
if self.builder.parsing_warnings:
|
||||
if hasattr(self, 'warnings_bar'):
|
||||
self.warnings_bar.hide_all()
|
||||
self.builder.parsing_warnings = []
|
||||
|
||||
def create_config_machine(self):
|
||||
self.machine_title = gtk.Label()
|
||||
self.machine_title.set_alignment(0.0, 0.5)
|
||||
@@ -202,10 +199,14 @@ class ImageConfigurationPage (HobPage):
|
||||
markup += "For more on layers, check the <a href=\""
|
||||
markup += "http://www.yoctoproject.org/docs/current/dev-manual/"
|
||||
markup += "dev-manual.html#understanding-and-using-layers\">reference manual</a>."
|
||||
self.layer_info_icon = HobInfoButton("<b>Layers</b>" + "*" + markup, self.get_parent())
|
||||
self.layer_info_icon = HobInfoButton(markup, self.get_parent())
|
||||
|
||||
# self.progress_box = gtk.HBox(False, 6)
|
||||
self.progress_bar = HobProgressBar()
|
||||
# self.progress_box.pack_start(self.progress_bar, expand=True, fill=True)
|
||||
self.stop_button = HobAltButton("Stop")
|
||||
self.stop_button.connect("clicked", self.stop_button_clicked_cb)
|
||||
# self.progress_box.pack_end(stop_button, expand=False, fill=False)
|
||||
self.machine_separator = gtk.HSeparator()
|
||||
|
||||
def set_config_machine_layout(self, show_progress_bar = False):
|
||||
@@ -229,19 +230,17 @@ class ImageConfigurationPage (HobPage):
|
||||
def create_config_baseimg(self):
|
||||
self.image_title = gtk.Label()
|
||||
self.image_title.set_alignment(0, 1.0)
|
||||
mark = "<span %s>Select an image recipe</span>" % self.span_tag('x-large', 'bold')
|
||||
mark = "<span %s>Select a base image</span>" % self.span_tag('x-large', 'bold')
|
||||
self.image_title.set_markup(mark)
|
||||
|
||||
self.image_title_desc = gtk.Label()
|
||||
self.image_title_desc.set_alignment(0, 0.5)
|
||||
|
||||
mark = ("<span %s>Image recipes are a starting point for the type of image you want. "
|
||||
mark = ("<span %s>Base images are a starting point for the type of image you want. "
|
||||
"You can build them as \n"
|
||||
"they are or edit them to suit your needs.\n</span>") % self.span_tag('medium')
|
||||
"they are or customize them to your specific needs.\n</span>") % self.span_tag('medium')
|
||||
self.image_title_desc.set_markup(mark)
|
||||
|
||||
self.image_combo = gtk.combo_box_new_text()
|
||||
self.image_combo.set_row_separator_func(self.combo_separator_func, None)
|
||||
self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
|
||||
|
||||
self.image_desc = gtk.Label()
|
||||
@@ -260,11 +259,6 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
self.image_separator = gtk.HSeparator()
|
||||
|
||||
def combo_separator_func(self, model, iter, user_data):
|
||||
name = model.get_value(iter, 0)
|
||||
if name == "--Separator--":
|
||||
return True
|
||||
|
||||
def set_config_baseimg_layout(self):
|
||||
self.gtable.attach(self.image_title, 0, 40, 15+self.warning_shift, 17+self.warning_shift)
|
||||
self.gtable.attach(self.image_title_desc, 0, 40, 18+self.warning_shift, 22+self.warning_shift)
|
||||
@@ -279,13 +273,15 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
# create button "Build image"
|
||||
self.just_bake_button = HobButton("Build image")
|
||||
self.just_bake_button.set_tooltip_text("Build the image recipe as it is")
|
||||
#self.just_bake_button.set_size_request(205, 49)
|
||||
self.just_bake_button.set_tooltip_text("Build target image")
|
||||
self.just_bake_button.connect("clicked", self.just_bake_button_clicked_cb)
|
||||
button_box.pack_end(self.just_bake_button, expand=False, fill=False)
|
||||
|
||||
# create button "Edit image recipe"
|
||||
self.edit_image_button = HobAltButton("Edit image recipe")
|
||||
self.edit_image_button.set_tooltip_text("Customize the recipes and packages to be included in your image")
|
||||
# create button "Edit Image"
|
||||
self.edit_image_button = HobAltButton("Edit image")
|
||||
#self.edit_image_button.set_size_request(205, 49)
|
||||
self.edit_image_button.set_tooltip_text("Edit target image")
|
||||
self.edit_image_button.connect("clicked", self.edit_image_button_clicked_cb)
|
||||
button_box.pack_end(self.edit_image_button, expand=False, fill=False)
|
||||
|
||||
@@ -300,14 +296,8 @@ class ImageConfigurationPage (HobPage):
|
||||
def view_warnings_button_clicked_cb(self, button):
|
||||
self.builder.show_warning_dialog()
|
||||
|
||||
def machine_combo_changed_idle_cb(self):
|
||||
self.builder.window.set_cursor(None)
|
||||
|
||||
def machine_combo_changed_cb(self, machine_combo):
|
||||
self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
||||
self.builder.wait(0.1) #wait for combo and cursor to update
|
||||
self.stopping = False
|
||||
self.builder.parsing_warnings = []
|
||||
combo_item = machine_combo.get_active_text()
|
||||
if not combo_item or combo_item == self.__dummy_machine__:
|
||||
return
|
||||
@@ -324,15 +314,10 @@ class ImageConfigurationPage (HobPage):
|
||||
# reset machine_combo_changed_by_manual
|
||||
self.machine_combo_changed_by_manual = True
|
||||
|
||||
self.builder.configuration.selected_image = None
|
||||
|
||||
# Do reparse recipes
|
||||
self.builder.populate_recipe_package_info_async()
|
||||
|
||||
glib.idle_add(self.machine_combo_changed_idle_cb)
|
||||
|
||||
def update_machine_combo(self):
|
||||
self.disable_warnings_bar()
|
||||
all_machines = [self.__dummy_machine__] + self.builder.parameters.all_machines
|
||||
|
||||
model = self.machine_combo.get_model()
|
||||
@@ -342,7 +327,6 @@ class ImageConfigurationPage (HobPage):
|
||||
self.machine_combo.set_active(0)
|
||||
|
||||
def switch_machine_combo(self):
|
||||
self.disable_warnings_bar()
|
||||
self.machine_combo_changed_by_manual = False
|
||||
model = self.machine_combo.get_model()
|
||||
active = 0
|
||||
@@ -376,33 +360,7 @@ class ImageConfigurationPage (HobPage):
|
||||
def image_combo_changed_cb(self, combo):
|
||||
self.builder.window_sensitive(False)
|
||||
selected_image = self.image_combo.get_active_text()
|
||||
if selected_image == self.__custom_image__:
|
||||
topdir = self.builder.get_topdir()
|
||||
images_dir = topdir + "/recipes/images/"
|
||||
self.builder.ensure_dir(images_dir)
|
||||
|
||||
dialog = RetrieveImageDialog(images_dir, "Select from my image recipes",
|
||||
self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
image_name = dialog.get_filename()
|
||||
head, tail = os.path.split(image_name)
|
||||
selected_image = os.path.splitext(tail)[0]
|
||||
self.custom_image_selected = selected_image
|
||||
self.update_image_combo(self.builder.recipe_model, selected_image)
|
||||
else:
|
||||
selected_image = self.__dummy_image__
|
||||
self.update_image_combo(self.builder.recipe_model, None)
|
||||
dialog.destroy()
|
||||
else:
|
||||
if self.custom_image_selected:
|
||||
self.custom_image_selected = None
|
||||
self.update_image_combo(self.builder.recipe_model, selected_image)
|
||||
|
||||
if not selected_image or (selected_image == self.__dummy_image__):
|
||||
self.builder.window_sensitive(True)
|
||||
self.just_bake_button.hide()
|
||||
self.edit_image_button.hide()
|
||||
return
|
||||
|
||||
# remove __dummy_image__ item from the store list after first user selection
|
||||
@@ -468,11 +426,6 @@ class ImageConfigurationPage (HobPage):
|
||||
self.image_combo.append_text(self.__dummy_image__)
|
||||
cnt = cnt + 1
|
||||
|
||||
self.image_combo.append_text(self.__custom_image__)
|
||||
self.image_combo.append_text("--Separator--")
|
||||
cnt = cnt + 2
|
||||
|
||||
topdir = self.builder.get_topdir()
|
||||
# append and set active
|
||||
while it:
|
||||
path = image_model.get_path(it)
|
||||
@@ -496,27 +449,16 @@ class ImageConfigurationPage (HobPage):
|
||||
else:
|
||||
allow = True
|
||||
|
||||
file_name = image_model[path][recipe_model.COL_FILE]
|
||||
if file_name and topdir in file_name:
|
||||
allow = False
|
||||
|
||||
if allow:
|
||||
self.image_combo.append_text(image_name)
|
||||
if image_name == selected_image:
|
||||
active = cnt
|
||||
cnt = cnt + 1
|
||||
self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
|
||||
|
||||
self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
|
||||
if selected_image == self.builder.recipe_model.__custom_image__:
|
||||
active = cnt
|
||||
|
||||
if self.custom_image_selected:
|
||||
self.image_combo.append_text("--Separator--")
|
||||
self.image_combo.append_text(self.custom_image_selected)
|
||||
cnt = cnt + 2
|
||||
if self.custom_image_selected == selected_image:
|
||||
active = cnt
|
||||
|
||||
self.image_combo.set_active(active)
|
||||
|
||||
if active != 0:
|
||||
@@ -527,17 +469,14 @@ class ImageConfigurationPage (HobPage):
|
||||
def layer_button_clicked_cb(self, button):
|
||||
# Create a layer selection dialog
|
||||
self.builder.show_layer_selection_dialog()
|
||||
|
||||
|
||||
def view_adv_configuration_button_clicked_cb(self, button):
|
||||
# Create an advanced settings dialog
|
||||
response, settings_changed = self.builder.show_adv_settings_dialog()
|
||||
if not response:
|
||||
return
|
||||
if settings_changed:
|
||||
self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
||||
self.builder.wait(0.1) #wait for adv_settings_dialog to terminate
|
||||
self.builder.reparse_post_adv_settings()
|
||||
self.builder.window.set_cursor(None)
|
||||
self.builder.reparse_post_adv_settings()
|
||||
|
||||
def just_bake_button_clicked_cb(self, button):
|
||||
self.builder.parsing_warnings = []
|
||||
@@ -547,6 +486,13 @@ class ImageConfigurationPage (HobPage):
|
||||
self.builder.configuration.initial_selected_image = self.builder.configuration.selected_image
|
||||
self.builder.show_recipes()
|
||||
|
||||
def template_button_clicked_cb(self, button):
|
||||
response, path = self.builder.show_load_template_dialog()
|
||||
if not response:
|
||||
return
|
||||
if path:
|
||||
self.builder.load_template(path)
|
||||
|
||||
def my_images_button_clicked_cb(self, button):
|
||||
self.builder.show_load_my_images_dialog()
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ from bb.ui.crumbs.hobwidget import hic, HobViewTable, HobAltButton, HobButton
|
||||
from bb.ui.crumbs.hobpages import HobPage
|
||||
import subprocess
|
||||
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||
from bb.ui.crumbs.hig.saveimagedialog import SaveImageDialog
|
||||
|
||||
#
|
||||
# ImageDetailsPage
|
||||
@@ -189,10 +188,7 @@ class ImageDetailsPage (HobPage):
|
||||
self.image_store = []
|
||||
self.button_ids = {}
|
||||
self.details_bottom_buttons = gtk.HBox(False, 6)
|
||||
self.image_saved = False
|
||||
self.create_visual_elements()
|
||||
self.name_field_template = ""
|
||||
self.description_field_template = ""
|
||||
|
||||
def create_visual_elements(self):
|
||||
# create visual elements
|
||||
@@ -201,6 +197,12 @@ class ImageDetailsPage (HobPage):
|
||||
self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
|
||||
self.toolbar.set_style(gtk.TOOLBAR_BOTH)
|
||||
|
||||
template_button = self.append_toolbar_button(self.toolbar,
|
||||
"Templates",
|
||||
hic.ICON_TEMPLATES_DISPLAY_FILE,
|
||||
hic.ICON_TEMPLATES_HOVER_FILE,
|
||||
"Load a previously saved template",
|
||||
self.template_button_clicked_cb)
|
||||
my_images_button = self.append_toolbar_button(self.toolbar,
|
||||
"Images",
|
||||
hic.ICON_IMAGES_DISPLAY_FILE,
|
||||
@@ -251,7 +253,7 @@ class ImageDetailsPage (HobPage):
|
||||
self.pack_start(self.group_align, expand=True, fill=True)
|
||||
|
||||
self.build_result = None
|
||||
if self.image_saved or (self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING):
|
||||
if self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING:
|
||||
# building is the previous step
|
||||
icon = gtk.Image()
|
||||
pixmap_path = hic.ICON_INDI_CONFIRM_FILE
|
||||
@@ -259,14 +261,15 @@ class ImageDetailsPage (HobPage):
|
||||
pix_buffer = gtk.gdk.pixbuf_new_from_file(pixmap_path)
|
||||
icon.set_from_pixbuf(pix_buffer)
|
||||
varlist = [""]
|
||||
if self.image_saved:
|
||||
vallist = ["Your image recipe has been saved"]
|
||||
else:
|
||||
vallist = ["Your image is ready"]
|
||||
vallist = ["Your image is ready"]
|
||||
self.build_result = self.BuildDetailBox(varlist=varlist, vallist=vallist, icon=icon, color=color)
|
||||
self.box_group_area.pack_start(self.build_result, expand=False, fill=False)
|
||||
|
||||
self.buttonlist = ["Build new image", "Save image recipe", "Run image", "Deploy image"]
|
||||
# create the buttons at the bottom first because the buttons are used in apply_button_per_image()
|
||||
if self.build_succeeded:
|
||||
self.buttonlist = ["Build new image", "Save as template", "Run image", "Deploy image"]
|
||||
else: # get to this page from "My images"
|
||||
self.buttonlist = ["Build new image", "Run image", "Deploy image"]
|
||||
|
||||
# Name
|
||||
self.image_store = []
|
||||
@@ -340,24 +343,19 @@ class ImageDetailsPage (HobPage):
|
||||
# self.kernel_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=change_kernel_button)
|
||||
# self.box_group_area.pack_start(self.kernel_detail, expand=True, fill=True)
|
||||
|
||||
# Machine, Image recipe and Layers
|
||||
# Machine, Base image and Layers
|
||||
layer_num_limit = 15
|
||||
varlist = ["Machine: ", "Image recipe: ", "Layers: "]
|
||||
varlist = ["Machine: ", "Base image: ", "Layers: "]
|
||||
vallist = []
|
||||
self.setting_detail = None
|
||||
if self.build_succeeded:
|
||||
vallist.append(machine)
|
||||
if self.builder.recipe_model.is_custom_image():
|
||||
if self.builder.configuration.initial_selected_image == self.builder.recipe_model.__custom_image__:
|
||||
base_image ="New image recipe"
|
||||
else:
|
||||
base_image = self.builder.configuration.initial_selected_image + " (edited)"
|
||||
vallist.append(base_image)
|
||||
i = 0
|
||||
for layer in layers:
|
||||
varlist.append(" - ")
|
||||
if i > layer_num_limit:
|
||||
break
|
||||
varlist.append(" - ")
|
||||
i += 1
|
||||
vallist.append("")
|
||||
i = 0
|
||||
@@ -371,7 +369,7 @@ class ImageDetailsPage (HobPage):
|
||||
i += 1
|
||||
|
||||
edit_config_button = HobAltButton("Edit configuration")
|
||||
edit_config_button.set_tooltip_text("Edit machine and image recipe")
|
||||
edit_config_button.set_tooltip_text("Edit machine, base image and recipes")
|
||||
edit_config_button.connect("clicked", self.edit_config_button_clicked_cb)
|
||||
self.setting_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=edit_config_button)
|
||||
self.box_group_area.pack_start(self.setting_detail, expand=True, fill=True)
|
||||
@@ -381,8 +379,6 @@ class ImageDetailsPage (HobPage):
|
||||
vallist = []
|
||||
vallist.append(pkg_num)
|
||||
vallist.append(default_image_size)
|
||||
self.builder.configuration.image_size = default_image_size
|
||||
self.builder.configuration.image_packages = self.builder.configuration.selected_packages
|
||||
if self.build_succeeded:
|
||||
edit_packages_button = HobAltButton("Edit packages")
|
||||
edit_packages_button.set_tooltip_text("Edit the packages included in your image")
|
||||
@@ -403,7 +399,6 @@ class ImageDetailsPage (HobPage):
|
||||
self.show_all()
|
||||
if self.kernel_detail and (not is_runnable):
|
||||
self.kernel_detail.hide()
|
||||
self.image_saved = False
|
||||
|
||||
def view_files_clicked_cb(self, button, image_addr):
|
||||
subprocess.call("xdg-open /%s" % image_addr, shell=True)
|
||||
@@ -587,14 +582,25 @@ class ImageDetailsPage (HobPage):
|
||||
created = True
|
||||
is_runnable = True
|
||||
|
||||
name = "Save image recipe"
|
||||
if name in buttonlist and self.builder.recipe_model.is_custom_image():
|
||||
save_button = HobAltButton("Save image recipe")
|
||||
save_button.set_tooltip_text("Keep your changes saving them as an image recipe")
|
||||
save_button.set_sensitive(not self.image_saved)
|
||||
name = "Save as template"
|
||||
if name in buttonlist:
|
||||
if created == True:
|
||||
# separator
|
||||
#label = gtk.Label(" or ")
|
||||
#self.details_bottom_buttons.pack_end(label, expand=False, fill=False)
|
||||
|
||||
# create button "Save as template"
|
||||
save_button = HobAltButton("Save as template")
|
||||
else:
|
||||
save_button = HobButton("Save as template")
|
||||
#save_button.set_size_request(205, 49)
|
||||
save_button.set_flags(gtk.CAN_DEFAULT)
|
||||
packed = True
|
||||
save_button.set_tooltip_text("Save the image configuration for reuse")
|
||||
button_id = save_button.connect("clicked", self.save_button_clicked_cb)
|
||||
self.button_ids[button_id] = save_button
|
||||
self.details_bottom_buttons.pack_end(save_button, expand=False, fill=False)
|
||||
create = True
|
||||
|
||||
name = "Build new image"
|
||||
if name in buttonlist:
|
||||
@@ -612,6 +618,9 @@ class ImageDetailsPage (HobPage):
|
||||
|
||||
return is_runnable
|
||||
|
||||
def save_button_clicked_cb(self, button):
|
||||
self.builder.show_save_template_dialog()
|
||||
|
||||
def deploy_button_clicked_cb(self, button):
|
||||
if self.toggled_image:
|
||||
if self.num_toggled > 1:
|
||||
@@ -630,24 +639,6 @@ class ImageDetailsPage (HobPage):
|
||||
else:
|
||||
self.builder.runqemu_image(self.toggled_image, self.sel_kernel)
|
||||
|
||||
def save_button_clicked_cb(self, button):
|
||||
topdir = self.builder.get_topdir()
|
||||
images_dir = topdir + "/recipes/images/"
|
||||
self.builder.ensure_dir(images_dir)
|
||||
|
||||
self.name_field_template = self.builder.image_configuration_page.custom_image_selected
|
||||
if self.name_field_template:
|
||||
image_path = self.builder.recipe_model.pn_path[self.name_field_template]
|
||||
image_iter = self.builder.recipe_model.get_iter(image_path)
|
||||
self.description_field_template = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_DESC)
|
||||
else:
|
||||
self.name_field_template = ""
|
||||
|
||||
dialog = SaveImageDialog(images_dir, self.name_field_template, self.description_field_template,
|
||||
"Save image recipe", self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
response = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
def build_new_button_clicked_cb(self, button):
|
||||
self.builder.initiate_new_build_async()
|
||||
|
||||
@@ -657,6 +648,13 @@ class ImageDetailsPage (HobPage):
|
||||
def edit_packages_button_clicked_cb(self, button):
|
||||
self.builder.show_packages(ask=False)
|
||||
|
||||
def template_button_clicked_cb(self, button):
|
||||
response, path = self.builder.show_load_template_dialog()
|
||||
if not response:
|
||||
return
|
||||
if path:
|
||||
self.builder.load_template(path)
|
||||
|
||||
def my_images_button_clicked_cb(self, button):
|
||||
self.builder.show_load_my_images_dialog()
|
||||
|
||||
|
||||
@@ -34,12 +34,10 @@ class PackageSelectionPage (HobPage):
|
||||
|
||||
pages = [
|
||||
{
|
||||
'name' : 'Included packages',
|
||||
'tooltip' : 'The packages currently included for your image',
|
||||
'filter' : { PackageListModel.COL_INC : [True] },
|
||||
'search' : 'Search packages by name',
|
||||
'searchtip' : 'Enter a package name to find it',
|
||||
'columns' : [{
|
||||
'name' : 'Included packages',
|
||||
'tooltip' : 'The packages currently included for your image',
|
||||
'filter' : { PackageListModel.COL_INC : [True] },
|
||||
'columns' : [{
|
||||
'col_name' : 'Package name',
|
||||
'col_id' : PackageListModel.COL_NAME,
|
||||
'col_style': 'text',
|
||||
@@ -53,13 +51,6 @@ class PackageSelectionPage (HobPage):
|
||||
'col_min' : 100,
|
||||
'col_max' : 300,
|
||||
'expand' : 'True'
|
||||
}, {
|
||||
'col_name' : 'Recipe',
|
||||
'col_id' : PackageListModel.COL_RCP,
|
||||
'col_style': 'text',
|
||||
'col_min' : 100,
|
||||
'col_max' : 250,
|
||||
'expand' : 'True'
|
||||
}, {
|
||||
'col_name' : 'Brought in by (+others)',
|
||||
'col_id' : PackageListModel.COL_BINB,
|
||||
@@ -75,12 +66,10 @@ class PackageSelectionPage (HobPage):
|
||||
'col_max' : 100
|
||||
}]
|
||||
}, {
|
||||
'name' : 'All packages',
|
||||
'tooltip' : 'All packages that have been built',
|
||||
'filter' : {},
|
||||
'search' : 'Search packages by name',
|
||||
'searchtip' : 'Enter a package name to find it',
|
||||
'columns' : [{
|
||||
'name' : 'All packages',
|
||||
'tooltip' : 'All packages that have been built',
|
||||
'filter' : {},
|
||||
'columns' : [{
|
||||
'col_name' : 'Package name',
|
||||
'col_id' : PackageListModel.COL_NAME,
|
||||
'col_style': 'text',
|
||||
@@ -94,13 +83,6 @@ class PackageSelectionPage (HobPage):
|
||||
'col_min' : 100,
|
||||
'col_max' : 500,
|
||||
'expand' : 'True'
|
||||
}, {
|
||||
'col_name' : 'Recipe',
|
||||
'col_id' : PackageListModel.COL_RCP,
|
||||
'col_style': 'text',
|
||||
'col_min' : 100,
|
||||
'col_max' : 250,
|
||||
'expand' : 'True'
|
||||
}, {
|
||||
'col_name' : 'Included',
|
||||
'col_id' : PackageListModel.COL_INC,
|
||||
@@ -117,7 +99,7 @@ class PackageSelectionPage (HobPage):
|
||||
def __init__(self, builder):
|
||||
super(PackageSelectionPage, self).__init__(builder, "Edit packages")
|
||||
|
||||
# set invisible members
|
||||
# set invisiable members
|
||||
self.recipe_model = self.builder.recipe_model
|
||||
self.package_model = self.builder.package_model
|
||||
|
||||
@@ -136,27 +118,26 @@ class PackageSelectionPage (HobPage):
|
||||
# set visible members
|
||||
self.ins = HobNotebook()
|
||||
self.tables = [] # we need to modify table when the dialog is shown
|
||||
|
||||
search_names = []
|
||||
search_tips = []
|
||||
# append the tab
|
||||
for page in self.pages:
|
||||
columns = page['columns']
|
||||
name = page['name']
|
||||
tab = HobViewTable(columns, name)
|
||||
search_names.append(page['search'])
|
||||
search_tips.append(page['searchtip'])
|
||||
tab = HobViewTable(columns)
|
||||
filter = page['filter']
|
||||
sort_model = self.package_model.tree_model(filter, initial=True)
|
||||
tab.set_model(sort_model)
|
||||
tab.connect("toggled", self.table_toggled_cb, name)
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
|
||||
tab.set_model(self.package_model.tree_model(filter))
|
||||
tab.connect("toggled", self.table_toggled_cb, page['name'])
|
||||
if page['name'] == "Included packages":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
self.ins.append_page(tab, page['name'], page['tooltip'])
|
||||
self.tables.append(tab)
|
||||
|
||||
self.ins.set_entry(search_names, search_tips)
|
||||
self.ins.search.connect("changed", self.search_entry_changed)
|
||||
self.ins.set_entry("Search packages:")
|
||||
# set the search entry for each table
|
||||
for tab in self.tables:
|
||||
search_tip = "Enter a package name to find it"
|
||||
self.ins.search.set_tooltip_text(search_tip)
|
||||
self.ins.search.props.has_tooltip = True
|
||||
tab.set_search_entry(0, self.ins.search)
|
||||
|
||||
# add all into the dialog
|
||||
self.box_group_area.pack_start(self.ins, expand=True, fill=True)
|
||||
@@ -176,49 +157,13 @@ class PackageSelectionPage (HobPage):
|
||||
self.back_button.connect("clicked", self.back_button_clicked_cb)
|
||||
self.button_box.pack_end(self.back_button, expand=False, fill=False)
|
||||
|
||||
def search_entry_changed(self, entry):
|
||||
text = entry.get_text()
|
||||
if self.ins.search_focus:
|
||||
self.ins.search_focus = False
|
||||
elif self.ins.page_changed:
|
||||
self.ins.page_change = False
|
||||
self.filter_search(entry)
|
||||
elif text not in self.ins.search_names:
|
||||
self.filter_search(entry)
|
||||
|
||||
def filter_search(self, entry):
|
||||
text = entry.get_text()
|
||||
current_tab = self.ins.get_current_page()
|
||||
filter = self.pages[current_tab]['filter']
|
||||
filter[PackageListModel.COL_NAME] = text
|
||||
self.tables[current_tab].set_model(self.package_model.tree_model(filter, search_data=text))
|
||||
if self.package_model.filtered_nb == 0:
|
||||
if not self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
|
||||
self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
|
||||
self.ins.get_nth_page(current_tab).top_bar.show()
|
||||
self.ins.get_nth_page(current_tab).scroll.hide()
|
||||
else:
|
||||
if self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).top_bar.hide()
|
||||
self.ins.get_nth_page(current_tab).scroll.show()
|
||||
if entry.get_text() == '':
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
else:
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, True)
|
||||
|
||||
def button_click_cb(self, widget, event):
|
||||
path, col = widget.table_tree.get_cursor()
|
||||
tree_model = widget.table_tree.get_model()
|
||||
if path and col.get_title() != 'Included': # else activation is likely a removal
|
||||
properties = {'binb': '' , 'name': '', 'size':'', 'recipe':'', 'files_list':''}
|
||||
properties['binb'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_BINB)
|
||||
properties['name'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_NAME)
|
||||
properties['size'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_SIZE)
|
||||
properties['recipe'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_RCP)
|
||||
properties['files_list'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_FLIST)
|
||||
|
||||
self.builder.show_recipe_property_dialog(properties)
|
||||
if path: # else activation is likely a removal
|
||||
binb = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_BINB)
|
||||
if binb:
|
||||
self.builder.show_binb_dialog(binb)
|
||||
|
||||
def open_log_clicked_cb(self, button, log_file):
|
||||
if log_file:
|
||||
@@ -243,13 +188,6 @@ class PackageSelectionPage (HobPage):
|
||||
self.builder.parsing_warnings = []
|
||||
self.builder.build_image()
|
||||
|
||||
def refresh_tables(self):
|
||||
self.ins.reset_entry(self.ins.search, 0)
|
||||
for tab in self.tables:
|
||||
index = self.tables.index(tab)
|
||||
filter = self.pages[index]['filter']
|
||||
tab.set_model(self.package_model.tree_model(filter, initial=True))
|
||||
|
||||
def back_button_clicked_cb(self, button):
|
||||
if self.builder.previous_step == self.builder.IMAGE_GENERATED:
|
||||
self.builder.restore_initial_selected_packages()
|
||||
@@ -257,28 +195,30 @@ class PackageSelectionPage (HobPage):
|
||||
self.builder.show_image_details()
|
||||
else:
|
||||
self.builder.show_configuration()
|
||||
self.refresh_tables()
|
||||
|
||||
def _expand_all(self):
|
||||
for tab in self.tables:
|
||||
tab.table_tree.expand_all()
|
||||
|
||||
def refresh_selection(self):
|
||||
self._expand_all()
|
||||
|
||||
self.builder.configuration.selected_packages = self.package_model.get_selected_packages()
|
||||
self.builder.configuration.user_selected_packages = self.package_model.get_user_selected_packages()
|
||||
selected_packages_num = len(self.builder.configuration.selected_packages)
|
||||
selected_packages_size = self.package_model.get_packages_size()
|
||||
selected_packages_size_str = HobPage._size_to_string(selected_packages_size)
|
||||
|
||||
if self.builder.configuration.image_packages == self.builder.configuration.selected_packages:
|
||||
image_total_size_str = self.builder.configuration.image_size
|
||||
else:
|
||||
image_overhead_factor = self.builder.configuration.image_overhead_factor
|
||||
image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB
|
||||
image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB
|
||||
base_size = image_overhead_factor * selected_packages_size
|
||||
image_total_size = max(base_size, image_rootfs_size) + image_extra_size
|
||||
if "zypper" in self.builder.configuration.selected_packages:
|
||||
image_total_size += (51200 * 1024)
|
||||
image_total_size_str = HobPage._size_to_string(image_total_size)
|
||||
image_overhead_factor = self.builder.configuration.image_overhead_factor
|
||||
image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB
|
||||
image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB
|
||||
base_size = image_overhead_factor * selected_packages_size
|
||||
image_total_size = max(base_size, image_rootfs_size) + image_extra_size
|
||||
if "zypper" in self.builder.configuration.selected_packages:
|
||||
image_total_size += (51200 * 1024)
|
||||
image_total_size_str = HobPage._size_to_string(image_total_size)
|
||||
|
||||
self.label.set_label("Packages included: %s\nSelected packages size: %s\nEstimated image size: %s" %
|
||||
self.label.set_label("Packages included: %s\nSelected packages size: %s\nTotal image size: %s" %
|
||||
(selected_packages_num, selected_packages_size_str, image_total_size_str))
|
||||
self.ins.show_indicator_icon("Included packages", selected_packages_num)
|
||||
|
||||
@@ -286,21 +226,20 @@ class PackageSelectionPage (HobPage):
|
||||
if not self.package_model.path_included(path):
|
||||
self.package_model.include_item(item_path=path, binb="User Selected")
|
||||
else:
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
if pagename == "Included packages":
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
else:
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
|
||||
self.refresh_selection()
|
||||
if not self.builder.customized:
|
||||
self.builder.customized = True
|
||||
self.builder.configuration.initial_selected_image = self.builder.configuration.selected_image
|
||||
self.builder.configuration.selected_image = self.recipe_model.__custom_image__
|
||||
self.builder.rcppkglist_populated()
|
||||
|
||||
self.builder.window_sensitive(True)
|
||||
view_model = view_tree.get_model()
|
||||
vpath = self.package_model.convert_path_to_vpath(view_model, path)
|
||||
view_tree.set_cursor(vpath)
|
||||
|
||||
def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
|
||||
# Click to include a package
|
||||
@@ -310,15 +249,11 @@ class PackageSelectionPage (HobPage):
|
||||
glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree):
|
||||
#after the fadeout the table will be sorted as before
|
||||
self.sort_column_id = self.package_model.sort_column_id
|
||||
self.sort_order = self.package_model.sort_order
|
||||
|
||||
self.package_model.resync_fadeout_column(self.package_model.get_iter_first())
|
||||
# Check out a model which base on the column COL_FADE_INC,
|
||||
# it's save the prev state of column COL_INC before do exclude_item
|
||||
filter = { PackageListModel.COL_FADE_INC : [True]}
|
||||
new_model = self.package_model.tree_model(filter, excluded_items_ahead=True)
|
||||
new_model = self.package_model.tree_model(filter)
|
||||
tree.set_model(new_model)
|
||||
tree.expand_all()
|
||||
|
||||
@@ -344,10 +279,8 @@ class PackageSelectionPage (HobPage):
|
||||
|
||||
cell.fadeout(tree, 1000, to_render_cells)
|
||||
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
|
||||
self.package_model.sort_column_id = self.sort_column_id
|
||||
self.package_model.sort_order = self.sort_order
|
||||
tree.set_model(self.package_model.tree_model(filter))
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree):
|
||||
tree.set_model(self.package_model.tree_model(self.pages[0]['filter']))
|
||||
tree.expand_all()
|
||||
|
||||
def set_packages_curr_tab(self, curr_page):
|
||||
|
||||
@@ -33,13 +33,11 @@ from bb.ui.crumbs.hobpages import HobPage
|
||||
class RecipeSelectionPage (HobPage):
|
||||
pages = [
|
||||
{
|
||||
'name' : 'Included recipes',
|
||||
'tooltip' : 'The recipes currently included for your image',
|
||||
'filter' : { RecipeListModel.COL_INC : [True],
|
||||
'name' : 'Included recipes',
|
||||
'tooltip' : 'The recipes currently included for your image',
|
||||
'filter' : { RecipeListModel.COL_INC : [True],
|
||||
RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] },
|
||||
'search' : 'Search recipes by name',
|
||||
'searchtip' : 'Enter a recipe name to find it',
|
||||
'columns' : [{
|
||||
'columns' : [{
|
||||
'col_name' : 'Recipe name',
|
||||
'col_id' : RecipeListModel.COL_NAME,
|
||||
'col_style': 'text',
|
||||
@@ -68,12 +66,10 @@ class RecipeSelectionPage (HobPage):
|
||||
'col_max' : 100
|
||||
}]
|
||||
}, {
|
||||
'name' : 'All recipes',
|
||||
'tooltip' : 'All recipes in your configured layers',
|
||||
'filter' : { RecipeListModel.COL_TYPE : ['recipe'] },
|
||||
'search' : 'Search recipes by name',
|
||||
'searchtip' : 'Enter a recipe name to find it',
|
||||
'columns' : [{
|
||||
'name' : 'All recipes',
|
||||
'tooltip' : 'All recipes in your configured layers',
|
||||
'filter' : { RecipeListModel.COL_TYPE : ['recipe'] },
|
||||
'columns' : [{
|
||||
'col_name' : 'Recipe name',
|
||||
'col_id' : RecipeListModel.COL_NAME,
|
||||
'col_style': 'text',
|
||||
@@ -102,12 +98,10 @@ class RecipeSelectionPage (HobPage):
|
||||
'col_max' : 100
|
||||
}]
|
||||
}, {
|
||||
'name' : 'Package Groups',
|
||||
'tooltip' : 'All package groups in your configured layers',
|
||||
'filter' : { RecipeListModel.COL_TYPE : ['packagegroup'] },
|
||||
'search' : 'Search package groups by name',
|
||||
'searchtip' : 'Enter a package group name to find it',
|
||||
'columns' : [{
|
||||
'name' : 'Package Groups',
|
||||
'tooltip' : 'All package groups in your configured layers',
|
||||
'filter' : { RecipeListModel.COL_TYPE : ['packagegroup'] },
|
||||
'columns' : [{
|
||||
'col_name' : 'Package group name',
|
||||
'col_id' : RecipeListModel.COL_NAME,
|
||||
'col_style': 'text',
|
||||
@@ -148,27 +142,26 @@ class RecipeSelectionPage (HobPage):
|
||||
# set visible members
|
||||
self.ins = HobNotebook()
|
||||
self.tables = [] # we need modify table when the dialog is shown
|
||||
|
||||
search_names = []
|
||||
search_tips = []
|
||||
# append the tabs in order
|
||||
for page in self.pages:
|
||||
columns = page['columns']
|
||||
name = page['name']
|
||||
tab = HobViewTable(columns, name)
|
||||
search_names.append(page['search'])
|
||||
search_tips.append(page['searchtip'])
|
||||
tab = HobViewTable(columns)
|
||||
filter = page['filter']
|
||||
sort_model = self.recipe_model.tree_model(filter, initial=True)
|
||||
tab.set_model(sort_model)
|
||||
tab.connect("toggled", self.table_toggled_cb, name)
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
|
||||
tab.set_model(self.recipe_model.tree_model(filter))
|
||||
tab.connect("toggled", self.table_toggled_cb, page['name'])
|
||||
if page['name'] == "Included recipes":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
self.ins.append_page(tab, page['name'], page['tooltip'])
|
||||
self.tables.append(tab)
|
||||
|
||||
self.ins.set_entry(search_names, search_tips)
|
||||
self.ins.search.connect("changed", self.search_entry_changed)
|
||||
self.ins.set_entry("Search recipes:")
|
||||
# set the search entry for each table
|
||||
for tab in self.tables:
|
||||
search_tip = "Enter a recipe's or task's name to find it"
|
||||
self.ins.search.set_tooltip_text(search_tip)
|
||||
self.ins.search.props.has_tooltip = True
|
||||
tab.set_search_entry(0, self.ins.search)
|
||||
|
||||
# add all into the window
|
||||
self.box_group_area.pack_start(self.ins, expand=True, fill=True)
|
||||
@@ -188,71 +181,22 @@ class RecipeSelectionPage (HobPage):
|
||||
self.back_button.connect("clicked", self.back_button_clicked_cb)
|
||||
button_box.pack_end(self.back_button, expand=False, fill=False)
|
||||
|
||||
def search_entry_changed(self, entry):
|
||||
text = entry.get_text()
|
||||
if self.ins.search_focus:
|
||||
self.ins.search_focus = False
|
||||
elif self.ins.page_changed:
|
||||
self.ins.page_change = False
|
||||
self.filter_search(entry)
|
||||
elif text not in self.ins.search_names:
|
||||
self.filter_search(entry)
|
||||
|
||||
def filter_search(self, entry):
|
||||
text = entry.get_text()
|
||||
current_tab = self.ins.get_current_page()
|
||||
filter = self.pages[current_tab]['filter']
|
||||
filter[RecipeListModel.COL_NAME] = text
|
||||
self.tables[current_tab].set_model(self.recipe_model.tree_model(filter, search_data=text))
|
||||
if self.recipe_model.filtered_nb == 0:
|
||||
if not self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
|
||||
self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
|
||||
self.ins.get_nth_page(current_tab).top_bar.show()
|
||||
self.ins.get_nth_page(current_tab).scroll.hide()
|
||||
else:
|
||||
if self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).top_bar.hide()
|
||||
self.ins.get_nth_page(current_tab).scroll.show()
|
||||
if entry.get_text() == '':
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
else:
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, True)
|
||||
|
||||
def button_click_cb(self, widget, event):
|
||||
path, col = widget.table_tree.get_cursor()
|
||||
tree_model = widget.table_tree.get_model()
|
||||
if path and col.get_title() != 'Included': # else activation is likely a removal
|
||||
properties = {'summary': '', 'name': '', 'version': '', 'revision': '', 'binb': '', 'group': '', 'license': '', 'homepage': '', 'bugtracker': '', 'description': ''}
|
||||
properties['summary'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_SUMMARY)
|
||||
properties['name'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_NAME)
|
||||
properties['version'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_VERSION)
|
||||
properties['revision'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_REVISION)
|
||||
properties['binb'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_BINB)
|
||||
properties['group'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_GROUP)
|
||||
properties['license'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_LIC)
|
||||
properties['homepage'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_HOMEPAGE)
|
||||
properties['bugtracker'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_BUGTRACKER)
|
||||
properties['description'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_DESC)
|
||||
self.builder.show_recipe_property_dialog(properties)
|
||||
if path: # else activation is likely a removal
|
||||
binb = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_BINB)
|
||||
if binb:
|
||||
self.builder.show_binb_dialog(binb)
|
||||
|
||||
def build_packages_clicked_cb(self, button):
|
||||
self.refresh_tables()
|
||||
self.builder.build_packages()
|
||||
|
||||
def refresh_tables(self):
|
||||
self.ins.reset_entry(self.ins.search, 0)
|
||||
for tab in self.tables:
|
||||
index = self.tables.index(tab)
|
||||
filter = self.pages[index]['filter']
|
||||
tab.set_model(self.recipe_model.tree_model(filter, search_data="", initial=True))
|
||||
|
||||
def back_button_clicked_cb(self, button):
|
||||
self.builder.recipe_model.set_selected_image(self.builder.configuration.initial_selected_image)
|
||||
self.builder.image_configuration_page.update_image_combo(self.builder.recipe_model, self.builder.configuration.initial_selected_image)
|
||||
self.builder.image_configuration_page.update_image_desc()
|
||||
self.builder.show_configuration()
|
||||
self.refresh_tables()
|
||||
|
||||
def refresh_selection(self):
|
||||
self.builder.configuration.selected_image = self.recipe_model.get_selected_image()
|
||||
@@ -263,9 +207,12 @@ class RecipeSelectionPage (HobPage):
|
||||
if not self.recipe_model.path_included(path):
|
||||
self.recipe_model.include_item(item_path=path, binb="User Selected", image_contents=False)
|
||||
else:
|
||||
self.pre_fadeout_checkout_include(view_tree, pagename)
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
if pagename == "Included recipes":
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
else:
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
|
||||
self.refresh_selection()
|
||||
if not self.builder.customized:
|
||||
@@ -275,10 +222,6 @@ class RecipeSelectionPage (HobPage):
|
||||
|
||||
self.builder.window_sensitive(True)
|
||||
|
||||
view_model = view_tree.get_model()
|
||||
vpath = self.recipe_model.convert_path_to_vpath(view_model, path)
|
||||
view_tree.set_cursor(vpath)
|
||||
|
||||
def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
|
||||
# Click to include a recipe
|
||||
self.builder.window_sensitive(False)
|
||||
@@ -286,11 +229,7 @@ class RecipeSelectionPage (HobPage):
|
||||
path = self.recipe_model.convert_vpath_to_path(view_model, view_path)
|
||||
glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree, pagename):
|
||||
#after the fadeout the table will be sorted as before
|
||||
self.sort_column_id = self.recipe_model.sort_column_id
|
||||
self.sort_order = self.recipe_model.sort_order
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree):
|
||||
#resync the included items to a backup fade include column
|
||||
it = self.recipe_model.get_iter_first()
|
||||
while it:
|
||||
@@ -299,14 +238,8 @@ class RecipeSelectionPage (HobPage):
|
||||
it = self.recipe_model.iter_next(it)
|
||||
# Check out a model which base on the column COL_FADE_INC,
|
||||
# it's save the prev state of column COL_INC before do exclude_item
|
||||
filter = { RecipeListModel.COL_FADE_INC:[True] }
|
||||
if pagename == "Included recipes":
|
||||
filter[RecipeListModel.COL_TYPE] = ['recipe', 'packagegroup']
|
||||
elif pagename == "All recipes":
|
||||
filter[RecipeListModel.COL_TYPE] = ['recipe']
|
||||
else:
|
||||
filter[RecipeListModel.COL_TYPE] = ['packagegroup']
|
||||
|
||||
filter = { RecipeListModel.COL_FADE_INC : [True],
|
||||
RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] }
|
||||
new_model = self.recipe_model.tree_model(filter, excluded_items_ahead=True)
|
||||
tree.set_model(new_model)
|
||||
|
||||
@@ -326,10 +259,8 @@ class RecipeSelectionPage (HobPage):
|
||||
|
||||
cell.fadeout(tree, 1000, to_render_cells)
|
||||
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
|
||||
self.recipe_model.sort_column_id = self.sort_column_id
|
||||
self.recipe_model.sort_order = self.sort_order
|
||||
tree.set_model(self.recipe_model.tree_model(filter))
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree):
|
||||
tree.set_model(self.recipe_model.tree_model(self.pages[0]['filter']))
|
||||
|
||||
def set_recipe_curr_tab(self, curr_page):
|
||||
self.ins.set_current_page(curr_page)
|
||||
|
||||
@@ -375,16 +375,10 @@ class RunningBuild (gobject.GObject):
|
||||
r = "R"
|
||||
else:
|
||||
r = ""
|
||||
|
||||
extra = ''
|
||||
if not event._reasons:
|
||||
if event._close_matches:
|
||||
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
|
||||
|
||||
if event._dependees:
|
||||
msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s\n" % (r, event._item, ", ".join(event._dependees), r, extra)
|
||||
msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)\n" % (r, event._item, ", ".join(event._dependees), r)
|
||||
else:
|
||||
msg = "Nothing %sPROVIDES '%s'%s\n" % (r, event._item, extra)
|
||||
msg = "Nothing %sPROVIDES '%s'\n" % (r, event._item)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
msg += ("%s\n" % reason)
|
||||
|
||||
211
bitbake/lib/bb/ui/crumbs/template.py
Normal file
211
bitbake/lib/bb/ui/crumbs/template.py
Normal file
@@ -0,0 +1,211 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011 Intel Corporation
|
||||
#
|
||||
# Authored by Shane Wang <shane.wang@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gobject
|
||||
import os
|
||||
import re
|
||||
|
||||
class File(gobject.GObject):
|
||||
|
||||
def __init__(self, pathfilename, suffix):
|
||||
if not pathfilename.endswith(suffix):
|
||||
pathfilename = "%s%s" % (pathfilename, suffix)
|
||||
gobject.GObject.__init__(self)
|
||||
self.pathfilename = pathfilename
|
||||
|
||||
def readFile(self):
|
||||
if not os.path.isfile(self.pathfilename):
|
||||
return None
|
||||
if not os.path.exists(self.pathfilename):
|
||||
return None
|
||||
|
||||
with open(self.pathfilename, 'r') as f:
|
||||
contents = f.readlines()
|
||||
f.close()
|
||||
|
||||
return contents
|
||||
|
||||
def writeFile(self, contents):
|
||||
if os.path.exists(self.pathfilename):
|
||||
orig = "%s.orig" % self.pathfilename
|
||||
if os.path.exists(orig):
|
||||
os.remove(orig)
|
||||
os.rename(self.pathfilename, orig)
|
||||
|
||||
with open(self.pathfilename, 'w') as f:
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
class ConfigFile(File):
|
||||
"""
|
||||
This object does save general config file. (say bblayers.conf, or local.conf). Again, it is the base class for other template files and image bb files.
|
||||
"""
|
||||
def __init__(self, pathfilename, suffix = None, header = None):
|
||||
if suffix:
|
||||
File.__init__(self, pathfilename, suffix)
|
||||
else:
|
||||
File.__init__(self, pathfilename, ".conf")
|
||||
if header:
|
||||
self.header = header
|
||||
else:
|
||||
self.header = "# Config generated by Hob\n\n"
|
||||
self.dictionary = {}
|
||||
|
||||
def setVar(self, var, val):
|
||||
if isinstance(val, list):
|
||||
liststr = ""
|
||||
if val:
|
||||
i = 0
|
||||
for value in val:
|
||||
if i < len(val) - 1:
|
||||
liststr += "%s " % value
|
||||
else:
|
||||
liststr += "%s" % value
|
||||
i += 1
|
||||
self.dictionary[var] = liststr
|
||||
else:
|
||||
self.dictionary[var] = val
|
||||
|
||||
def save(self):
|
||||
contents = self.header
|
||||
for var, val in self.dictionary.items():
|
||||
contents += "%s = \"%s\"\n" % (var, val)
|
||||
File.writeFile(self, contents)
|
||||
|
||||
class HobTemplateFile(ConfigFile):
|
||||
"""
|
||||
This object does save or load hob specific file.
|
||||
"""
|
||||
def __init__(self, pathfilename):
|
||||
ConfigFile.__init__(self, pathfilename, ".hob", "# Hob Template generated by Hob\n\n")
|
||||
|
||||
def getVar(self, var):
|
||||
if var in self.dictionary:
|
||||
return self.dictionary[var]
|
||||
else:
|
||||
return ""
|
||||
|
||||
def getVersion(self):
|
||||
contents = ConfigFile.readFile(self)
|
||||
|
||||
pattern = "^\s*(\S+)\s*=\s*(\".*?\")"
|
||||
|
||||
for line in contents:
|
||||
match = re.search(pattern, line)
|
||||
if match:
|
||||
if match.group(1) == "VERSION":
|
||||
return match.group(2).strip('"')
|
||||
return None
|
||||
|
||||
def load(self):
|
||||
contents = ConfigFile.readFile(self)
|
||||
self.dictionary.clear()
|
||||
|
||||
pattern = "^\s*(\S+)\s*=\s*(\".*?\")"
|
||||
|
||||
for line in contents:
|
||||
match = re.search(pattern, line)
|
||||
if match:
|
||||
var = match.group(1)
|
||||
val = match.group(2).strip('"')
|
||||
self.dictionary[var] = val
|
||||
return self.dictionary
|
||||
|
||||
class RecipeFile(ConfigFile):
|
||||
"""
|
||||
This object is for image bb file.
|
||||
"""
|
||||
def __init__(self, pathfilename):
|
||||
ConfigFile.__init__(self, pathfilename, ".bb", "# Recipe generated by Hob\n\ninherit core-image\n")
|
||||
|
||||
class TemplateMgr(gobject.GObject):
|
||||
|
||||
__gLocalVars__ = ["MACHINE", "PACKAGE_CLASSES", "DISTRO", "DL_DIR", "SSTATE_DIR", "SSTATE_MIRRORS", "PARALLEL_MAKE", "BB_NUMBER_THREADS", "CONF_VERSION"]
|
||||
__gBBLayersVars__ = ["BBLAYERS", "LCONF_VERSION"]
|
||||
__gRecipeVars__ = ["DEPENDS", "IMAGE_INSTALL"]
|
||||
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
self.template_hob = None
|
||||
self.bblayers_conf = None
|
||||
self.local_conf = None
|
||||
self.image_bb = None
|
||||
|
||||
@classmethod
|
||||
def convert_to_template_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "template-", filename, ".hob")
|
||||
|
||||
@classmethod
|
||||
def convert_to_bblayers_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "bblayers-", filename, ".conf")
|
||||
|
||||
@classmethod
|
||||
def convert_to_local_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "local-", filename, ".conf")
|
||||
|
||||
@classmethod
|
||||
def convert_to_image_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "hob-image-", filename, ".bb")
|
||||
|
||||
def open(self, filename, path):
|
||||
self.template_hob = HobTemplateFile(TemplateMgr.convert_to_template_pathfilename(filename, path))
|
||||
self.bblayers_conf = ConfigFile(TemplateMgr.convert_to_bblayers_pathfilename(filename, path))
|
||||
self.local_conf = ConfigFile(TemplateMgr.convert_to_local_pathfilename(filename, path))
|
||||
self.image_bb = RecipeFile(TemplateMgr.convert_to_image_pathfilename(filename, path))
|
||||
|
||||
def setVar(self, var, val):
|
||||
if var in TemplateMgr.__gLocalVars__:
|
||||
self.local_conf.setVar(var, val)
|
||||
if var in TemplateMgr.__gBBLayersVars__:
|
||||
self.bblayers_conf.setVar(var, val)
|
||||
if var in TemplateMgr.__gRecipeVars__:
|
||||
self.image_bb.setVar(var, val)
|
||||
|
||||
self.template_hob.setVar(var, val)
|
||||
|
||||
def save(self):
|
||||
self.local_conf.save()
|
||||
self.bblayers_conf.save()
|
||||
self.image_bb.save()
|
||||
self.template_hob.save()
|
||||
|
||||
def getVersion(self, path):
|
||||
return HobTemplateFile(path).getVersion()
|
||||
|
||||
def load(self, path):
|
||||
self.template_hob = HobTemplateFile(path)
|
||||
self.dictionary = self.template_hob.load()
|
||||
|
||||
def getVar(self, var):
|
||||
return self.template_hob.getVar(var)
|
||||
|
||||
def destroy(self):
|
||||
if self.template_hob:
|
||||
del self.template_hob
|
||||
template_hob = None
|
||||
if self.bblayers_conf:
|
||||
del self.bblayers_conf
|
||||
self.bblayers_conf = None
|
||||
if self.local_conf:
|
||||
del self.local_conf
|
||||
self.local_conf = None
|
||||
if self.image_bb:
|
||||
del self.image_bb
|
||||
self.image_bb = None
|
||||
@@ -154,7 +154,7 @@ class DepExplorer(gtk.Window):
|
||||
|
||||
def on_cursor_changed(self, selection):
|
||||
(model, it) = selection.get_selected()
|
||||
if it is None:
|
||||
if iter is None:
|
||||
current_package = None
|
||||
else:
|
||||
current_package = model.get_value(it, COL_PKG_NAME)
|
||||
@@ -163,17 +163,23 @@ class DepExplorer(gtk.Window):
|
||||
self.revdep_treeview.set_current_package(current_package)
|
||||
|
||||
|
||||
def parse(self, depgraph):
|
||||
for package in depgraph["pn"]:
|
||||
self.pkg_model.insert(0, (package,))
|
||||
def parse(depgraph, pkg_model, depends_model):
|
||||
for package in depgraph["pn"]:
|
||||
pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
|
||||
|
||||
for package in depgraph["depends"]:
|
||||
for depend in depgraph["depends"][package]:
|
||||
self.depends_model.insert (0, (TYPE_DEP, package, depend))
|
||||
for package in depgraph["depends"]:
|
||||
for depend in depgraph["depends"][package]:
|
||||
depends_model.set (depends_model.append(),
|
||||
COL_DEP_TYPE, TYPE_DEP,
|
||||
COL_DEP_PARENT, package,
|
||||
COL_DEP_PACKAGE, depend)
|
||||
|
||||
for package in depgraph["rdepends-pn"]:
|
||||
for rdepend in depgraph["rdepends-pn"][package]:
|
||||
self.depends_model.insert (0, (TYPE_RDEP, package, rdepend))
|
||||
for package in depgraph["rdepends-pn"]:
|
||||
for rdepend in depgraph["rdepends-pn"][package]:
|
||||
depends_model.set (depends_model.append(),
|
||||
COL_DEP_TYPE, TYPE_RDEP,
|
||||
COL_DEP_PARENT, package,
|
||||
COL_DEP_PACKAGE, rdepend)
|
||||
|
||||
|
||||
class gtkthread(threading.Thread):
|
||||
@@ -190,18 +196,16 @@ class gtkthread(threading.Thread):
|
||||
gtkthread.quit.set()
|
||||
|
||||
|
||||
def main(server, eventHandler, params):
|
||||
def main(server, eventHandler):
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if error:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
if not cmdline or cmdline[0] != "generateDotGraph":
|
||||
elif not cmdline or cmdline[0] != "generateDotGraph":
|
||||
print("This UI is only compatible with the -g option")
|
||||
return 1
|
||||
ret, error = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
|
||||
@@ -287,7 +291,7 @@ def main(server, eventHandler, params):
|
||||
|
||||
if isinstance(event, bb.event.DepTreeGenerated):
|
||||
gtk.gdk.threads_enter()
|
||||
dep.parse(event._depgraph)
|
||||
parse(event._depgraph, dep.pkg_model, dep.depends_model)
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
if isinstance(event, bb.command.CommandCompleted):
|
||||
@@ -314,7 +318,7 @@ def main(server, eventHandler, params):
|
||||
break
|
||||
if shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
print('Unable to cleanly stop: %s' % error)
|
||||
if shutdown == 0:
|
||||
|
||||
@@ -63,7 +63,7 @@ class MainWindow (gtk.Window):
|
||||
scrolled_window.add (self.cur_build_tv)
|
||||
|
||||
|
||||
def main (server, eventHandler, params):
|
||||
def main (server, eventHandler):
|
||||
gobject.threads_init()
|
||||
gtk.gdk.threads_init()
|
||||
|
||||
@@ -80,15 +80,13 @@ def main (server, eventHandler, params):
|
||||
running_build.connect ("build-failed", running_build_failed_cb)
|
||||
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if err:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
@@ -117,5 +115,5 @@ def main (server, eventHandler, params):
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
server.runCommand(["stateForceShutdown"])
|
||||
server.runCommand(["stateStop"])
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ from bb.ui.crumbs.hoblistmodel import RecipeListModel, PackageListModel
|
||||
from bb.ui.crumbs.hobeventhandler import HobHandler
|
||||
from bb.ui.crumbs.builder import Builder
|
||||
|
||||
featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES]
|
||||
extraCaches = ['bb.cache_extra:HobRecipeInfo']
|
||||
|
||||
def event_handle_idle_func(eventHandler, hobHandler):
|
||||
# Consume as many messages as we can in the time available to us
|
||||
@@ -58,21 +58,7 @@ def event_handle_idle_func(eventHandler, hobHandler):
|
||||
event = eventHandler.getEvent()
|
||||
return True
|
||||
|
||||
_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
|
||||
"bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
|
||||
"bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
|
||||
"bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
|
||||
"bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
|
||||
"bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
|
||||
"bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
|
||||
"bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
|
||||
"bb.event.SanityCheckPassed", "bb.event.SanityCheckFailed", "bb.event.PackageInfo",
|
||||
"bb.event.TargetsTreeGenerated", "bb.event.ConfigFilesFound", "bb.event.ConfigFilePathFound",
|
||||
"bb.event.FilesMatchingFound", "bb.event.NetworkTestFailed", "bb.event.NetworkTestPassed",
|
||||
"bb.event.BuildStarted", "bb.event.BuildCompleted", "bb.event.DiskFull"]
|
||||
|
||||
def main (server, eventHandler, params):
|
||||
params.updateFromServer(server)
|
||||
def main (server = None, eventHandler = None):
|
||||
gobject.threads_init()
|
||||
|
||||
# That indicates whether the Hob and the bitbake server are
|
||||
@@ -81,8 +67,6 @@ def main (server, eventHandler, params):
|
||||
recipe_model = RecipeListModel()
|
||||
package_model = PackageListModel()
|
||||
|
||||
llevel, debug_domains = bb.msg.constructLogOptions()
|
||||
server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
|
||||
hobHandler = HobHandler(server, recipe_model, package_model)
|
||||
builder = Builder(hobHandler, recipe_model, package_model)
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -201,11 +201,11 @@ class TerminalFilter(object):
|
||||
content = "No currently running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total)
|
||||
else:
|
||||
content = "Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total)
|
||||
print(content)
|
||||
print content
|
||||
lines = 1 + int(len(content) / (self.columns + 1))
|
||||
for tasknum, task in enumerate(tasks):
|
||||
content = "%s: %s" % (tasknum, task)
|
||||
print(content)
|
||||
print content
|
||||
lines = lines + 1 + int(len(content) / (self.columns + 1))
|
||||
self.footer_present = lines
|
||||
self.lastpids = runningpids[:]
|
||||
@@ -216,34 +216,21 @@ class TerminalFilter(object):
|
||||
fd = sys.stdin.fileno()
|
||||
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
|
||||
|
||||
def _log_settings_from_server(server):
|
||||
def main(server, eventHandler, tf = TerminalFilter):
|
||||
|
||||
# Get values of variables which control our output
|
||||
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return 1
|
||||
loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return 1
|
||||
consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return includelogs, loglines, consolelogfile
|
||||
|
||||
_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
|
||||
"bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
|
||||
"bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
|
||||
"bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
|
||||
"bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
|
||||
"bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
|
||||
"bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
|
||||
"bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent"]
|
||||
|
||||
def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
includelogs, loglines, consolelogfile = _log_settings_from_server(server)
|
||||
return 1
|
||||
|
||||
if sys.stdin.isatty() and sys.stdout.isatty():
|
||||
log_exec_tty = True
|
||||
@@ -258,12 +245,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
|
||||
if params.options.remote_server and params.options.kill_server:
|
||||
server.terminateServer()
|
||||
return
|
||||
|
||||
if consolelogfile and not params.options.show_environment:
|
||||
if consolelogfile:
|
||||
bb.utils.mkdirhier(os.path.dirname(consolelogfile))
|
||||
conlogformat = bb.msg.BBLogFormatter(format_str)
|
||||
consolelog = logging.FileHandler(consolelogfile)
|
||||
@@ -271,27 +253,24 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
consolelog.setFormatter(conlogformat)
|
||||
logger.addHandler(consolelog)
|
||||
|
||||
llevel, debug_domains = bb.msg.constructLogOptions()
|
||||
server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
|
||||
|
||||
if not params.observe_only:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
try:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if error:
|
||||
logger.error("Unable to get bitbake commandline arguments: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
|
||||
ret, error = server.runCommand(cmdline['action'])
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
logger.error("Command '%s' failed: %s" % (cmdline, error))
|
||||
return 1
|
||||
elif ret != True:
|
||||
logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
|
||||
return 1
|
||||
|
||||
except xmlrpclib.Fault as x:
|
||||
logger.error("XMLRPC Fault getting commandline:\n %s" % x)
|
||||
return 1
|
||||
|
||||
parseprogress = None
|
||||
cacheprogress = None
|
||||
@@ -306,10 +285,8 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
while True:
|
||||
try:
|
||||
event = eventHandler.waitEvent(0)
|
||||
if event is None:
|
||||
termfilter.updateFooter()
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
termfilter.updateFooter()
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
if event is None:
|
||||
if main.shutdown > 1:
|
||||
break
|
||||
@@ -323,7 +300,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
if log_exec_tty:
|
||||
tries = event.retries
|
||||
while tries:
|
||||
print("Trying to run: %s" % event.prog)
|
||||
print "Trying to run: %s" % event.prog
|
||||
if os.system(event.prog) == 0:
|
||||
break
|
||||
time.sleep(event.sleep_delay)
|
||||
@@ -340,7 +317,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
elif event.levelno == format.WARNING:
|
||||
warnings = warnings + 1
|
||||
# For "normal" logging conditions, don't show note logs from tasks
|
||||
# but do show them if the user has changed the default log level to
|
||||
# but do show them if the user has changed the default log level to
|
||||
# include verbose/debug messages
|
||||
if event.taskpid != 0 and event.levelno <= format.NOTE:
|
||||
continue
|
||||
@@ -405,9 +382,8 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
if isinstance(event, bb.command.CommandFailed):
|
||||
return_value = event.exitcode
|
||||
if event.error:
|
||||
errors = errors + 1
|
||||
logger.error("Command execution failed: %s", event.error)
|
||||
errors = errors + 1
|
||||
logger.error("Command execution failed: %s", event.error)
|
||||
main.shutdown = 2
|
||||
continue
|
||||
if isinstance(event, bb.command.CommandExit):
|
||||
@@ -431,15 +407,10 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
else:
|
||||
r = ""
|
||||
|
||||
extra = ''
|
||||
if not event._reasons:
|
||||
if event._close_matches:
|
||||
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
|
||||
|
||||
if event._dependees:
|
||||
logger.error("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s", r, event._item, ", ".join(event._dependees), r, extra)
|
||||
logger.error("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)", r, event._item, ", ".join(event._dependees), r)
|
||||
else:
|
||||
logger.error("Nothing %sPROVIDES '%s'%s", r, event._item, extra)
|
||||
logger.error("Nothing %sPROVIDES '%s'", r, event._item)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
logger.error("%s", reason)
|
||||
@@ -472,12 +443,8 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
event.taskid, event.taskstring, event.exitcode)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.DepTreeGenerated):
|
||||
continue
|
||||
|
||||
# ignore
|
||||
if isinstance(event, (bb.event.BuildBase,
|
||||
bb.event.MetadataEvent,
|
||||
bb.event.StampUpdate,
|
||||
bb.event.ConfigParsed,
|
||||
bb.event.RecipeParsed,
|
||||
@@ -486,8 +453,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
bb.runqueue.runQueueExitWait,
|
||||
bb.event.OperationStarted,
|
||||
bb.event.OperationCompleted,
|
||||
bb.event.OperationProgress,
|
||||
bb.event.DiskFull)):
|
||||
bb.event.OperationProgress)):
|
||||
continue
|
||||
|
||||
logger.error("Unknown event: %s", event)
|
||||
@@ -499,15 +465,12 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
termfilter.clearFooter()
|
||||
if params.observe_only:
|
||||
print("\nKeyboard Interrupt, exiting observer...")
|
||||
main.shutdown = 2
|
||||
if not params.observe_only and main.shutdown == 1:
|
||||
if main.shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
logger.error("Unable to cleanly stop: %s" % error)
|
||||
if not params.observe_only and main.shutdown == 0:
|
||||
if main.shutdown == 0:
|
||||
print("\nKeyboard Interrupt, closing down...\n")
|
||||
interrupted = True
|
||||
_, error = server.runCommand(["stateShutdown"])
|
||||
@@ -525,7 +488,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
if warnings:
|
||||
summary += pluralise("\nSummary: There was %s WARNING message shown.",
|
||||
"\nSummary: There were %s WARNING messages shown.", warnings)
|
||||
if return_value and errors:
|
||||
if return_value:
|
||||
summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.",
|
||||
"\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors)
|
||||
if summary:
|
||||
|
||||
@@ -196,7 +196,7 @@ class NCursesUI:
|
||||
# t.start()
|
||||
|
||||
#-------------------------------------------------------------------------#
|
||||
def main(self, stdscr, server, eventHandler, params):
|
||||
def main(self, stdscr, server, eventHandler):
|
||||
#-------------------------------------------------------------------------#
|
||||
height, width = stdscr.getmaxyx()
|
||||
|
||||
@@ -236,15 +236,13 @@ class NCursesUI:
|
||||
shutdown = 0
|
||||
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
return
|
||||
elif error:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
@@ -350,7 +348,7 @@ class NCursesUI:
|
||||
exitflag = True
|
||||
if shutdown == 1:
|
||||
mw.appendText("Second Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
print("Unable to cleanly stop: %s" % error)
|
||||
if shutdown == 0:
|
||||
|
||||
@@ -28,7 +28,7 @@ import socket, threading, pickle
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
|
||||
class BBUIEventQueue:
|
||||
def __init__(self, BBServer, clientinfo=("localhost, 0"), featureset=[]):
|
||||
def __init__(self, BBServer, clientinfo=("localhost, 0")):
|
||||
|
||||
self.eventQueue = []
|
||||
self.eventQueueLock = threading.Lock()
|
||||
@@ -44,10 +44,7 @@ class BBUIEventQueue:
|
||||
server.register_function( self.send_event, "event.sendpickle" )
|
||||
server.socket.settimeout(1)
|
||||
|
||||
self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port, featureset)
|
||||
|
||||
if (self.EventHandle == None):
|
||||
bb.fatal("Could not register UI event handler")
|
||||
self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port)
|
||||
|
||||
self.server = server
|
||||
|
||||
@@ -87,7 +84,6 @@ class BBUIEventQueue:
|
||||
|
||||
def startCallbackHandler(self):
|
||||
|
||||
self.server.timeout = 1
|
||||
while not self.server.quit:
|
||||
self.server.handle_request()
|
||||
self.server.server_close()
|
||||
|
||||
@@ -27,31 +27,17 @@ import bb
|
||||
import bb.msg
|
||||
import multiprocessing
|
||||
import fcntl
|
||||
import subprocess
|
||||
import glob
|
||||
import traceback
|
||||
import errno
|
||||
from commands import getstatusoutput
|
||||
from contextlib import contextmanager
|
||||
|
||||
logger = logging.getLogger("BitBake.Util")
|
||||
|
||||
def clean_context():
|
||||
return {
|
||||
"os": os,
|
||||
"bb": bb,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
def get_context():
|
||||
return _context
|
||||
|
||||
|
||||
def set_context(ctx):
|
||||
_context = ctx
|
||||
|
||||
# Context used in better_exec, eval
|
||||
_context = clean_context()
|
||||
_context = {
|
||||
"os": os,
|
||||
"bb": bb,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
def explode_version(s):
|
||||
r = []
|
||||
@@ -250,16 +236,14 @@ def _print_trace(body, line):
|
||||
"""
|
||||
Print the Environment of a Text Body
|
||||
"""
|
||||
error = []
|
||||
# print the environment of the method
|
||||
min_line = max(1, line-4)
|
||||
max_line = min(line + 4, len(body))
|
||||
for i in range(min_line, max_line + 1):
|
||||
for i in xrange(min_line, max_line + 1):
|
||||
if line == i:
|
||||
error.append(' *** %.4d:%s' % (i, body[i-1].rstrip()))
|
||||
logger.error(' *** %.4d:%s', i, body[i-1])
|
||||
else:
|
||||
error.append(' %.4d:%s' % (i, body[i-1].rstrip()))
|
||||
return error
|
||||
logger.error(' %.4d:%s', i, body[i-1])
|
||||
|
||||
def better_compile(text, file, realfile, mode = "exec"):
|
||||
"""
|
||||
@@ -269,78 +253,22 @@ def better_compile(text, file, realfile, mode = "exec"):
|
||||
try:
|
||||
return compile(text, file, mode)
|
||||
except Exception as e:
|
||||
error = []
|
||||
# split the text into lines again
|
||||
body = text.split('\n')
|
||||
error.append("Error in compiling python function in %s:\n" % realfile)
|
||||
logger.error("Error in compiling python function in %s", realfile)
|
||||
logger.error(str(e))
|
||||
if e.lineno:
|
||||
error.append("The code lines resulting in this error were:")
|
||||
error.extend(_print_trace(body, e.lineno))
|
||||
logger.error("The lines leading to this error were:")
|
||||
logger.error("\t%d:%s:'%s'", e.lineno, e.__class__.__name__, body[e.lineno-1])
|
||||
_print_trace(body, e.lineno)
|
||||
else:
|
||||
error.append("The function causing this error was:")
|
||||
logger.error("The function causing this error was:")
|
||||
for line in body:
|
||||
error.append(line)
|
||||
error.append("%s: %s" % (e.__class__.__name__, str(e)))
|
||||
|
||||
logger.error("\n".join(error))
|
||||
logger.error(line)
|
||||
|
||||
e = bb.BBHandledException(e)
|
||||
raise e
|
||||
|
||||
def _print_exception(t, value, tb, realfile, text, context):
|
||||
error = []
|
||||
try:
|
||||
exception = traceback.format_exception_only(t, value)
|
||||
error.append('Error executing a python function in %s:\n' % realfile)
|
||||
|
||||
# Strip 'us' from the stack (better_exec call)
|
||||
tb = tb.tb_next
|
||||
|
||||
textarray = text.split('\n')
|
||||
|
||||
linefailed = tb.tb_lineno
|
||||
|
||||
tbextract = traceback.extract_tb(tb)
|
||||
tbformat = traceback.format_list(tbextract)
|
||||
error.append("The stack trace of python calls that resulted in this exception/failure was:")
|
||||
error.append("File: '%s', lineno: %s, function: %s" % (tbextract[0][0], tbextract[0][1], tbextract[0][2]))
|
||||
error.extend(_print_trace(textarray, linefailed))
|
||||
|
||||
# See if this is a function we constructed and has calls back into other functions in
|
||||
# "text". If so, try and improve the context of the error by diving down the trace
|
||||
level = 0
|
||||
nexttb = tb.tb_next
|
||||
while nexttb is not None and (level+1) < len(tbextract):
|
||||
error.append("File: '%s', lineno: %s, function: %s" % (tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2]))
|
||||
if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
|
||||
# The code was possibly in the string we compiled ourselves
|
||||
error.extend(_print_trace(textarray, tbextract[level+1][1]))
|
||||
elif tbextract[level+1][0].startswith("/"):
|
||||
# The code looks like it might be in a file, try and load it
|
||||
try:
|
||||
with open(tbextract[level+1][0], "r") as f:
|
||||
text = f.readlines()
|
||||
error.extend(_print_trace(text, tbextract[level+1][1]))
|
||||
except:
|
||||
error.append(tbformat[level+1])
|
||||
elif "d" in context and tbextract[level+1][2]:
|
||||
# Try and find the code in the datastore based on the functionname
|
||||
d = context["d"]
|
||||
functionname = tbextract[level+1][2]
|
||||
text = d.getVar(functionname, True)
|
||||
if text:
|
||||
error.extend(_print_trace(text.split('\n'), tbextract[level+1][1]))
|
||||
else:
|
||||
error.append(tbformat[level+1])
|
||||
else:
|
||||
error.append(tbformat[level+1])
|
||||
nexttb = tb.tb_next
|
||||
level = level + 1
|
||||
|
||||
error.append("Exception: %s" % ''.join(exception))
|
||||
finally:
|
||||
logger.error("\n".join(error))
|
||||
|
||||
def better_exec(code, context, text = None, realfile = "<code>"):
|
||||
"""
|
||||
Similiar to better_compile, better_exec will
|
||||
@@ -353,25 +281,64 @@ def better_exec(code, context, text = None, realfile = "<code>"):
|
||||
if not hasattr(code, "co_filename"):
|
||||
code = better_compile(code, realfile, realfile)
|
||||
try:
|
||||
exec(code, get_context(), context)
|
||||
exec(code, _context, context)
|
||||
except Exception as e:
|
||||
(t, value, tb) = sys.exc_info()
|
||||
|
||||
if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
|
||||
raise
|
||||
try:
|
||||
_print_exception(t, value, tb, realfile, text, context)
|
||||
except Exception as e:
|
||||
logger.error("Exception handler error: %s" % str(e))
|
||||
|
||||
import traceback
|
||||
exception = traceback.format_exception_only(t, value)
|
||||
logger.error('Error executing a python function in %s:\n%s',
|
||||
realfile, ''.join(exception))
|
||||
|
||||
# Strip 'us' from the stack (better_exec call)
|
||||
tb = tb.tb_next
|
||||
|
||||
textarray = text.split('\n')
|
||||
linefailed = traceback.tb_lineno(tb)
|
||||
|
||||
tbextract = traceback.extract_tb(tb)
|
||||
tbformat = "\n".join(traceback.format_list(tbextract))
|
||||
logger.error("The stack trace of python calls that resulted in this exception/failure was:")
|
||||
for line in tbformat.split('\n'):
|
||||
logger.error(line)
|
||||
|
||||
logger.error("The code that was being executed was:")
|
||||
_print_trace(textarray, linefailed)
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[0][0], tbextract[0][1], tbextract[0][2])
|
||||
|
||||
# See if this is a function we constructed and has calls back into other functions in
|
||||
# "text". If so, try and improve the context of the error by diving down the trace
|
||||
level = 0
|
||||
nexttb = tb.tb_next
|
||||
while nexttb is not None and (level+1) < len(tbextract):
|
||||
if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
|
||||
_print_trace(textarray, tbextract[level+1][1])
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
|
||||
elif "d" in context and tbextract[level+1][2]:
|
||||
d = context["d"]
|
||||
functionname = tbextract[level+1][2]
|
||||
text = d.getVar(functionname, True)
|
||||
if text:
|
||||
_print_trace(text.split('\n'), tbextract[level+1][1])
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
nexttb = tb.tb_next
|
||||
level = level + 1
|
||||
|
||||
e = bb.BBHandledException(e)
|
||||
raise e
|
||||
|
||||
def simple_exec(code, context):
|
||||
exec(code, get_context(), context)
|
||||
exec(code, _context, context)
|
||||
|
||||
def better_eval(source, locals):
|
||||
return eval(source, get_context(), locals)
|
||||
return eval(source, _context, locals)
|
||||
|
||||
@contextmanager
|
||||
def fileslocked(files):
|
||||
@@ -427,10 +394,6 @@ def lockfile(name, shared=False, retry=True):
|
||||
return lf
|
||||
lf.close()
|
||||
except Exception:
|
||||
try:
|
||||
lf.close()
|
||||
except Exception:
|
||||
pass
|
||||
pass
|
||||
if not retry:
|
||||
return None
|
||||
@@ -460,9 +423,8 @@ def md5_file(filename):
|
||||
import md5
|
||||
m = md5.new()
|
||||
|
||||
with open(filename, "rb") as f:
|
||||
for line in f:
|
||||
m.update(line)
|
||||
for line in open(filename):
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
|
||||
def sha256_file(filename):
|
||||
@@ -478,9 +440,8 @@ def sha256_file(filename):
|
||||
return None
|
||||
|
||||
s = hashlib.sha256()
|
||||
with open(filename, "rb") as f:
|
||||
for line in f:
|
||||
s.update(line)
|
||||
for line in open(filename):
|
||||
s.update(line)
|
||||
return s.hexdigest()
|
||||
|
||||
def preserved_envvars_exported():
|
||||
@@ -497,6 +458,27 @@ def preserved_envvars_exported():
|
||||
'USER',
|
||||
]
|
||||
|
||||
def preserved_envvars_exported_interactive():
|
||||
"""Variables which are taken from the environment and placed in and exported
|
||||
from the metadata, for interactive tasks"""
|
||||
return [
|
||||
'COLORTERM',
|
||||
'DBUS_SESSION_BUS_ADDRESS',
|
||||
'DESKTOP_SESSION',
|
||||
'DESKTOP_STARTUP_ID',
|
||||
'DISPLAY',
|
||||
'GNOME_KEYRING_PID',
|
||||
'GNOME_KEYRING_SOCKET',
|
||||
'GPG_AGENT_INFO',
|
||||
'GTK_RC_FILES',
|
||||
'SESSION_MANAGER',
|
||||
'KRB5CCNAME',
|
||||
'SSH_AUTH_SOCK',
|
||||
'XAUTHORITY',
|
||||
'XDG_DATA_DIRS',
|
||||
'XDG_SESSION_COOKIE',
|
||||
]
|
||||
|
||||
def preserved_envvars():
|
||||
"""Variables which are taken from the environment and placed in the metadata"""
|
||||
v = [
|
||||
@@ -505,7 +487,7 @@ def preserved_envvars():
|
||||
'BB_ENV_WHITELIST',
|
||||
'BB_ENV_EXTRAWHITE',
|
||||
]
|
||||
return v + preserved_envvars_exported()
|
||||
return v + preserved_envvars_exported() + preserved_envvars_exported_interactive()
|
||||
|
||||
def filter_environment(good_vars):
|
||||
"""
|
||||
@@ -513,37 +495,36 @@ def filter_environment(good_vars):
|
||||
are not known and may influence the build in a negative way.
|
||||
"""
|
||||
|
||||
removed_vars = {}
|
||||
removed_vars = []
|
||||
for key in os.environ.keys():
|
||||
if key in good_vars:
|
||||
continue
|
||||
|
||||
removed_vars[key] = os.environ[key]
|
||||
removed_vars.append(key)
|
||||
os.unsetenv(key)
|
||||
del os.environ[key]
|
||||
|
||||
if len(removed_vars):
|
||||
logger.debug(1, "Removed the following variables from the environment: %s", ", ".join(removed_vars.keys()))
|
||||
logger.debug(1, "Removed the following variables from the environment: %s", ", ".join(removed_vars))
|
||||
|
||||
return removed_vars
|
||||
|
||||
def create_interactive_env(d):
|
||||
for k in preserved_envvars_exported_interactive():
|
||||
os.setenv(k, d.getVar(k, True))
|
||||
|
||||
def approved_variables():
|
||||
"""
|
||||
Determine and return the list of whitelisted variables which are approved
|
||||
to remain in the envrionment.
|
||||
"""
|
||||
if 'BB_PRESERVE_ENV' in os.environ:
|
||||
return os.environ.keys()
|
||||
approved = []
|
||||
if 'BB_ENV_WHITELIST' in os.environ:
|
||||
approved = os.environ['BB_ENV_WHITELIST'].split()
|
||||
approved.extend(['BB_ENV_WHITELIST'])
|
||||
else:
|
||||
approved = preserved_envvars()
|
||||
if 'BB_ENV_EXTRAWHITE' in os.environ:
|
||||
approved.extend(os.environ['BB_ENV_EXTRAWHITE'].split())
|
||||
if 'BB_ENV_EXTRAWHITE' not in approved:
|
||||
approved.extend(['BB_ENV_EXTRAWHITE'])
|
||||
return approved
|
||||
|
||||
def clean_environment():
|
||||
@@ -553,9 +534,7 @@ def clean_environment():
|
||||
"""
|
||||
if 'BB_PRESERVE_ENV' not in os.environ:
|
||||
good_vars = approved_variables()
|
||||
return filter_environment(good_vars)
|
||||
|
||||
return {}
|
||||
filter_environment(good_vars)
|
||||
|
||||
def empty_environment():
|
||||
"""
|
||||
@@ -579,15 +558,14 @@ def remove(path, recurse=False):
|
||||
"""Equivalent to rm -f or rm -rf"""
|
||||
if not path:
|
||||
return
|
||||
if recurse:
|
||||
# shutil.rmtree(name) would be ideal but its too slow
|
||||
subprocess.call(['rm', '-rf'] + glob.glob(path))
|
||||
return
|
||||
import os, errno, shutil, glob
|
||||
for name in glob.glob(path):
|
||||
try:
|
||||
os.unlink(name)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
if recurse and exc.errno == errno.EISDIR:
|
||||
shutil.rmtree(name)
|
||||
elif exc.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
def prunedir(topdir):
|
||||
@@ -724,7 +702,7 @@ def copyfile(src, dest, newmtime = None, sstat = None):
|
||||
if not sstat:
|
||||
sstat = os.lstat(src)
|
||||
except Exception as e:
|
||||
logger.warn("copyfile: stat of %s failed (%s)" % (src, e))
|
||||
print("copyfile: Stating source file failed...", e)
|
||||
return False
|
||||
|
||||
destexists = 1
|
||||
@@ -751,7 +729,7 @@ def copyfile(src, dest, newmtime = None, sstat = None):
|
||||
#os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
|
||||
return os.lstat(dest)
|
||||
except Exception as e:
|
||||
logger.warn("copyfile: failed to create symlink %s to %s (%s)" % (dest, target, e))
|
||||
print("copyfile: failed to properly create symlink:", dest, "->", target, e)
|
||||
return False
|
||||
|
||||
if stat.S_ISREG(sstat[stat.ST_MODE]):
|
||||
@@ -766,7 +744,7 @@ def copyfile(src, dest, newmtime = None, sstat = None):
|
||||
shutil.copyfile(src, dest + "#new")
|
||||
os.rename(dest + "#new", dest)
|
||||
except Exception as e:
|
||||
logger.warn("copyfile: copy %s to %s failed (%s)" % (src, dest, e))
|
||||
print('copyfile: copy', src, '->', dest, 'failed.', e)
|
||||
return False
|
||||
finally:
|
||||
if srcchown:
|
||||
@@ -777,13 +755,13 @@ def copyfile(src, dest, newmtime = None, sstat = None):
|
||||
#we don't yet handle special, so we need to fall back to /bin/mv
|
||||
a = getstatusoutput("/bin/cp -f " + "'" + src + "' '" + dest + "'")
|
||||
if a[0] != 0:
|
||||
logger.warn("copyfile: failed to copy special file %s to %s (%s)" % (src, dest, a))
|
||||
print("copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a)
|
||||
return False # failure
|
||||
try:
|
||||
os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])
|
||||
os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
|
||||
except Exception as e:
|
||||
logger.warn("copyfile: failed to chown/chmod %s (%s)" % (dest, e))
|
||||
print("copyfile: Failed to chown/chmod/unlink", dest, e)
|
||||
return False
|
||||
|
||||
if newmtime:
|
||||
@@ -793,28 +771,22 @@ def copyfile(src, dest, newmtime = None, sstat = None):
|
||||
newmtime = sstat[stat.ST_MTIME]
|
||||
return newmtime
|
||||
|
||||
def which(path, item, direction = 0, history = False):
|
||||
def which(path, item, direction = 0):
|
||||
"""
|
||||
Locate a file in a PATH
|
||||
"""
|
||||
|
||||
hist = []
|
||||
paths = (path or "").split(':')
|
||||
if direction != 0:
|
||||
paths.reverse()
|
||||
|
||||
for p in paths:
|
||||
next = os.path.join(p, item)
|
||||
hist.append(next)
|
||||
if os.path.exists(next):
|
||||
if not os.path.isabs(next):
|
||||
next = os.path.abspath(next)
|
||||
if history:
|
||||
return next, hist
|
||||
return next
|
||||
|
||||
if history:
|
||||
return "", hist
|
||||
return ""
|
||||
|
||||
def to_boolean(string, default=None):
|
||||
@@ -848,28 +820,3 @@ def cpu_count():
|
||||
def nonblockingfd(fd):
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
|
||||
def process_profilelog(fn):
|
||||
# Redirect stdout to capture profile information
|
||||
pout = open(fn + '.processed', 'w')
|
||||
so = sys.stdout.fileno()
|
||||
orig_so = os.dup(sys.stdout.fileno())
|
||||
os.dup2(pout.fileno(), so)
|
||||
|
||||
import pstats
|
||||
p = pstats.Stats(fn)
|
||||
p.sort_stats('time')
|
||||
p.print_stats()
|
||||
p.print_callers()
|
||||
p.sort_stats('cumulative')
|
||||
p.print_stats()
|
||||
|
||||
os.dup2(orig_so, so)
|
||||
pout.flush()
|
||||
pout.close()
|
||||
|
||||
#
|
||||
# Was present to work around multiprocessing pool bugs in python < 2.7.3
|
||||
#
|
||||
def multiprocessingpool(*args, **kwargs):
|
||||
return multiprocessing.Pool(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@ def init_logger(logfile, loglevel):
|
||||
FORMAT = '%(asctime)-15s %(message)s'
|
||||
logging.basicConfig(level=numeric_level, filename=logfile, format=FORMAT)
|
||||
|
||||
class NotFoundError(Exception):
|
||||
class NotFoundError(StandardError):
|
||||
pass
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
import os.path
|
||||
import errno
|
||||
import prserv
|
||||
import time
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
@@ -15,7 +14,7 @@ sqlversion = sqlite3.sqlite_version_info
|
||||
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
|
||||
raise Exception("sqlite3 version 3.3.0 or later is required.")
|
||||
|
||||
class PRTable(object):
|
||||
class PRTable():
|
||||
def __init__(self, conn, table, nohist):
|
||||
self.conn = conn
|
||||
self.nohist = nohist
|
||||
@@ -33,20 +32,16 @@ class PRTable(object):
|
||||
|
||||
def _execute(self, *query):
|
||||
"""Execute a query, waiting to acquire a lock if necessary"""
|
||||
start = time.time()
|
||||
end = start + 20
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
return self.conn.execute(*query)
|
||||
except sqlite3.OperationalError as exc:
|
||||
if 'is locked' in str(exc) and end > time.time():
|
||||
if 'database is locked' in str(exc) and count < 500:
|
||||
count = count + 1
|
||||
continue
|
||||
raise exc
|
||||
|
||||
def sync(self):
|
||||
self.conn.commit()
|
||||
self._execute("BEGIN EXCLUSIVE TRANSACTION")
|
||||
|
||||
def _getValueHist(self, version, pkgarch, checksum):
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
@@ -56,9 +51,11 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table,self.table),
|
||||
(version,pkgarch, checksum,version, pkgarch))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -82,9 +79,11 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table,self.table),
|
||||
(version, pkgarch, checksum, version, pkgarch))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
self.conn.rollback()
|
||||
@@ -113,8 +112,10 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum, value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -128,14 +129,18 @@ class PRTable(object):
|
||||
def _importNohist(self, version, pkgarch, checksum, value):
|
||||
try:
|
||||
#try to insert
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum,value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
#already have the record, try to update
|
||||
try:
|
||||
self._execute("BEGIN")
|
||||
self._execute("UPDATE %s SET value=? WHERE version=? AND pkgarch=? AND checksum=? AND value<?"
|
||||
% (self.table),
|
||||
(value,version,pkgarch,checksum,value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -218,7 +223,7 @@ class PRData(object):
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise e
|
||||
self.connection=sqlite3.connect(self.filename, isolation_level="EXCLUSIVE", check_same_thread = False)
|
||||
self.connection=sqlite3.connect(self.filename, isolation_level="DEFERRED")
|
||||
self.connection.row_factory=sqlite3.Row
|
||||
self._tables={}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import os,sys,logging
|
||||
import signal, time, atexit, threading
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
import xmlrpclib
|
||||
import threading
|
||||
import Queue
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
@@ -33,17 +31,19 @@ class Handler(SimpleXMLRPCRequestHandler):
|
||||
PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
|
||||
singleton = None
|
||||
|
||||
|
||||
class PRServer(SimpleXMLRPCServer):
|
||||
def __init__(self, dbfile, logfile, interface, daemon=True):
|
||||
''' constructor '''
|
||||
SimpleXMLRPCServer.__init__(self, interface,
|
||||
requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=False, allow_none=True)
|
||||
self.dbfile=dbfile
|
||||
self.daemon=daemon
|
||||
self.logfile=logfile
|
||||
self.working_thread=None
|
||||
self.host, self.port = self.socket.getsockname()
|
||||
self.db=prserv.db.PRData(dbfile)
|
||||
self.table=self.db["PRMAIN"]
|
||||
self.pidfile=PIDPREFIX % (self.host, self.port)
|
||||
|
||||
self.register_function(self.getPR, "getPR")
|
||||
@@ -53,32 +53,6 @@ class PRServer(SimpleXMLRPCServer):
|
||||
self.register_function(self.importone, "importone")
|
||||
self.register_introspection_functions()
|
||||
|
||||
self.db = prserv.db.PRData(self.dbfile)
|
||||
self.table = self.db["PRMAIN"]
|
||||
|
||||
self.requestqueue = Queue.Queue()
|
||||
self.handlerthread = threading.Thread(target = self.process_request_thread)
|
||||
self.handlerthread.daemon = False
|
||||
|
||||
def process_request_thread(self):
|
||||
"""Same as in BaseServer but as a thread.
|
||||
|
||||
In addition, exception handling is done here.
|
||||
|
||||
"""
|
||||
while True:
|
||||
(request, client_address) = self.requestqueue.get()
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
except:
|
||||
self.handle_error(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
self.table.sync()
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
self.requestqueue.put((request, client_address))
|
||||
|
||||
def export(self, version=None, pkgarch=None, checksum=None, colinfo=True):
|
||||
try:
|
||||
return self.table.export(version, pkgarch, checksum, colinfo)
|
||||
@@ -112,24 +86,27 @@ class PRServer(SimpleXMLRPCServer):
|
||||
def work_forever(self,):
|
||||
self.quit = False
|
||||
self.timeout = 0.5
|
||||
|
||||
logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
|
||||
logger.info("PRServer: started! DBfile: %s, IP: %s, PORT: %s, PID: %s" %
|
||||
(self.dbfile, self.host, self.port, str(os.getpid())))
|
||||
|
||||
self.handlerthread.start()
|
||||
while not self.quit:
|
||||
self.handle_request()
|
||||
|
||||
self.table.sync()
|
||||
logger.info("PRServer: stopping...")
|
||||
self.server_close()
|
||||
return
|
||||
|
||||
def start(self):
|
||||
pid = self.daemonize()
|
||||
# Ensure both the parent sees this and the child from the work_forever log entry above
|
||||
logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
|
||||
(self.dbfile, self.host, self.port, str(pid)))
|
||||
if self.daemon is True:
|
||||
logger.info("PRServer: try to start daemon...")
|
||||
self.daemonize()
|
||||
else:
|
||||
atexit.register(self.delpid)
|
||||
pid = str(os.getpid())
|
||||
pf = file(self.pidfile, 'w+')
|
||||
pf.write("%s\n" % pid)
|
||||
pf.close()
|
||||
self.work_forever()
|
||||
|
||||
def delpid(self):
|
||||
os.remove(self.pidfile)
|
||||
@@ -141,9 +118,8 @@ class PRServer(SimpleXMLRPCServer):
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
os.waitpid(pid, 0)
|
||||
#parent return instead of exit to give control
|
||||
return pid
|
||||
return
|
||||
except OSError as e:
|
||||
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
@@ -155,7 +131,7 @@ class PRServer(SimpleXMLRPCServer):
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0: #parent
|
||||
os._exit(0)
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
@@ -171,55 +147,53 @@ class PRServer(SimpleXMLRPCServer):
|
||||
os.dup2(so.fileno(),sys.stdout.fileno())
|
||||
os.dup2(se.fileno(),sys.stderr.fileno())
|
||||
|
||||
# Clear out all log handlers prior to the fork() to avoid calling
|
||||
# event handlers not part of the PRserver
|
||||
for logger_iter in logging.Logger.manager.loggerDict.keys():
|
||||
logging.getLogger(logger_iter).handlers = []
|
||||
|
||||
# Ensure logging makes it to the logfile
|
||||
streamhandler = logging.StreamHandler()
|
||||
streamhandler.setLevel(logging.DEBUG)
|
||||
formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
streamhandler.setFormatter(formatter)
|
||||
logger.addHandler(streamhandler)
|
||||
|
||||
# write pidfile
|
||||
atexit.register(self.delpid)
|
||||
pid = str(os.getpid())
|
||||
pf = file(self.pidfile, 'w')
|
||||
pf.write("%s\n" % pid)
|
||||
pf.close()
|
||||
|
||||
self.work_forever()
|
||||
self.delpid()
|
||||
os._exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
class PRServSingleton(object):
|
||||
class PRServSingleton():
|
||||
def __init__(self, dbfile, logfile, interface):
|
||||
self.dbfile = dbfile
|
||||
self.logfile = logfile
|
||||
self.interface = interface
|
||||
self.host = None
|
||||
self.port = None
|
||||
self.event = threading.Event()
|
||||
|
||||
def _work(self):
|
||||
self.prserv = PRServer(self.dbfile, self.logfile, self.interface, False)
|
||||
self.host, self.port = self.prserv.getinfo()
|
||||
self.event.set()
|
||||
self.prserv.work_forever()
|
||||
del self.prserv.db
|
||||
|
||||
def start(self):
|
||||
self.prserv = PRServer(self.dbfile, self.logfile, self.interface)
|
||||
self.prserv.start()
|
||||
self.host, self.port = self.prserv.getinfo()
|
||||
self.working_thread = threading.Thread(target=self._work)
|
||||
self.working_thread.start()
|
||||
|
||||
def getinfo(self):
|
||||
self.event.wait()
|
||||
return (self.host, self.port)
|
||||
|
||||
class PRServerConnection(object):
|
||||
class PRServerConnection():
|
||||
def __init__(self, host, port):
|
||||
if is_local_special(host, port):
|
||||
host, port = singleton.getinfo()
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.connection, self.transport = bb.server.xmlrpc._create_server(self.host, self.port)
|
||||
self.connection = bb.server.xmlrpc._create_server(self.host, self.port)
|
||||
|
||||
def terminate(self):
|
||||
# Don't wait for server indefinitely
|
||||
import socket
|
||||
socket.setdefaulttimeout(2)
|
||||
try:
|
||||
logger.info("Terminating PRServer...")
|
||||
self.connection.quit()
|
||||
except Exception as exc:
|
||||
sys.stderr.write("%s\n" % str(exc))
|
||||
@@ -236,9 +210,6 @@ class PRServerConnection(object):
|
||||
def importone(self, version, pkgarch, checksum, value):
|
||||
return self.connection.importone(version, pkgarch, checksum, value)
|
||||
|
||||
def getinfo(self):
|
||||
return self.host, self.port
|
||||
|
||||
def start_daemon(dbfile, host, port, logfile):
|
||||
pidfile = PIDPREFIX % (host, port)
|
||||
try:
|
||||
@@ -295,27 +266,17 @@ def is_local_special(host, port):
|
||||
else:
|
||||
return False
|
||||
|
||||
class PRServiceConfigError(Exception):
|
||||
pass
|
||||
|
||||
def auto_start(d):
|
||||
global singleton
|
||||
if (not d.getVar('PRSERV_HOST', True)) or (not d.getVar('PRSERV_PORT', True)):
|
||||
return True
|
||||
|
||||
host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
|
||||
if not host_params:
|
||||
return None
|
||||
|
||||
if len(host_params) != 2:
|
||||
logger.critical('\n'.join(['PRSERV_HOST: incorrect format',
|
||||
'Usage: PRSERV_HOST = "<hostname>:<port>"']))
|
||||
raise PRServiceConfigError
|
||||
|
||||
if is_local_special(host_params[0], int(host_params[1])) and not singleton:
|
||||
if is_local_special(d.getVar('PRSERV_HOST', True), int(d.getVar('PRSERV_PORT', True))) and not singleton:
|
||||
import bb.utils
|
||||
cachedir = (d.getVar("PERSISTENT_DIR", True) or d.getVar("CACHE", True))
|
||||
if not cachedir:
|
||||
logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
|
||||
raise PRServiceConfigError
|
||||
sys.exit(1)
|
||||
bb.utils.mkdirhier(cachedir)
|
||||
dbfile = os.path.join(cachedir, "prserv.sqlite3")
|
||||
logfile = os.path.join(cachedir, "prserv.log")
|
||||
@@ -324,18 +285,14 @@ def auto_start(d):
|
||||
if singleton:
|
||||
host, port = singleton.getinfo()
|
||||
else:
|
||||
host = host_params[0]
|
||||
port = int(host_params[1])
|
||||
host = d.getVar('PRSERV_HOST', True)
|
||||
port = int(d.getVar('PRSERV_PORT', True))
|
||||
|
||||
try:
|
||||
connection = PRServerConnection(host,port)
|
||||
connection.ping()
|
||||
realhost, realport = connection.getinfo()
|
||||
return str(realhost) + ":" + str(realport)
|
||||
|
||||
return PRServerConnection(host,port).ping()
|
||||
except Exception:
|
||||
logger.critical("PRservice %s:%d not available" % (host, port))
|
||||
raise PRServiceConfigError
|
||||
return False
|
||||
|
||||
def auto_shutdown(d=None):
|
||||
global singleton
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
# or the mega-manual (single, large HTML file comprised of all
|
||||
# Yocto Project manuals).
|
||||
# html: generates an HTML version of a manual.
|
||||
# eclipse: generates an HTML version of a manual that can be used as
|
||||
# eclipse help (including necessary metadata files).
|
||||
# tarball: creates a tarball for the doc files.
|
||||
# validate: validates
|
||||
# publish: pushes generated files to the Yocto Project website
|
||||
@@ -72,19 +70,26 @@
|
||||
#
|
||||
|
||||
ifeq ($(DOC),bsp-guide)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
TARFILES = bsp-style.css bsp-guide.html bsp-guide.pdf figures/bsp-title.png \
|
||||
eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
XSLTOPTS = --stringparam html.stylesheet bsp-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
TARFILES = bsp-style.css bsp-guide.html bsp-guide.pdf figures/bsp-title.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(DOC),dev-manual)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
XSLTOPTS = --stringparam html.stylesheet dev-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
#
|
||||
# Note that the tarfile might produce the "Cannot stat: No such file or directory" error
|
||||
# message for .PNG files that are not present when building a particular branch. The
|
||||
@@ -115,24 +120,21 @@ TARFILES = dev-style.css dev-manual.html dev-manual.pdf \
|
||||
figures/app-dev-flow.png figures/bsp-dev-flow.png figures/dev-title.png \
|
||||
figures/git-workflow.png figures/index-downloads.png figures/kernel-dev-flow.png \
|
||||
figures/kernel-overview-1.png figures/kernel-overview-2-generic.png \
|
||||
figures/source-repos.png figures/yp-download.png \
|
||||
eclipse
|
||||
figures/source-repos.png figures/yp-download.png
|
||||
endif
|
||||
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(DOC),yocto-project-qs)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html eclipse tarball
|
||||
TARFILES = yocto-project-qs.html qs-style.css figures/yocto-environment.png \
|
||||
figures/building-an-image.png figures/using-a-pre-built-image.png \
|
||||
figures/yocto-project-transp.png \
|
||||
eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/eclipse
|
||||
XSLTOPTS = --stringparam html.stylesheet qs-style.css \
|
||||
--xinclude
|
||||
ALLPREQ = html tarball
|
||||
TARFILES = yocto-project-qs.html qs-style.css figures/yocto-environment.png figures/building-an-image.png figures/using-a-pre-built-image.png figures/yocto-project-transp.png
|
||||
MANUALS = $(DOC)/$(DOC).html
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
@@ -176,32 +178,11 @@ TARFILES = mega-manual.html mega-style.css figures/yocto-environment.png figures
|
||||
figures/using-a-pre-built-image.png \
|
||||
figures/poky-title.png figures/buildhistory.png figures/buildhistory-web.png \
|
||||
figures/adt-title.png figures/bsp-title.png \
|
||||
figures/kernel-dev-title.png figures/kernel-architecture-overview.png \
|
||||
figures/kernel-title.png figures/kernel-architecture-overview.png \
|
||||
figures/app-dev-flow.png figures/bsp-dev-flow.png figures/dev-title.png \
|
||||
figures/git-workflow.png figures/index-downloads.png figures/kernel-dev-flow.png \
|
||||
figures/kernel-overview-1.png figures/kernel-overview-2-generic.png \
|
||||
figures/source-repos.png figures/yp-download.png \
|
||||
figures/profile-title.png figures/kernelshark-all.png \
|
||||
figures/kernelshark-choose-events.png figures/kernelshark-i915-display.png \
|
||||
figures/kernelshark-output-display.png figures/lttngmain0.png \
|
||||
figures/oprofileui-busybox.png figures/oprofileui-copy-to-user.png \
|
||||
figures/oprofileui-downloading.png figures/oprofileui-processes.png \
|
||||
figures/perf-probe-do_fork-profile.png figures/perf-report-cycles-u.png \
|
||||
figures/perf-systemwide.png figures/perf-systemwide-libc.png \
|
||||
figures/perf-wget-busybox-annotate-menu.png figures/perf-wget-busybox-annotate-udhcpc.png \
|
||||
figures/perf-wget-busybox-debuginfo.png figures/perf-wget-busybox-dso-zoom.png \
|
||||
figures/perf-wget-busybox-dso-zoom-menu.png figures/perf-wget-busybox-expanded-stripped.png \
|
||||
figures/perf-wget-flat-stripped.png figures/perf-wget-g-copy-from-user-expanded-stripped.png \
|
||||
figures/perf-wget-g-copy-to-user-expanded-debuginfo.png figures/perf-wget-g-copy-to-user-expanded-stripped.png \
|
||||
figures/perf-wget-g-copy-to-user-expanded-stripped-unresolved-hidden.png figures/pybootchartgui-linux-yocto.png \
|
||||
figures/pychart-linux-yocto-rpm.png figures/pychart-linux-yocto-rpm-nostrip.png \
|
||||
figures/sched-wakeup-profile.png figures/sysprof-callers.png \
|
||||
figures/sysprof-copy-from-user.png figures/sysprof-copy-to-user.png figures/cross-development-toolchains.png \
|
||||
figures/yocto-environment-ref.png figures/user-configuration.png figures/source-input.png \
|
||||
figures/package-feeds.png figures/layer-input.png figures/images.png figures/sdk.png \
|
||||
figures/source-fetching.png figures/patching.png figures/configuration-compile-autoreconf.png \
|
||||
figures/analysis-for-package-splitting.png figures/image-generation.png \
|
||||
figures/sdk-generation.png
|
||||
figures/source-repos.png figures/yp-download.png figures/kernel-dev-title.png
|
||||
endif
|
||||
|
||||
MANUALS = $(DOC)/$(DOC).html
|
||||
@@ -211,66 +192,59 @@ STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
|
||||
ifeq ($(DOC),ref-manual)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
XSLTOPTS = --stringparam html.stylesheet ref-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam appendix.autolabel A \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
TARFILES = ref-manual.html ref-style.css figures/poky-title.png \
|
||||
figures/buildhistory.png figures/buildhistory-web.png eclipse \
|
||||
figures/cross-development-toolchains.png figures/layer-input.png \
|
||||
figures/package-feeds.png figures/source-input.png \
|
||||
figures/user-configuration.png figures/yocto-environment-ref.png \
|
||||
figures/images.png figures/sdk.png figures/source-fetching.png \
|
||||
figures/patching.png figures/configuration-compile-autoreconf.png \
|
||||
figures/analysis-for-package-splitting.png figures/image-generation.png \
|
||||
figures/sdk-generation.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
figures/buildhistory.png figures/buildhistory-web.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(DOC),adt-manual)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
TARFILES = adt-manual.html adt-manual.pdf adt-style.css figures/adt-title.png \
|
||||
eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
XSLTOPTS = --stringparam html.stylesheet adt-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam appendix.autolabel A \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
TARFILES = adt-manual.html adt-manual.pdf adt-style.css figures/adt-title.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
|
||||
ifeq ($(DOC),profile-manual)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
TARFILES = profile-manual.html profile-manual.pdf profile-manual-style.css \
|
||||
figures/profile-title.png figures/kernelshark-all.png \
|
||||
figures/kernelshark-choose-events.png figures/kernelshark-i915-display.png \
|
||||
figures/kernelshark-output-display.png figures/lttngmain0.png \
|
||||
figures/oprofileui-busybox.png figures/oprofileui-copy-to-user.png \
|
||||
figures/oprofileui-downloading.png figures/oprofileui-processes.png \
|
||||
figures/perf-probe-do_fork-profile.png figures/perf-report-cycles-u.png \
|
||||
figures/perf-systemwide.png figures/perf-systemwide-libc.png \
|
||||
figures/perf-wget-busybox-annotate-menu.png figures/perf-wget-busybox-annotate-udhcpc.png \
|
||||
figures/perf-wget-busybox-debuginfo.png figures/perf-wget-busybox-dso-zoom.png \
|
||||
figures/perf-wget-busybox-dso-zoom-menu.png figures/perf-wget-busybox-expanded-stripped.png \
|
||||
figures/perf-wget-flat-stripped.png figures/perf-wget-g-copy-from-user-expanded-stripped.png \
|
||||
figures/perf-wget-g-copy-to-user-expanded-debuginfo.png figures/perf-wget-g-copy-to-user-expanded-stripped.png \
|
||||
figures/perf-wget-g-copy-to-user-expanded-stripped-unresolved-hidden.png figures/pybootchartgui-linux-yocto.png \
|
||||
figures/pychart-linux-yocto-rpm.png figures/pychart-linux-yocto-rpm-nostrip.png \
|
||||
figures/sched-wakeup-profile.png figures/sysprof-callers.png \
|
||||
figures/sysprof-copy-from-user.png figures/sysprof-copy-to-user.png \
|
||||
eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
ifeq ($(DOC),kernel-manual)
|
||||
XSLTOPTS = --stringparam html.stylesheet kernel-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam appendix.autolabel A \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
TARFILES = kernel-manual.html kernel-manual.pdf kernel-style.css figures/kernel-title.png figures/kernel-architecture-overview.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
|
||||
ifeq ($(DOC),kernel-dev)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
TARFILES = kernel-dev.html kernel-dev.pdf kernel-dev-style.css figures/kernel-dev-title.png \
|
||||
figures/kernel-architecture-overview.png \
|
||||
eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
XSLTOPTS = --stringparam html.stylesheet kernel-dev-style.css \
|
||||
--stringparam chapter.autolabel 1 \
|
||||
--stringparam appendix.autolabel A \
|
||||
--stringparam section.autolabel 1 \
|
||||
--stringparam section.label.includes.component.label 1 \
|
||||
--xinclude
|
||||
ALLPREQ = html pdf tarball
|
||||
TARFILES = kernel-dev.html kernel-dev.pdf kernel-dev-style.css figures/kernel-dev-title.png
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
endif
|
||||
@@ -324,49 +298,6 @@ else
|
||||
endif
|
||||
|
||||
|
||||
eclipse: BASE_DIR = html/$(DOC)/
|
||||
|
||||
eclipse: eclipse-generate eclipse-resolve-links
|
||||
|
||||
.PHONY : eclipse-generate eclipse-resolve-links
|
||||
|
||||
eclipse-generate:
|
||||
ifeq ($(filter $(DOC), adt-manual bsp-guide dev-manual kernel-dev profile-manual ref-manual yocto-project-qs),)
|
||||
@echo " "
|
||||
@echo "ERROR: You can only create eclipse documentation"
|
||||
@echo " of the following documentation parts:"
|
||||
@echo " - adt-manual"
|
||||
@echo " - bsp-guide"
|
||||
@echo " - dev-manual"
|
||||
@echo " - kernel-dev"
|
||||
@echo " - profile-manual"
|
||||
@echo " - ref-manual"
|
||||
@echo " - yocto-project-qs"
|
||||
@echo " "
|
||||
else
|
||||
@echo " "
|
||||
@echo "******** Building eclipse help of "$(DOC)
|
||||
@echo " "
|
||||
cd $(DOC) && \
|
||||
xsltproc $(XSLTOPTS) \
|
||||
--stringparam base.dir '$(BASE_DIR)' \
|
||||
-o eclipse/$(DOC).html \
|
||||
$(DOC)-eclipse-customization.xsl $(DOC).xml && \
|
||||
mv eclipse/toc.xml eclipse/$(DOC)-toc.xml && \
|
||||
cp -rf $(FIGURES) eclipse/$(BASE_DIR) && \
|
||||
cd ..;
|
||||
|
||||
$(call modify-eclipse)
|
||||
endif
|
||||
|
||||
eclipse-resolve-links:
|
||||
@echo " "
|
||||
@echo "******** Using eclipse-help.sed to process external links"
|
||||
@echo " "
|
||||
$(foreach FILE, \
|
||||
$(wildcard $(DOC)/eclipse/html/$(DOC)/*.html), \
|
||||
$(shell sed -i -f tools/eclipse-help.sed $(FILE)))
|
||||
|
||||
tarball: html
|
||||
@echo " "
|
||||
@echo "******** Creating Tarball of document files"
|
||||
@@ -383,8 +314,8 @@ publish:
|
||||
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); \
|
||||
scp -r $(MANUALS) $(STYLESHEET) www.yoctoproject.org:/srv/www/www.yoctoproject.org-docs/$(VER)/$(DOC); \
|
||||
cd $(DOC); scp -r $(FIGURES) www.yoctoproject.org:/srv/www/www.yoctoproject.org-docs/$(VER)/$(DOC); \
|
||||
else \
|
||||
echo " "; \
|
||||
echo $(DOC)".html missing. Generate the file first then try again."; \
|
||||
|
||||
@@ -2,60 +2,60 @@ documentation
|
||||
=============
|
||||
|
||||
This is the directory that contains the Yocto Project documentation. The Yocto
|
||||
Project source repositories at http://git.yoctoproject.org/cgit.cgi have two
|
||||
Project source repositories at http://git.yoctoproject.org/cgit.cgi have two
|
||||
instances of the "documentation" directory. You should understand each of
|
||||
these instances.
|
||||
|
||||
poky/documentation - The directory within the poky Git repository containing
|
||||
the set of Yocto Project manuals. When you clone the
|
||||
poky Git repository, the documentation directory
|
||||
contains the manuals. The state of the manuals in this
|
||||
directory is guaranteed to reflect the latest Yocto
|
||||
Project release. The manuals at the tip of this
|
||||
directory will also likely contain most manual
|
||||
the set of Yocto Project manuals. When you clone the
|
||||
poky Git repository, the documentation directory
|
||||
contains the manuals. The state of the manuals in this
|
||||
directory is guaranteed to reflect the latest Yocto
|
||||
Project release. The manuals at the tip of this
|
||||
directory will also likely contain most manual
|
||||
development changes.
|
||||
|
||||
yocto-docs/documentation - The Git repository for the Yocto Project manuals.
|
||||
This repository is where manual development
|
||||
occurs. If you plan on contributing back to the
|
||||
Yocto Project documentation, you should set up
|
||||
a local Git repository based on this upstream
|
||||
yocto-docs/documentation - The Git repository for the Yocto Project manuals.
|
||||
This repository is where manual development
|
||||
occurs. If you plan on contributing back to the
|
||||
Yocto Project documentation, you should set up
|
||||
a local Git repository based on this upstream
|
||||
repository as follows:
|
||||
|
||||
git clone git://git.yoctoproject.org/yocto-docs
|
||||
|
||||
Changes and patches are first pushed to the
|
||||
yocto-docs Git repository. Later, they make it
|
||||
into the poky Git repository found at
|
||||
git://git.yoctoproject.org/poky.
|
||||
Changes and patches are first pushed to the
|
||||
yocto-docs Git repository. Later, they make it
|
||||
into the poky Git repository found at
|
||||
git://git.yoctoproject.org/poky.
|
||||
|
||||
Manual Organization
|
||||
===================
|
||||
|
||||
Folders exist for individual manuals as follows:
|
||||
|
||||
* adt-manual - The Yocto Project Application Developer's Guide.
|
||||
* bsp-guide - The Yocto Project Board Support Package (BSP) Developer's Guide
|
||||
* adt-manual - The Yocto Project Application Development Toolkit (ADT)
|
||||
User's Guide.
|
||||
* bsp-guide - The Yocto Project Board Support (BSP) Developer's Guide
|
||||
* dev-manual - The Yocto Project Development Manual
|
||||
* kernel-dev - The Yocto Project Linux Kernel Development Manual
|
||||
* ref-manual - The Yocto Project Reference Manual
|
||||
* kernel-manual - The Yocto Project Kernel Architecture and Use Manual
|
||||
* poky-ref-manual - The Yocto Project Reference Manual
|
||||
* yocto-project-qs - The Yocto Project Quick Start
|
||||
* mega-manual - An aggregated manual comprised of all YP manuals and guides
|
||||
* profile-manual - The Yocto Project Profile and Tracing Manual
|
||||
* mega-manual - The aggregated manual comprised of all YP manuals and guides
|
||||
|
||||
Each folder is self-contained regarding content and figures. Note that there
|
||||
is a sed file needed to process the links of the mega-manual. The sed file
|
||||
is located in the tools directory. Also note that the figures folder in the
|
||||
is located in the tools directory. Also note that the figures folder in the
|
||||
mega-manual directory contains duplicates of all the figures in the YP folders
|
||||
directories for all YP manuals and guides.
|
||||
|
||||
If you want to find HTML versions of the Yocto Project manuals on the web,
|
||||
go to http://www.yoctoproject.org and click on the "Documentation" tab. From
|
||||
If you want to find HTML versions of the Yocto Project manuals on the web,
|
||||
go to http://www.yoctoproject.org and click on the "Documentation" tab. From
|
||||
there you have access to archived documentation from previous releases, current
|
||||
documentation for the latest release, and "Docs in Progress" for the release
|
||||
documentation for the latest release, and "Docs in Progress" for the release
|
||||
currently being developed.
|
||||
|
||||
In general, the Yocto Project site (http://www.yoctoproject.org) is a great
|
||||
In general, the Yocto Project site (http://www.yoctoproject.org) is a great
|
||||
reference for both information and downloads.
|
||||
|
||||
Makefile
|
||||
@@ -66,8 +66,8 @@ 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
|
||||
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 ADT manual.
|
||||
The DOC variable specifies the manual you are making:
|
||||
|
||||
@@ -86,6 +86,9 @@ Contains various templates, fonts, and some old PNG files.
|
||||
tools
|
||||
=====
|
||||
Contains a tool to convert the DocBook files to PDF format. This folder also
|
||||
contains the mega-manual.sed file, which is used by Makefile to process
|
||||
cross-references from within the manual that normally go to an external
|
||||
contains the mega-manual.sed file, which is used by Makefile to process
|
||||
cross-references from within the manual that normally go to an external
|
||||
manual.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,33 +7,24 @@
|
||||
|
||||
<para>
|
||||
Recall that earlier the manual discussed how to use an existing toolchain
|
||||
tarball that had been installed into the default installation
|
||||
directory, <filename>/opt/poky/&DISTRO;</filename>, which is outside of the
|
||||
tarball that had been installed into <filename>/opt/poky</filename>,
|
||||
which is outside of the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>
|
||||
(see the section "<link linkend='using-an-existing-toolchain-tarball'>Using a Cross-Toolchain Tarball)</link>".
|
||||
And, that sourcing your architecture-specific environment setup script
|
||||
initializes a suitable cross-toolchain development environment.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
During this setup, locations for the compiler, QEMU scripts, QEMU binary,
|
||||
During the setup, locations for the compiler, QEMU scripts, QEMU binary,
|
||||
a special version of <filename>pkgconfig</filename> and other useful
|
||||
utilities are added to the <filename>PATH</filename> variable.
|
||||
Also, variables to assist
|
||||
<filename>pkgconfig</filename> and <filename>autotools</filename>
|
||||
are also defined so that, for example, <filename>configure.sh</filename>
|
||||
can find pre-generated test results for tests that need target hardware
|
||||
on which to run.
|
||||
Variables to assist <filename>pkgconfig</filename> and <filename>autotools</filename>
|
||||
are also defined so that,
|
||||
for example, <filename>configure.sh</filename> can find pre-generated
|
||||
test results for tests that need target hardware on which to run.
|
||||
These conditions allow you to easily use the toolchain outside of the
|
||||
OpenEmbedded build environment on both autotools-based projects and
|
||||
Makefile-based projects.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Collectively, these conditions allow you to easily use the toolchain
|
||||
outside of the OpenEmbedded build environment on both autotools-based
|
||||
projects and Makefile-based projects.
|
||||
This chapter provides information for both these types of projects.
|
||||
</para>
|
||||
|
||||
|
||||
<section id='autotools-based-projects'>
|
||||
<title>Autotools-Based Projects</title>
|
||||
|
||||
@@ -90,21 +81,19 @@
|
||||
<listitem><para><emphasis>Source the cross-toolchain
|
||||
environment setup file:</emphasis>
|
||||
Installation of the cross-toolchain creates a cross-toolchain
|
||||
environment setup script in the directory that the ADT
|
||||
was installed.
|
||||
environment setup script in <filename>/opt/poky/<release></filename>.
|
||||
Before you can use the tools to develop your project, you must
|
||||
source this setup script.
|
||||
The script begins with the string "environment-setup" and contains
|
||||
the machine architecture, which is followed by the string
|
||||
"poky-linux".
|
||||
Here is an example that sources a script from the
|
||||
default ADT installation directory that uses the
|
||||
Here is an example for an environment setup using the
|
||||
32-bit Intel x86 Architecture and using the
|
||||
&DISTRO_NAME; Yocto Project release:
|
||||
<literallayout class='monospaced'>
|
||||
$ source /opt/poky/&DISTRO;/environment-setup-i586-poky-linux
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para><emphasis>Generate the local aclocal.m4
|
||||
<listitem><para><emphasis>Generate the local <filename>aclocal.m4</filename>
|
||||
files and create the configure script:</emphasis>
|
||||
The following GNU Autotools generate the local
|
||||
<filename>aclocal.m4</filename> files and create the
|
||||
@@ -121,7 +110,7 @@
|
||||
<literallayout class='monospaced'>
|
||||
$ touch NEWS README AUTHORS ChangeLog
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para><emphasis>Generate the configure
|
||||
<listitem><para><emphasis>Generate the <filename>configure</filename>
|
||||
file:</emphasis>
|
||||
This command generates the <filename>configure</filename>:
|
||||
<literallayout class='monospaced'>
|
||||
@@ -169,7 +158,8 @@
|
||||
For an Autotools-based project, you can use the cross-toolchain by just
|
||||
passing the appropriate host option to <filename>configure.sh</filename>.
|
||||
The host option you use is derived from the name of the environment setup
|
||||
script found in the directory in which you installed the cross-toolchain.
|
||||
script in <filename>/opt/poky</filename> resulting from installation of the
|
||||
cross-toolchain tarball.
|
||||
For example, the host option for an ARM-based target that uses the GNU EABI
|
||||
is <filename>armv5te-poky-linux-gnueabi</filename>.
|
||||
You will notice that the name of the script is
|
||||
@@ -188,7 +178,7 @@
|
||||
If <filename>configure</filename> script results in problems recognizing the
|
||||
<filename>--with-libtool-sysroot=<sysroot-dir></filename> option,
|
||||
regenerate the script to enable the support by doing the following and then
|
||||
run the script again:
|
||||
re-running the script:
|
||||
<literallayout class='monospaced'>
|
||||
$ libtoolize --automake
|
||||
$ aclocal -I ${OECORE_NATIVE_SYSROOT}/usr/share/aclocal \
|
||||
|
||||
@@ -19,13 +19,7 @@
|
||||
how to access and use the cross-development toolchains, how to
|
||||
customize the development packages installation,
|
||||
how to use command line development for both Autotools-based and Makefile-based projects,
|
||||
and an introduction to the <trademark class='trade'>Eclipse</trademark> IDE
|
||||
Yocto Plug-in.
|
||||
<note>
|
||||
The ADT is distribution-neutral and does not require the Yocto
|
||||
Project reference distribution, which is called Poky.
|
||||
This manual, however, uses examples that use the Poky distribution.
|
||||
</note>
|
||||
and an introduction to the Eclipse Yocto Plug-in.
|
||||
</para>
|
||||
|
||||
<section id='adt-intro-section'>
|
||||
@@ -42,10 +36,8 @@
|
||||
Fundamentally, the ADT consists of the following:
|
||||
<itemizedlist>
|
||||
<listitem><para>An architecture-specific cross-toolchain and matching
|
||||
sysroot both built by the OpenEmbedded build system.
|
||||
The toolchain and sysroot are based on a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#metadata'>Metadata</ulink>
|
||||
configuration and extensions,
|
||||
sysroot both built by the OpenEmbedded build system, which uses Poky.
|
||||
The toolchain and sysroot are based on a metadata configuration and extensions,
|
||||
which allows you to cross-develop on the host machine for the target hardware.
|
||||
</para></listitem>
|
||||
<listitem><para>The Eclipse IDE Yocto Plug-in.</para></listitem>
|
||||
@@ -56,20 +48,17 @@
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<section id='the-cross-development-toolchain'>
|
||||
<title>The Cross-Development Toolchain</title>
|
||||
<section id='the-cross-toolchain'>
|
||||
<title>The Cross-Toolchain</title>
|
||||
|
||||
<para>
|
||||
The
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#cross-development-toolchain'>Cross-Development Toolchain</ulink>
|
||||
consists of a cross-compiler, cross-linker, and cross-debugger
|
||||
that are used to develop user-space applications for targeted
|
||||
hardware.
|
||||
This toolchain is created either by running the ADT Installer
|
||||
script, a toolchain installer script, or through a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>
|
||||
that is based on your Metadata configuration or extension for
|
||||
your targeted device.
|
||||
The cross-toolchain consists of a cross-compiler, cross-linker, and cross-debugger
|
||||
that are used to develop user-space applications for targeted hardware.
|
||||
This toolchain is created either by running the ADT Installer script, a toolchain installer
|
||||
script, or through a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink> that
|
||||
is based on your metadata
|
||||
configuration or extension for your targeted device.
|
||||
The cross-toolchain works with a matching target sysroot.
|
||||
</para>
|
||||
</section>
|
||||
@@ -81,7 +70,7 @@
|
||||
The matching target sysroot contains needed headers and libraries for generating
|
||||
binaries that run on the target architecture.
|
||||
The sysroot is based on the target root filesystem image that is built by
|
||||
the OpenEmbedded build system and uses the same Metadata configuration
|
||||
the OpenEmbedded build system Poky and uses the same metadata configuration
|
||||
used to build the cross-toolchain.
|
||||
</para>
|
||||
</section>
|
||||
@@ -130,7 +119,7 @@
|
||||
the environment setup script, QEMU is installed and automatically
|
||||
available.</para></listitem>
|
||||
<listitem><para>If you have installed the cross-toolchain
|
||||
tarball and you have sourced the toolchain's setup environment script, QEMU
|
||||
tarball and you have sourcing the toolchain's setup environment script, QEMU
|
||||
is also installed and automatically available.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
@@ -149,7 +138,8 @@
|
||||
that causes skips in audio,
|
||||
stutters in your desktop experience, or situations that overload your server
|
||||
even when you have plenty of CPU power left.
|
||||
</para></listitem>
|
||||
You can find out more about LatencyTOP at
|
||||
<ulink url='http://www.latencytop.org/'></ulink>.</para></listitem>
|
||||
<listitem><para><emphasis>PowerTOP:</emphasis> Helps you determine what
|
||||
software is using the most power.
|
||||
You can find out more about PowerTOP at
|
||||
@@ -157,29 +147,18 @@
|
||||
<listitem><para><emphasis>OProfile:</emphasis> A system-wide profiler for Linux
|
||||
systems that is capable of profiling all running code at low overhead.
|
||||
You can find out more about OProfile at
|
||||
<ulink url='http://oprofile.sourceforge.net/about/'></ulink>.
|
||||
For examples on how to setup and use this tool, see the
|
||||
"<ulink url='&YOCTO_DOCS_PROF_URL;#profile-manual-oprofile'>OProfile</ulink>"
|
||||
section in the Yocto Project Profiling and Tracing Manual.
|
||||
</para></listitem>
|
||||
<ulink url='http://oprofile.sourceforge.net/about/'></ulink>.</para></listitem>
|
||||
<listitem><para><emphasis>Perf:</emphasis> Performance counters for Linux used
|
||||
to keep track of certain types of hardware and software events.
|
||||
For more information on these types of counters see
|
||||
<ulink url='https://perf.wiki.kernel.org/'></ulink> and click
|
||||
on “Perf tools.”
|
||||
For examples on how to setup and use this tool, see the
|
||||
"<ulink url='&YOCTO_DOCS_PROF_URL;#profile-manual-perf'>perf</ulink>"
|
||||
section in the Yocto Project Profiling and Tracing Manual.
|
||||
</para></listitem>
|
||||
on “Perf tools.”</para></listitem>
|
||||
<listitem><para><emphasis>SystemTap:</emphasis> A free software infrastructure
|
||||
that simplifies information gathering about a running Linux system.
|
||||
This information helps you diagnose performance or functional problems.
|
||||
SystemTap is not available as a user-space tool through the Eclipse IDE Yocto Plug-in.
|
||||
See <ulink url='http://sourceware.org/systemtap'></ulink> for more information
|
||||
on SystemTap.
|
||||
For examples on how to setup and use this tool, see the
|
||||
"<ulink url='&YOCTO_DOCS_PROF_URL;#profile-manual-systemtap'>SystemTap</ulink>"
|
||||
section in the Yocto Project Profiling and Tracing Manual.</para></listitem>
|
||||
on SystemTap.</para></listitem>
|
||||
<listitem><para><emphasis>Lttng-ust:</emphasis> A User-space Tracer designed to
|
||||
provide detailed information on user-space activity.
|
||||
See <ulink url='http://lttng.org/ust'></ulink> for more information on Lttng-ust.
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" />
|
||||
|
||||
<xsl:param name="html.stylesheet" select="'adt-style.css'" />
|
||||
<xsl:param name="chapter.autolabel" select="1" />
|
||||
<xsl:param name="appendix.autolabel" select="1" />
|
||||
<xsl:param name="section.autolabel" select="1" />
|
||||
<xsl:param name="section.label.includes.component.label" select="1" />
|
||||
<!-- <xsl:param name="generate.toc" select="'article nop'"></xsl:param> -->
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
||||
@@ -1,27 +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/eclipse/eclipse3.xsl" />
|
||||
|
||||
<xsl:param name="chunker.output.indent" select="'yes'"/>
|
||||
<xsl:param name="chunk.quietly" select="1"/>
|
||||
<xsl:param name="chunk.first.sections" select="1"/>
|
||||
<xsl:param name="chunk.section.depth" select="10"/>
|
||||
<xsl:param name="use.id.as.filename" select="1"/>
|
||||
<xsl:param name="ulink.target" select="'_self'" />
|
||||
<xsl:param name="base.dir" select="'html/adt-manual/'"/>
|
||||
<xsl:param name="html.stylesheet" select="'../book.css'"/>
|
||||
<xsl:param name="eclipse.manifest" select="0"/>
|
||||
<xsl:param name="create.plugin.xml" select="0"/>
|
||||
<xsl:param name="suppress.navigation" select="1"/>
|
||||
<xsl:param name="generate.index" select="0"/>
|
||||
<xsl:param name="chapter.autolabel" select="1" />
|
||||
<xsl:param name="appendix.autolabel" select="1" />
|
||||
<xsl:param name="section.autolabel" select="1" />
|
||||
<xsl:param name="section.label.includes.component.label" select="1" />
|
||||
</xsl:stylesheet>
|
||||
@@ -16,9 +16,7 @@
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<title>
|
||||
Yocto Project Application Developer's Guide
|
||||
</title>
|
||||
<title></title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
@@ -58,35 +56,10 @@
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4</revnumber>
|
||||
<date>April 2013</date>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>October 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.1</revnumber>
|
||||
<date>January 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.2</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.3</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.4</revnumber>
|
||||
<date>December 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.4 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
<year>©RIGHT_YEAR;</year>
|
||||
@@ -99,10 +72,11 @@
|
||||
the terms of the <ulink type="http" url="http://creativecommons.org/licenses/by-sa/2.0/uk/">Creative Commons Attribution-Share Alike 2.0 UK: England & Wales</ulink> as published by Creative Commons.
|
||||
</para>
|
||||
<note>
|
||||
For the latest version of this manual associated with this
|
||||
Yocto Project release, see the
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;'>Yocto Project Application Developer's Guide</ulink>
|
||||
from the Yocto Project website.
|
||||
Due to production processes, there could be differences between the Yocto Project
|
||||
documentation bundled in the release tarball and the
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;'>Yocto Project Application Developer's Guide</ulink> on
|
||||
the <ulink url='&YOCTO_HOME_URL;'>Yocto Project</ulink> website.
|
||||
For the latest version of this manual, see the manual on the website.
|
||||
</note>
|
||||
|
||||
</legalnotice>
|
||||
|
||||
@@ -55,9 +55,9 @@
|
||||
</para>
|
||||
|
||||
<note>
|
||||
For build performance information related to the PMS, see the
|
||||
"<ulink url='&YOCTO_DOCS_REF_URL;#ref-classes-package'>Packaging - <filename>package*.bbclass</filename></ulink>"
|
||||
section in the Yocto Project Reference Manual.
|
||||
For build performance information related to the PMS, see
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#ref-classes-package'>Packaging - <filename>package*.bbclass</filename></ulink>
|
||||
in the Yocto Project Reference Manual.
|
||||
</note>
|
||||
|
||||
<para>
|
||||
|
||||
@@ -39,18 +39,18 @@
|
||||
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Use the ADT installer script:</emphasis>
|
||||
<listitem><para><emphasis>Use the ADT Installer Script:</emphasis>
|
||||
This method is the recommended way to install the ADT because it
|
||||
automates much of the process for you.
|
||||
For example, you can configure the installation to install the QEMU emulator
|
||||
and the user-space NFS, specify which root filesystem profiles to download,
|
||||
and define the target sysroot location.</para></listitem>
|
||||
<listitem><para><emphasis>Use an existing toolchain:</emphasis>
|
||||
<listitem><para><emphasis>Use an Existing Toolchain:</emphasis>
|
||||
Using this method, you select and download an architecture-specific
|
||||
toolchain installer and then run the script to hand-install the toolchain.
|
||||
If you use this method, you just get the cross-toolchain and QEMU - you do not
|
||||
get any of the other mentioned benefits had you run the ADT Installer script.</para></listitem>
|
||||
<listitem><para><emphasis>Use the toolchain from within the Build Directory:</emphasis>
|
||||
<listitem><para><emphasis>Use the Toolchain from within the Build Directory:</emphasis>
|
||||
If you already have a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>,
|
||||
you can build the cross-toolchain within the directory.
|
||||
@@ -80,48 +80,37 @@
|
||||
|
||||
<para>
|
||||
The ADT Installer is contained in the ADT Installer tarball.
|
||||
You can get the tarball using either of these methods:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Download the Tarball:</emphasis>
|
||||
You can download the tarball from
|
||||
<ulink url='&YOCTO_ADTINSTALLER_DL_URL;'></ulink> into
|
||||
any directory.</para></listitem>
|
||||
<listitem><para><emphasis>Build the Tarball:</emphasis>
|
||||
You can use BitBake to generate the tarball inside an
|
||||
existing
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
</para>
|
||||
<para>If you use BitBake to generate the ADT Installer
|
||||
tarball, you must <filename>source</filename> the
|
||||
environment setup script
|
||||
(<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>)
|
||||
located in the Source Directory before running the
|
||||
BitBake command that creates the tarball.</para>
|
||||
<para>The following example commands establish
|
||||
the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>,
|
||||
check out the current release branch, set up the
|
||||
build environment while also creating the default
|
||||
Build Directory, and run the BitBake command that
|
||||
results in the tarball
|
||||
<filename>poky/build/tmp/deploy/sdk/adt_installer.tar.bz2</filename>:
|
||||
<note>
|
||||
Before using BitBake to build the ADT tarball, be
|
||||
sure to make sure your
|
||||
<filename>local.conf</filename> file is properly
|
||||
configured.
|
||||
</note>
|
||||
<literallayout class='monospaced'>
|
||||
You can download the tarball into any directory from the
|
||||
<ulink url='&YOCTO_DL_URL;/releases'>Index of Releases</ulink>, specifically
|
||||
at
|
||||
<ulink url='&YOCTO_ADTINSTALLER_DL_URL;'></ulink>.
|
||||
Or, you can use BitBake to generate the tarball inside the existing
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you use BitBake to generate the ADT Installer tarball, you must
|
||||
<filename>source</filename> the environment setup script
|
||||
(<filename>&OE_INIT_FILE;</filename>) located
|
||||
in the Source Directory before running the <filename>bitbake</filename>
|
||||
command that creates the tarball.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following example commands download the Poky tarball, set up the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>,
|
||||
set up the environment while also creating the default Build Directory,
|
||||
and run the <filename>bitbake</filename> command that results in the tarball
|
||||
<filename>~/yocto-project/build/tmp/deploy/sdk/adt_installer.tar.bz2</filename>:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~
|
||||
$ git clone git://git.yoctoproject.org/poky
|
||||
$ cd poky
|
||||
$ git checkout -b &DISTRO_NAME; origin/&DISTRO_NAME;
|
||||
$ source &OE_INIT_FILE;
|
||||
$ mkdir yocto-project
|
||||
$ cd yocto-project
|
||||
$ wget &YOCTO_RELEASE_DL_URL;/&YOCTO_POKY_TARBALL;
|
||||
$ tar xjf &YOCTO_POKY_TARBALL;
|
||||
$ source &OE_INIT_PATH;
|
||||
$ bitbake adt-installer
|
||||
</literallayout></para></listitem>
|
||||
</itemizedlist>
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -136,7 +125,7 @@
|
||||
a top-level directory named <filename>adt-installer</filename>:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~
|
||||
$ cp poky/build/tmp/deploy/sdk/adt_installer.tar.bz2 $HOME
|
||||
$ cp ~/poky/build/tmp/deploy/sdk/adt_installer.tar.bz2 $HOME
|
||||
$ tar -xjf adt_installer.tar.bz2
|
||||
</literallayout>
|
||||
Unpacking it creates the directory <filename>adt-installer</filename>,
|
||||
@@ -201,34 +190,32 @@
|
||||
|
||||
<para>
|
||||
After you have configured the <filename>adt_installer.conf</filename> file,
|
||||
run the installer using the following command:
|
||||
run the installer using the following command.
|
||||
Be sure that you are not trying to use cross-compilation tools.
|
||||
When you run the installer, the environment must use a
|
||||
host <filename>gcc</filename>:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd adt-installer
|
||||
$ cd ~/adt-installer
|
||||
$ ./adt_installer
|
||||
</literallayout>
|
||||
Once the installer begins to run, you are asked to enter the
|
||||
location for cross-toolchain installation.
|
||||
The default location is
|
||||
<filename>/opt/poky/<release></filename>.
|
||||
After either accepting the default location or selecting your
|
||||
own location, you are prompted to run the installation script
|
||||
interactively or in silent mode.
|
||||
If you want to closely monitor the installation,
|
||||
choose “I” for interactive mode rather than “S” for silent mode.
|
||||
Once the installer begins to run, you are asked to enter the location for
|
||||
cross-toolchain installation.
|
||||
The default location is <filename>/opt/poky/<release></filename>.
|
||||
After selecting the location, you are prompted to run in
|
||||
interactive or silent mode.
|
||||
If you want to closely monitor the installation, choose “I” for interactive
|
||||
mode rather than “S” for silent mode.
|
||||
Follow the prompts from the script to complete the installation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once the installation completes, the ADT, which includes the
|
||||
cross-toolchain, is installed in the selected installation
|
||||
directory.
|
||||
You will notice environment setup files for the cross-toolchain
|
||||
in the installation directory, and image tarballs in the
|
||||
<filename>adt-installer</filename> directory according to your
|
||||
installer configurations, and the target sysroot located
|
||||
according to the
|
||||
<filename>YOCTOADT_TARGET_SYSROOT_LOC_<arch></filename>
|
||||
variable also in your configuration file.
|
||||
Once the installation completes, the ADT, which includes the cross-toolchain, is installed.
|
||||
You will notice environment setup files for the cross-toolchain in
|
||||
<filename>&YOCTO_ADTPATH_DIR;</filename>,
|
||||
and image tarballs in the <filename>adt-installer</filename>
|
||||
directory according to your installer configurations, and the target sysroot located
|
||||
according to the <filename>YOCTOADT_TARGET_SYSROOT_LOC_<arch></filename> variable
|
||||
also in your configuration file.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
@@ -237,79 +224,69 @@
|
||||
<title>Using a Cross-Toolchain Tarball</title>
|
||||
|
||||
<para>
|
||||
If you want to simply install a cross-toolchain by hand, you can
|
||||
do so by running the toolchain installer.
|
||||
The installer includes the pre-built cross-toolchain, the
|
||||
<filename>runqemu</filename> script, and support files.
|
||||
If you use this method to install the cross-toolchain, you
|
||||
might still need to install the target sysroot by installing and
|
||||
extracting it separately.
|
||||
For information on how to install the sysroot, see the
|
||||
If you want to simply install the cross-toolchain by hand, you can do so by running the
|
||||
toolchain installer.
|
||||
If you use this method to install the cross-toolchain and you still need to install the target
|
||||
sysroot, you will have to extract and install sysroot separately.
|
||||
For information on how to do this, see the
|
||||
"<link linkend='extracting-the-root-filesystem'>Extracting the Root Filesystem</link>" section.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Get your toolchain installer using one of the
|
||||
following methods:
|
||||
<itemizedlist>
|
||||
<listitem><para>Go to
|
||||
<ulink url='&YOCTO_TOOLCHAIN_DL_URL;'></ulink>
|
||||
and find the folder that matches your host
|
||||
development system (i.e. <filename>i686</filename>
|
||||
for 32-bit machines or <filename>x86_64</filename>
|
||||
for 64-bit machines).</para>
|
||||
<para>Go into that folder and download the toolchain
|
||||
installer whose name includes the appropriate target
|
||||
architecture.
|
||||
The toolchains provided by the Yocto Project
|
||||
are based off of the
|
||||
<filename>core-image-sato</filename> image and
|
||||
contain libraries appropriate for developing
|
||||
against that image.
|
||||
For example, if your host development system is a
|
||||
64-bit x86 system and you are going to use
|
||||
your cross-toolchain for a 32-bit x86
|
||||
target, go into the <filename>x86_64</filename>
|
||||
folder and download the following installer:
|
||||
<literallayout class='monospaced'>
|
||||
poky-eglibc-x86_64-core-image-sato-i586-toolchain-&DISTRO;.sh
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para>Build your own toolchain installer.
|
||||
For cases where you cannot use an installer
|
||||
from the download area, you can build your own as
|
||||
described in the
|
||||
"<link linkend='optionally-building-a-toolchain-installer'>Optionally Building a Toolchain Installer</link>"
|
||||
section.</para></listitem>
|
||||
</itemizedlist></para></listitem>
|
||||
<listitem><para>Once you have the installer, run it to install
|
||||
the toolchain.
|
||||
<note>
|
||||
You must change the permissions on the toolchain
|
||||
installer script so that it is executable.
|
||||
</note></para>
|
||||
<para>The following command shows how to run the installer
|
||||
given a toolchain tarball for a 64-bit x86 development host
|
||||
system and a 32-bit x86 target architecture.
|
||||
The example assumes the toolchain installer is located
|
||||
in <filename>~/Downloads/</filename>.
|
||||
<listitem><para>Go to
|
||||
<ulink url='&YOCTO_TOOLCHAIN_DL_URL;'></ulink>
|
||||
and find the folder that matches your host development system
|
||||
(i.e. <filename>i686</filename> for 32-bit machines or
|
||||
<filename>x86-64</filename> for 64-bit machines).</para></listitem>
|
||||
<listitem><para>Go into that folder and download the toolchain installer whose name
|
||||
includes the appropriate target architecture.
|
||||
For example, if your host development system is an Intel-based 64-bit system and
|
||||
you are going to use your cross-toolchain for an Intel-based 32-bit target, go into the
|
||||
<filename>x86_64</filename> folder and download the following installer:
|
||||
<literallayout class='monospaced'>
|
||||
$ ~/Downloads/poky-eglibc-x86_64-core-image-sato-i586-toolchain-&DISTRO;.sh
|
||||
poky-eglibc-x86_64-i586-toolchain-gmae-&DISTRO;.sh
|
||||
</literallayout>
|
||||
The first thing the installer prompts you for is the
|
||||
directory into which you want to install the toolchain.
|
||||
The default directory used is
|
||||
<filename>/opt/poky/&DISTRO;</filename>.
|
||||
If you do not have write permissions for the directory
|
||||
into which you are installing the toolchain, the
|
||||
toolchain installer notifies you and exits.
|
||||
Be sure you have write permissions in the directory and
|
||||
run the installer again.</para>
|
||||
<para>When the script finishes, the cross-toolchain is
|
||||
installed.
|
||||
You will notice environment setup files for the
|
||||
cross-toolchain in the installation directory.
|
||||
<note><para>As an alternative to steps one and two, you can build the toolchain installer
|
||||
if you have a <ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
If you need GMAE, you should use the <filename>bitbake meta-toolchain-gmae</filename>
|
||||
command.
|
||||
The resulting installation script when run will support such development.
|
||||
However, if you are not concerned with GMAE,
|
||||
you can generate the toolchain installer using
|
||||
<filename>bitbake meta-toolchain</filename>.</para>
|
||||
<para>Use the appropriate <filename>bitbake</filename> command only after you have
|
||||
sourced the <filename>&OE_INIT_PATH;</filename> script located in the Source
|
||||
Directory and you have made sure your <filename>conf/local.conf</filename>
|
||||
variables are correct.
|
||||
In particular, you need to be sure the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
variable matches the architecture for which you are building and that the
|
||||
<filename>SDKMACHINE</filename> variable is correctly set if you are building
|
||||
a toolchain for an architecture that differs from your current
|
||||
development host machine.</para>
|
||||
<para>When the <filename>bitbake</filename> command completes, the
|
||||
toolchain installer will be in <filename>tmp/deploy/sdk</filename> in the
|
||||
Build Directory.
|
||||
</para></note>
|
||||
</para></listitem>
|
||||
<listitem><para>Once you have the installer, run it to install the toolchain.
|
||||
You must change the permissions on the toolchain installer
|
||||
script so that it is executable.</para>
|
||||
<para>The following command shows how to run the installer given a toolchain tarball
|
||||
for a 64-bit development host system and a 32-bit target architecture.
|
||||
The example assumes the toolchain installer is located in <filename>~/Downloads/</filename>.
|
||||
<literallayout class='monospaced'>
|
||||
$ ~/Downloads/poky-eglibc-x86_64-i586-toolchain-gmae-&DISTRO;.sh
|
||||
</literallayout>
|
||||
<note>
|
||||
If you do not have write permissions for the directory into which you are installing
|
||||
the toolchain, the toolchain installer notifies you and exits.
|
||||
Be sure you have write permissions in the directory and run the installer again.
|
||||
</note>
|
||||
Once the tarball is expanded, the cross-toolchain is installed.
|
||||
You will notice environment setup files for the cross-toolchain in the directory.
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
@@ -322,7 +299,7 @@
|
||||
A final way of making the cross-toolchain available is to use BitBake
|
||||
to generate the toolchain within an existing
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
This method does not install the toolchain into the default
|
||||
This method does not install the toolchain into the
|
||||
<filename>/opt</filename> directory.
|
||||
As with the previous method, if you need to install the target sysroot, you must
|
||||
do that separately as well.
|
||||
@@ -331,43 +308,37 @@
|
||||
<para>
|
||||
Follow these steps to generate the toolchain into the Build Directory:
|
||||
<orderedlist>
|
||||
<listitem><para><emphasis>Set up the Build Environment:</emphasis>
|
||||
Source the OpenEmbedded build environment setup
|
||||
script (i.e.
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>)
|
||||
located in the
|
||||
<listitem><para>Source the environment setup script
|
||||
<filename>&OE_INIT_FILE;</filename> located in the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Check your Local Configuration File:</emphasis>
|
||||
At this point, you should be sure that the
|
||||
<listitem><para>At this point, you should be sure that the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink> variable
|
||||
in the <filename>local.conf</filename> file found in the
|
||||
<filename>conf</filename> directory of the Build Directory
|
||||
is set for the target architecture.
|
||||
Comments within the <filename>local.conf</filename> file
|
||||
list the values you can use for the
|
||||
<filename>MACHINE</filename> variable.
|
||||
<note>
|
||||
You can populate the Build Directory with the
|
||||
cross-toolchains for more than a single architecture.
|
||||
You just need to edit the <filename>MACHINE</filename>
|
||||
variable in the <filename>local.conf</filename> file and
|
||||
re-run the BitBake command.
|
||||
</note></para></listitem>
|
||||
<listitem><para><emphasis>Generate the Cross-Toolchain:</emphasis>
|
||||
Run <filename>bitbake meta-ide-support</filename> to
|
||||
complete the cross-toolchain generation.
|
||||
Once the BitBake command finishes, the cross-toolchain is
|
||||
generated and populated within the Build Directory.
|
||||
You will notice environment setup files for the
|
||||
cross-toolchain that contain the string
|
||||
"<filename>environment-setup</filename>" in the
|
||||
Build Directory's <filename>tmp</filename> folder.</para>
|
||||
<para>Be aware that when you use this method to install the
|
||||
toolchain, you still need to separately extract and install
|
||||
the sysroot filesystem.
|
||||
Comments within the <filename>local.conf</filename> file list the values you
|
||||
can use for the <filename>MACHINE</filename> variable.
|
||||
<note>You can populate the Build Directory with the cross-toolchains for more
|
||||
than a single architecture.
|
||||
You just need to edit the <filename>MACHINE</filename> variable in the
|
||||
<filename>local.conf</filename> file and re-run the BitBake
|
||||
command.</note></para></listitem>
|
||||
<listitem><para>Run <filename>bitbake meta-ide-support</filename> to complete the
|
||||
cross-toolchain generation.
|
||||
<note>If you change out of your working directory after you
|
||||
<filename>source</filename> the environment setup script and before you run
|
||||
the <filename>bitbake</filename> command, the command might not work.
|
||||
Be sure to run the <filename>bitbake</filename> command immediately
|
||||
after checking or editing the <filename>local.conf</filename> but without
|
||||
changing out of your working directory.</note>
|
||||
Once the <filename>bitbake</filename> command finishes,
|
||||
the cross-toolchain is generated and populated within the Build Directory.
|
||||
You will notice environment setup files for the cross-toolchain in the
|
||||
Build Directory in the <filename>tmp</filename> directory.
|
||||
Setup script filenames contain the strings <filename>environment-setup</filename>.</para>
|
||||
<para>Be aware that when you use this method to install the toolchain you still need
|
||||
to separately extract and install the sysroot filesystem.
|
||||
For information on how to do this, see the
|
||||
"<link linkend='extracting-the-root-filesystem'>Extracting the Root Filesystem</link>" section.
|
||||
</para></listitem>
|
||||
@@ -383,8 +354,7 @@
|
||||
Before you can develop using the cross-toolchain, you need to set up the
|
||||
cross-development environment by sourcing the toolchain's environment setup script.
|
||||
If you used the ADT Installer or hand-installed cross-toolchain,
|
||||
then you can find this script in the directory you chose for installation.
|
||||
The default installation directory is the <filename>&YOCTO_ADTPATH_DIR;</filename>
|
||||
then you can find this script in the <filename>&YOCTO_ADTPATH_DIR;</filename>
|
||||
directory.
|
||||
If you installed the toolchain in the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>,
|
||||
@@ -393,14 +363,12 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Be sure to run the environment setup script that matches the
|
||||
architecture for which you are developing.
|
||||
Environment setup scripts begin with the string
|
||||
"<filename>environment-setup</filename>" and include as part of their
|
||||
name the architecture.
|
||||
For example, the toolchain environment setup script for a 64-bit
|
||||
IA-based architecture installed in the default installation directory
|
||||
would be the following:
|
||||
Be sure to run the environment setup script that matches the architecture for
|
||||
which you are developing.
|
||||
Environment setup scripts begin with the string “<filename>environment-setup</filename>”
|
||||
and include as part of their name the architecture.
|
||||
For example, the toolchain environment setup script for a 64-bit IA-based architecture would
|
||||
be the following:
|
||||
<literallayout class='monospaced'>
|
||||
&YOCTO_ADTPATH_DIR;/environment-setup-x86_64-poky-linux
|
||||
</literallayout>
|
||||
@@ -445,8 +413,8 @@
|
||||
<para>
|
||||
If you are planning on developing against your image and you are not
|
||||
building or using one of the Yocto Project development images
|
||||
(e.g. <filename>core-image-*-dev</filename>), you must be sure to
|
||||
include the development packages as part of your image recipe.
|
||||
(e.g. core-image-*-dev), you must be sure to include the development
|
||||
packages as part of your image recipe.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -454,8 +422,8 @@
|
||||
application from within the
|
||||
Eclipse IDE, you must have an image that contains the Yocto Target Communication
|
||||
Framework (TCF) agent (<filename>tcf-agent</filename>).
|
||||
By default, the Yocto Project provides only one type of pre-built
|
||||
image that contains the <filename>tcf-agent</filename>.
|
||||
By default, the Yocto Project provides only one type pre-built image that contains the
|
||||
<filename>tcf-agent</filename>.
|
||||
And, those images are SDK (e.g.<filename>core-image-sato-sdk</filename>).
|
||||
</para>
|
||||
|
||||
@@ -484,16 +452,13 @@
|
||||
the following commands:
|
||||
<literallayout class='monospaced'>
|
||||
$ git clone http://git.eclipse.org/gitroot/tcf/org.eclipse.tcf.agent.git
|
||||
$ cd org.eclipse.tcf.agent/agent
|
||||
$ cd agent
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para>Locate the
|
||||
<filename>Makefile.inc</filename> file inside the
|
||||
<filename>agent</filename> folder and modify it
|
||||
<listitem><para>Modify the <filename>Makefile.inc</filename> file
|
||||
for the cross-compilation environment by setting the
|
||||
<filename>OPSYS</filename> and
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
variables according to your target.
|
||||
</para></listitem>
|
||||
variables according to your target.</para></listitem>
|
||||
<listitem><para>Use the cross-development tools to build the
|
||||
<filename>tcf-agent</filename>.
|
||||
Before you "Make" the file, be sure your cross-tools are set up first.
|
||||
@@ -513,130 +478,36 @@
|
||||
<title>Extracting the Root Filesystem</title>
|
||||
|
||||
<para>
|
||||
If you install your toolchain by hand or build it using BitBake and
|
||||
you need a root filesystem, you need to extract it separately.
|
||||
If you use the ADT Installer to install the ADT, the root
|
||||
filesystem is automatically extracted and installed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here are some cases where you need to extract the root filesystem:
|
||||
<itemizedlist>
|
||||
<listitem><para>You want to boot the image using NFS.
|
||||
</para></listitem>
|
||||
<listitem><para>You want to use the root filesystem as the
|
||||
target sysroot.
|
||||
For example, the Eclipse IDE environment with the Eclipse
|
||||
Yocto Plug-in installed allows you to use QEMU to boot
|
||||
under NFS.</para></listitem>
|
||||
<listitem><para>You want to develop your target application
|
||||
using the root filesystem as the target sysroot.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
You must extract the root filesystem if you want to boot the image using NFS
|
||||
or you want to use the root filesystem as the target sysroot.
|
||||
For example, the Eclipse IDE environment with the Eclipse Yocto Plug-in installed allows you
|
||||
to use QEMU to boot under NFS.
|
||||
Another example is if you want to develop your target application using the
|
||||
root filesystem as the target sysroot.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To extract the root filesystem, first <filename>source</filename>
|
||||
the cross-development environment setup script.
|
||||
If you built the toolchain in the Build Directory, you will find
|
||||
the toolchain environment script in the
|
||||
<filename>tmp</filename> directory.
|
||||
If you installed the toolchain by hand, the environment setup
|
||||
script is located in <filename>/opt/poky/&DISTRO;</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After sourcing the environment script, use the
|
||||
<filename>runqemu-extract-sdk</filename> command and provide the
|
||||
the cross-development environment setup script and then
|
||||
use the <filename>runqemu-extract-sdk</filename> command on the
|
||||
filesystem image.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Following is an example.
|
||||
The second command sets up the environment.
|
||||
In this case, the setup script is located in the
|
||||
<filename>/opt/poky/&DISTRO;</filename> directory.
|
||||
The third command extracts the root filesystem from a previously
|
||||
built filesystem that is located in the
|
||||
<filename>~/Downloads</filename> directory.
|
||||
Furthermore, this command extracts the root filesystem into the
|
||||
<filename>qemux86-sato</filename> directory:
|
||||
For example, the following commands set up the environment and then extract
|
||||
the root filesystem from a previously built filesystem image tarball named
|
||||
<filename>core-image-sato-sdk-qemux86-2011091411831.rootfs.tar.bz2</filename>.
|
||||
The example extracts the root filesystem into the <filename>$HOME/qemux86-sato</filename>
|
||||
directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~
|
||||
$ source /opt/poky/&DISTRO;/environment-setup-i586-poky-linux
|
||||
$ source $HOME/poky/build/tmp/environment-setup-i586-poky-linux
|
||||
$ runqemu-extract-sdk \
|
||||
~/Downloads/core-image-sato-sdk-qemux86-2011091411831.rootfs.tar.bz2 \
|
||||
tmp/deploy/images/core-image-sato-sdk-qemux86-2011091411831.rootfs.tar.bz2 \
|
||||
$HOME/qemux86-sato
|
||||
</literallayout>
|
||||
You could now point to the target sysroot at
|
||||
<filename>qemux86-sato</filename>.
|
||||
In this case, you could now point to the target sysroot at
|
||||
<filename>$HOME/qemux86-sato</filename>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='optionally-building-a-toolchain-installer'>
|
||||
<title>Optionally Building a Toolchain Installer</title>
|
||||
|
||||
<para>
|
||||
As an alternative to locating and downloading a toolchain installer,
|
||||
you can build the toolchain installer one of two ways if you have a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>:
|
||||
<itemizedlist>
|
||||
<listitem><para>Use <filename>bitbake meta-toolchain</filename>.
|
||||
This method requires you to still install the target
|
||||
sysroot by installing and extracting it separately.
|
||||
For information on how to install the sysroot, see the
|
||||
"<link linkend='extracting-the-root-filesystem'>Extracting the Root Filesystem</link>"
|
||||
section.</para></listitem>
|
||||
<listitem><para>Use
|
||||
<filename>bitbake image -c populate_sdk</filename>.
|
||||
This method has significant advantages over the previous method
|
||||
because it results in a toolchain installer that contains the
|
||||
sysroot that matches your target root filesystem.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Remember, before using any BitBake command, you
|
||||
must source the build environment setup script
|
||||
(i.e.
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>)
|
||||
located in the Source Directory and you must make sure your
|
||||
<filename>conf/local.conf</filename> variables are correct.
|
||||
In particular, you need to be sure the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
variable matches the architecture for which you are building and that
|
||||
the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-SDKMACHINE'><filename>SDKMACHINE</filename></ulink>
|
||||
variable is correctly set if you are building a toolchain designed to
|
||||
run on an architecture that differs from your current development host
|
||||
machine (i.e. the build machine).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When the BitBake command completes, the toolchain installer will be in
|
||||
<filename>tmp/deploy/sdk</filename> in the Build Directory.
|
||||
<note>
|
||||
By default, this toolchain does not build static binaries.
|
||||
If you want to use the toolchain to build these types of libraries,
|
||||
you need to be sure your image has the appropriate static
|
||||
development libraries.
|
||||
Use the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-IMAGE_INSTALL'><filename>IMAGE_INSTALL</filename></ulink>
|
||||
variable inside your <filename>local.conf</filename> file to
|
||||
install the appropriate library packages.
|
||||
Following is an example using <filename>eglibc</filename> static
|
||||
development libraries:
|
||||
<literallayout class='monospaced'>
|
||||
IMAGE_INSTALL_append = " eglibc-staticdev"
|
||||
</literallayout>
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
|
||||
@@ -3,8 +3,4 @@
|
||||
|
||||
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" />
|
||||
|
||||
<xsl:param name="html.stylesheet" select="'bsp-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:stylesheet>
|
||||
|
||||
@@ -1,27 +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/eclipse/eclipse3.xsl" />
|
||||
|
||||
<xsl:param name="chunker.output.indent" select="'yes'"/>
|
||||
<xsl:param name="chunk.quietly" select="1"/>
|
||||
<xsl:param name="chunk.first.sections" select="1"/>
|
||||
<xsl:param name="chunk.section.depth" select="10"/>
|
||||
<xsl:param name="use.id.as.filename" select="1"/>
|
||||
<xsl:param name="ulink.target" select="'_self'" />
|
||||
<xsl:param name="base.dir" select="'html/bsp-guide/'"/>
|
||||
<xsl:param name="html.stylesheet" select="'../book.css'"/>
|
||||
<xsl:param name="eclipse.manifest" select="0"/>
|
||||
<xsl:param name="create.plugin.xml" select="0"/>
|
||||
<xsl:param name="suppress.navigation" select="1"/>
|
||||
<xsl:param name="generate.index" select="0"/>
|
||||
<xsl:param name="chapter.autolabel" select="1" />
|
||||
<xsl:param name="appendix.autolabel" select="1" />
|
||||
<xsl:param name="section.autolabel" select="1" />
|
||||
<xsl:param name="section.label.includes.component.label" select="1" />
|
||||
</xsl:stylesheet>
|
||||
@@ -16,9 +16,7 @@
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<title>
|
||||
Yocto Project Board Support Package Developer's Guide
|
||||
</title>
|
||||
<title></title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
@@ -70,34 +68,9 @@
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4</revnumber>
|
||||
<date>April 2013</date>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>October 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.1</revnumber>
|
||||
<date>January 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.2</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.3</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5.4</revnumber>
|
||||
<date>December 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.5.4 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
@@ -111,10 +84,11 @@
|
||||
the terms of the <ulink type="http" url="http://creativecommons.org/licenses/by-nc-sa/2.0/uk/">Creative Commons Attribution-Non-Commercial-Share Alike 2.0 UK: England & Wales</ulink> as published by Creative Commons.
|
||||
</para>
|
||||
<note>
|
||||
For the latest version of this manual associated with this
|
||||
Yocto Project release, see the
|
||||
<ulink url='&YOCTO_DOCS_BSP_URL;'>Yocto Project Board Support Package (BSP) Developer's Guide</ulink>
|
||||
from the Yocto Project website.
|
||||
Due to production processes, there could be differences between the Yocto Project
|
||||
documentation bundled in the release tarball and the
|
||||
<ulink url='&YOCTO_DOCS_BSP_URL;'>Yocto Project Board Support Package (BSP) Developer's Guide</ulink> on
|
||||
the <ulink url='&YOCTO_HOME_URL;'>Yocto Project</ulink> website.
|
||||
For the latest version of this manual, see the manual on the website.
|
||||
</note>
|
||||
</legalnotice>
|
||||
|
||||
|
||||
@@ -34,13 +34,11 @@
|
||||
The BSP consists of a file structure inside a base directory.
|
||||
Collectively, you can think of the base directory and the file structure
|
||||
as a BSP Layer.
|
||||
Although not a strict requirement, layers in the Yocto Project use the
|
||||
following well established naming convention:
|
||||
BSP Layers use the following naming convention:
|
||||
<literallayout class='monospaced'>
|
||||
meta-<bsp_name>
|
||||
</literallayout>
|
||||
The string "meta-" is prepended to the machine or platform name, which is
|
||||
"bsp_name" in the above form.
|
||||
"bsp_name" is a placeholder for the machine or platform name.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -54,7 +52,7 @@
|
||||
definition and from it build an image.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
BBLAYERS ?= " \
|
||||
BBLAYERS = ?" \
|
||||
/usr/local/src/yocto/meta \
|
||||
/usr/local/src/yocto/meta-yocto \
|
||||
/usr/local/src/yocto/meta-yocto-bsp \
|
||||
@@ -183,15 +181,14 @@
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay/
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay/xorg.conf
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay-noemgd/
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay-noemgd/xorg.conf
|
||||
meta-crownbay/recipes-kernel/
|
||||
meta-crownbay/recipes-kernel/linux/
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.4.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.8.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.10.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto-dev.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto-rt_3.2.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto-rt_3.4.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto-rt_3.8.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto-rt_3.10.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.2.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.4.bbappend
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
@@ -259,9 +256,8 @@
|
||||
This file provides information on where to locate the BSP source files.
|
||||
For example, information provides where to find the sources that comprise
|
||||
the images shipped with the BSP.
|
||||
Information is also included to help you find the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#metadata'>Metadata</ulink>
|
||||
used to generate the images that ship with the BSP.
|
||||
Information is also included to help you find the metadata used to generate the images
|
||||
that ship with the BSP.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -277,7 +273,7 @@
|
||||
<para>
|
||||
This optional area contains useful pre-built kernels and user-space filesystem
|
||||
images appropriate to the target system.
|
||||
This directory typically contains graphical (e.g. Sato) and minimal live images
|
||||
This directory typically contains graphical (e.g. sato) and minimal live images
|
||||
when the BSP tarball has been created and made available in the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project</ulink> website.
|
||||
You can use these kernels and images to get a system running and quickly get started
|
||||
@@ -316,24 +312,24 @@
|
||||
<para>
|
||||
<literallayout class='monospaced'>
|
||||
# We have a conf and classes directory, add to BBPATH
|
||||
BBPATH .= ":${LAYERDIR}"
|
||||
BBPATH := "${BBPATH}:${LAYERDIR}"
|
||||
|
||||
# We have a recipes directory, add to BBFILES
|
||||
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
|
||||
${LAYERDIR}/recipes-*/*/*.bbappend"
|
||||
BBFILES := "${BBFILES} ${LAYERDIR}/recipes-*/*.bb \
|
||||
${LAYERDIR}/recipes-*/*.bbappend"
|
||||
|
||||
BBFILE_COLLECTIONS += "bsp"
|
||||
BBFILE_PATTERN_bsp = "^${LAYERDIR}/"
|
||||
BBFILE_PATTERN_bsp := "^${LAYERDIR}/"
|
||||
BBFILE_PRIORITY_bsp = "6"
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To illustrate the string substitutions, here are the corresponding statements
|
||||
from the Crown Bay <filename>conf/layer.conf</filename> file:
|
||||
To illustrate the string substitutions, here are the last three statements from the Crown
|
||||
Bay <filename>conf/layer.conf</filename> file:
|
||||
<literallayout class='monospaced'>
|
||||
BBFILE_COLLECTIONS += "crownbay"
|
||||
BBFILE_PATTERN_crownbay = "^${LAYERDIR}/"
|
||||
BBFILE_PATTERN_crownbay := "^${LAYERDIR}/"
|
||||
BBFILE_PRIORITY_crownbay = "6"
|
||||
</literallayout>
|
||||
</para>
|
||||
@@ -374,6 +370,15 @@
|
||||
<para>
|
||||
Each BSP Layer requires at least one machine file.
|
||||
However, you can supply more than one file.
|
||||
For example, in the Crown Bay BSP shown earlier in this section, the
|
||||
<filename>conf/machine</filename> directory contains two configuration files:
|
||||
<filename>crownbay.conf</filename> and <filename>crownbay-noemgd.conf</filename>.
|
||||
The <filename>crownbay.conf</filename> file is used for the Crown Bay BSP
|
||||
that supports the <trademark class='registered'>Intel</trademark> Embedded
|
||||
Media and Graphics Driver (<trademark class='registered'>Intel</trademark>
|
||||
EMGD), while the <filename>crownbay-noemgd.conf</filename> file is used for the
|
||||
Crown Bay BSP that does not support the <trademark class='registered'>Intel</trademark>
|
||||
EMGD.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -388,25 +393,24 @@
|
||||
Tuning files are found in the <filename>meta/conf/machine/include</filename>
|
||||
directory within the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>.
|
||||
Tuning files can also reside in the BSP Layer itself.
|
||||
For example, the <filename>ia32-base.inc</filename> file resides in the
|
||||
<filename>meta/conf/machine/include</filename> directory.
|
||||
<filename>meta-intel</filename> BSP Layer in <filename>conf/machine/include</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To use an include file, you simply include them in the machine configuration file.
|
||||
For example, the Crown Bay BSP <filename>crownbay.conf</filename> contains the
|
||||
For example, the Crown Bay BSP <filename>crownbay.conf</filename> has the
|
||||
following statements:
|
||||
<literallayout class='monospaced'>
|
||||
require conf/machine/include/tune-atom.inc
|
||||
require conf/machine/include/ia32-base.inc
|
||||
require conf/machine/include/meta-intel.inc
|
||||
require conf/machine/include/meta-intel-emgd.inc
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-misc-recipes'>
|
||||
<title>Miscellaneous BSP-Specific Recipe Files</title>
|
||||
<title>Miscellaneous Recipe Files</title>
|
||||
<para>
|
||||
You can find these files in the BSP Layer at:
|
||||
<literallayout class='monospaced'>
|
||||
@@ -418,13 +422,15 @@
|
||||
This optional directory contains miscellaneous recipe files for the BSP.
|
||||
Most notably would be the formfactor files.
|
||||
For example, in the Crown Bay BSP there is the
|
||||
<filename>formfactor_0.0.bbappend</filename> file, which is an
|
||||
append file used to augment the recipe that starts the build.
|
||||
Furthermore, there are machine-specific settings used during the
|
||||
build that are defined by the <filename>machconfig</filename> file.
|
||||
In the Crown Bay example, two <filename>machconfig</filename> files
|
||||
exist: one that supports the Intel® Embedded Media and Graphics
|
||||
Driver (Intel® EMGD) and one that does not:
|
||||
<filename>formfactor_0.0.bbappend</filename> file, which is an append file used
|
||||
to augment the recipe that starts the build.
|
||||
Furthermore, there are machine-specific settings used during the build that are
|
||||
defined by the <filename>machconfig</filename> files.
|
||||
In the Crown Bay example, two <filename>machconfig</filename> files exist:
|
||||
one that supports the
|
||||
<trademark class='registered'>Intel</trademark> Embedded
|
||||
Media and Graphics Driver (<trademark class='registered'>Intel</trademark>
|
||||
EMGD) and one that does not:
|
||||
<literallayout class='monospaced'>
|
||||
meta-crownbay/recipes-bsp/formfactor/formfactor/crownbay/machconfig
|
||||
meta-crownbay/recipes-bsp/formfactor/formfactor/crownbay-noemgd/machconfig
|
||||
@@ -455,13 +461,16 @@
|
||||
This optional directory contains recipes for the BSP if it has
|
||||
special requirements for graphics support.
|
||||
All files that are needed for the BSP to support a display are kept here.
|
||||
For example, the Crown Bay BSP's <filename>xorg.conf</filename> file
|
||||
detects the graphics support needed (i.e. the Intel® Embedded Media
|
||||
Graphics Driver (EMGD) or the Video Electronics Standards Association
|
||||
(VESA) graphics):
|
||||
For example, the Crown Bay BSP contains two versions of the
|
||||
<filename>xorg.conf</filename> file.
|
||||
The version in <filename>crownbay</filename> builds a BSP that supports the
|
||||
<trademark class='registered'>Intel</trademark> Embedded Media Graphics Driver (EMGD),
|
||||
while the version in <filename>crownbay-noemgd</filename> builds
|
||||
a BSP that supports Video Electronics Standards Association (VESA) graphics only:
|
||||
<literallayout class='monospaced'>
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay/xorg.conf
|
||||
meta-crownbay/recipes-graphics/xorg-xserver/xserver-xf86-config/crownbay-noemgd/xorg.conf
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
@@ -487,28 +496,28 @@
|
||||
the <filename>meta-<bsp_name>/recipes-kernel/linux</filename> directory).
|
||||
</para>
|
||||
<para>
|
||||
Suppose you are using the <filename>linux-yocto_3.10.bb</filename> recipe to build
|
||||
Suppose you are using the <filename>linux-yocto_3.4.bb</filename> recipe to build
|
||||
the kernel.
|
||||
In other words, you have selected the kernel in your
|
||||
<filename><bsp_name>.conf</filename> file by adding these types
|
||||
of statements:
|
||||
<literallayout class='monospaced'>
|
||||
PREFERRED_PROVIDER_virtual/kernel ?= "linux-yocto"
|
||||
PREFERRED_VERSION_linux-yocto ?= "3.10%"
|
||||
PREFERRED_VERSION_linux-yocto = "3.4%"
|
||||
</literallayout>
|
||||
<note>
|
||||
When the preferred provider is assumed by default, the
|
||||
<filename>PREFERRED_PROVIDER</filename> statement does not appear in the
|
||||
<filename><bsp_name>.conf</filename> file.
|
||||
</note>
|
||||
You would use the <filename>linux-yocto_3.10.bbappend</filename> file to append
|
||||
You would use the <filename>linux-yocto_3.4.bbappend</filename> file to append
|
||||
specific BSP settings to the kernel, thus configuring the kernel for your particular BSP.
|
||||
</para>
|
||||
<para>
|
||||
As an example, look at the existing Crown Bay BSP.
|
||||
The append file used is:
|
||||
<literallayout class='monospaced'>
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.10.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.4.bbappend
|
||||
</literallayout>
|
||||
The following listing shows the file.
|
||||
Be aware that the actual commit ID strings in this example listing might be different
|
||||
@@ -517,18 +526,43 @@
|
||||
<literallayout class='monospaced'>
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay = "crownbay"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay-noemgd = "crownbay-noemgd"
|
||||
KMACHINE_crownbay-noemgd = "crownbay"
|
||||
KBRANCH_crownbay-noemgd = "standard/crownbay"
|
||||
KERNEL_FEATURES_append_crownbay-noemgd = " cfg/vesafb"
|
||||
KMACHINE_crownbay-noemgd = "crownbay"
|
||||
KBRANCH_crownbay-noemgd = "standard/crownbay"
|
||||
|
||||
LINUX_VERSION = "3.10.11"
|
||||
SRCREV_machine_pn-linux-yocto_crownbay ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
SRCREV_emgd_pn-linux-yocto_crownbay ?= "86643bdd8cbad616a161ab91f51108cf0da827bc"
|
||||
|
||||
SRCREV_meta_crownbay-noemgd = "285f93bf942e8f6fa678ffc6cc53696ed5400718"
|
||||
SRCREV_machine_crownbay-noemgd = "702040ac7c7ec66a29b4d147665ccdd0ff015577"
|
||||
SRCREV_machine_pn-linux-yocto_crownbay-noemgd ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay-noemgd ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
|
||||
KSRC_linux_yocto_3_4 ?= "git.yoctoproject.org/linux-yocto-3.4.git"
|
||||
SRC_URI_crownbay = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;nocheckout=1;branch=${KBRANCH},meta,emgd-1.14;name=machine,meta,emgd"
|
||||
SRC_URI_crownbay-noemgd = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;nocheckout=1;branch=${KBRANCH},meta;name=machine,meta"
|
||||
</literallayout>
|
||||
This append file contains statements used to support the Crown Bay BSP.
|
||||
The file defines <filename>crownbay</filename> as the
|
||||
This append file contains statements used to support the Crown Bay BSP for both
|
||||
<trademark class='registered'>Intel</trademark> EMGD and the VESA graphics.
|
||||
The build process, in this case, recognizes and uses only the statements that
|
||||
apply to the defined machine name - <filename>crownbay</filename> in this case.
|
||||
So, the applicable statements in the <filename>linux-yocto_3.4.bbappend</filename>
|
||||
file are follows:
|
||||
<literallayout class='monospaced'>
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay = "crownbay"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
|
||||
SRCREV_machine_pn-linux-yocto_crownbay ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
SRCREV_emgd_pn-linux-yocto_crownbay ?= "86643bdd8cbad616a161ab91f51108cf0da827bc"
|
||||
</literallayout>
|
||||
The append file defines <filename>crownbay</filename> as the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-COMPATIBLE_MACHINE'><filename>COMPATIBLE_MACHINE</filename></ulink>
|
||||
and uses the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KMACHINE'><filename>KMACHINE</filename></ulink> variable to
|
||||
@@ -536,15 +570,18 @@
|
||||
machine name used by the Linux Yocto kernel.
|
||||
The file also uses the optional
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KBRANCH'><filename>KBRANCH</filename></ulink> variable
|
||||
to ensure the build process uses the <filename>standard/crownbay</filename>
|
||||
to ensure the build process uses the <filename>standard/default/crownbay</filename>
|
||||
kernel branch.
|
||||
The
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_FEATURES'><filename>KERNEL_FEATURES</filename></ulink>
|
||||
variable enables features specific to the kernel.
|
||||
Finally, the append file points to specific commits in the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink> Git
|
||||
repository and the <filename>meta</filename> Git repository branches to identify the
|
||||
exact kernel needed to build the Crown Bay BSP.
|
||||
<note>
|
||||
For <filename>crownbay</filename>, a specific commit is also needed to point
|
||||
to the branch that supports EMGD graphics.
|
||||
At a minimum, every BSP points to the
|
||||
<filename>machine</filename> and <filename>meta</filename> commits.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -560,9 +597,9 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For example, suppose you had some configuration options in a file called
|
||||
For example, suppose you had a some configuration options in a file called
|
||||
<filename>network_configs.cfg</filename>.
|
||||
You can place that file inside a directory named <filename>linux-yocto</filename> and then add
|
||||
You can place that file inside a directory named <filename>/linux-yocto</filename> and then add
|
||||
a <filename>SRC_URI</filename> statement such as the following to the append file.
|
||||
When the OpenEmbedded build system builds the kernel, the configuration options are
|
||||
picked up and applied.
|
||||
@@ -584,8 +621,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'><filename>FILESEXTRAPATHS</filename></ulink>
|
||||
variable is in boilerplate form in the
|
||||
The <filename>FILESEXTRAPATHS</filename> variable is in boilerplate form in the
|
||||
previous example in order to make it easy to do that.
|
||||
This variable must be in your layer or BitBake will not find the patches or
|
||||
configurations even if you have them in your <filename>SRC_URI</filename>.
|
||||
@@ -625,8 +661,8 @@
|
||||
<para>
|
||||
Certain requirements exist for a released BSP to be considered
|
||||
compliant with the Yocto Project.
|
||||
Additionally, recommendations also exist.
|
||||
This section describes the requirements and recommendations for
|
||||
Additionally, a single recommendation also exists.
|
||||
This section describes the requirements and recommendation for
|
||||
released BSPs.
|
||||
</para>
|
||||
|
||||
@@ -647,11 +683,11 @@
|
||||
You should consult the packaging and distribution guidelines for your
|
||||
specific release process.
|
||||
For an example of packaging and distribution requirements, see the
|
||||
"<ulink url='https://wiki.yoctoproject.org/wiki/Third_Party_BSP_Release_Process'>Third Party BSP Release Process</ulink>"
|
||||
wiki page.</para></listitem>
|
||||
<ulink url='https://wiki.yoctoproject.org/wiki/Third_Party_BSP_Release_Process'>Third
|
||||
Party BSP Release Process</ulink> wiki page.</para></listitem>
|
||||
<listitem><para>The requirements for the BSP as it is made available to a developer
|
||||
are completely independent of the released form of the BSP.
|
||||
For example, the BSP Metadata can be contained within a Git repository
|
||||
For example, the BSP metadata can be contained within a Git repository
|
||||
and could have a directory structure completely different from what appears
|
||||
in the officially released BSP layer.</para></listitem>
|
||||
<listitem><para>It is not required that specific packages or package
|
||||
@@ -682,7 +718,7 @@
|
||||
as outlined in <filename>recipes.txt</filename>.
|
||||
If you cannot find a category in <filename>recipes.txt</filename>
|
||||
to fit a particular recipe, you can make up your own
|
||||
<filename>recipes-*</filename> subdirectory.
|
||||
<filename>recipe-*</filename> subdirectory.
|
||||
You can find <filename>recipes.txt</filename> in the
|
||||
<filename>meta</filename> directory of the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>,
|
||||
@@ -700,12 +736,12 @@
|
||||
recipes.
|
||||
The recipes themselves should follow the general guidelines
|
||||
for recipes used in the Yocto Project found in the
|
||||
"<ulink url='https://wiki.yoctoproject.org/wiki/Recipe_%26_Patch_Style_Guide'>Yocto Recipe and Patch Style Guide</ulink>".
|
||||
</para></listitem>
|
||||
<ulink url='https://wiki.yoctoproject.org/wiki/Recipe_%26_Patch_Style_Guide'>Yocto
|
||||
Recipe and Patch Style Guide</ulink>.</para></listitem>
|
||||
<listitem><para><emphasis>License File:</emphasis>
|
||||
You must include a license file in the
|
||||
<filename>meta-<bsp_name></filename> directory.
|
||||
This license covers the BSP Metadata as a whole.
|
||||
This license covers the BSP metadata as a whole.
|
||||
You must specify which license to use since there is no
|
||||
default license if one is not specified.
|
||||
See the
|
||||
@@ -734,21 +770,17 @@
|
||||
For example, this information includes information on
|
||||
special variables needed to satisfy a EULA,
|
||||
or instructions on information needed to build or distribute
|
||||
binaries built from the BSP Metadata.</para></listitem>
|
||||
binaries built from the BSP metadata.</para></listitem>
|
||||
<listitem><para>The name and contact information for the
|
||||
BSP layer maintainer.
|
||||
This is the person to whom patches and questions should
|
||||
be sent.
|
||||
For information on how to find the right person, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#how-to-submit-a-change'>How to Submit a Change</ulink>"
|
||||
section in the Yocto Project Development Manual.
|
||||
</para></listitem>
|
||||
be sent.</para></listitem>
|
||||
<listitem><para>Instructions on how to build the BSP using the BSP
|
||||
layer.</para></listitem>
|
||||
<listitem><para>Instructions on how to boot the BSP build from
|
||||
the BSP layer.</para></listitem>
|
||||
<listitem><para>Instructions on how to boot the binary images
|
||||
contained in the <filename>binary</filename> directory,
|
||||
contained in the <filename>/binary</filename> directory,
|
||||
if present.</para></listitem>
|
||||
<listitem><para>Information on any known bugs or issues that users
|
||||
should know about when either building or booting the BSP
|
||||
@@ -759,7 +791,7 @@
|
||||
<filename>meta-<bsp_name></filename> directory.
|
||||
This file specifies exactly where you can find the sources used to
|
||||
generate the binary images contained in the
|
||||
<filename>binary</filename> directory, if present.
|
||||
<filename>/binary</filename> directory, if present.
|
||||
See the
|
||||
<ulink url='&YOCTO_GIT_URL;/cgit.cgi/meta-intel/tree/meta-fri2/README.sources'><filename>README.sources</filename></ulink>
|
||||
file for the Fish River Island 2 BSP in the <filename>meta-fri2</filename> BSP layer
|
||||
@@ -770,9 +802,9 @@
|
||||
This file identifies the <filename>meta-<bsp_name></filename>
|
||||
BSP layer as a layer to the build system.</para></listitem>
|
||||
<listitem><para><emphasis>Machine Configuration File:</emphasis>
|
||||
You must include one or more <filename>conf/machine/<bsp_name>.conf</filename>
|
||||
files in the <filename>meta-<bsp_name></filename> directory.
|
||||
These configuration files define machine targets that can be built
|
||||
You must include a <filename>conf/machine/<bsp_name>.conf</filename>
|
||||
in the <filename>meta-<bsp_name></filename> directory.
|
||||
This configuration file defines a machine target that can be built
|
||||
using the BSP layer.
|
||||
Multiple machine configuration files define variations of machine
|
||||
configurations that are supported by the BSP.
|
||||
@@ -785,9 +817,8 @@
|
||||
BSP layers for each target.
|
||||
<note>It is completely possible for a developer to structure the
|
||||
working repository as a conglomeration of unrelated BSP
|
||||
files, and to possibly generate BSPs targeted for release
|
||||
from that directory using scripts or some other mechanism
|
||||
(e.g. <filename>meta-yocto-bsp</filename> layer).
|
||||
files, and to possibly generate specifically targeted 'release' BSPs
|
||||
from that directory using scripts or some other mechanism.
|
||||
Such considerations are outside the scope of this document.</note>
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
@@ -843,59 +874,31 @@
|
||||
If you plan on customizing a recipe for a particular BSP, you need to do the
|
||||
following:
|
||||
<itemizedlist>
|
||||
<listitem><para>Create a <filename>.bbappend</filename>
|
||||
file for the modified recipe.
|
||||
For information on using append files, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#using-bbappend-files'>Using .bbappend Files</ulink>"
|
||||
section in the Yocto Project Development Manual.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Ensure your directory structure in the BSP layer
|
||||
that supports your machine is such that it can be found
|
||||
by the build system.
|
||||
See the example later in this section for more information.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Put the append file in a directory whose name matches
|
||||
the machine's name and is located in an appropriate
|
||||
sub-directory inside the BSP layer (i.e.
|
||||
<filename>recipes-bsp</filename>, <filename>recipes-graphics</filename>,
|
||||
<filename>recipes-core</filename>, and so forth).
|
||||
</para></listitem>
|
||||
<listitem><para>Place the BSP-specific files in the directory named for
|
||||
your machine inside the BSP layer.
|
||||
</para></listitem>
|
||||
<listitem><para>Include within the BSP layer a <filename>.bbappend</filename>
|
||||
file for the modified recipe.</para></listitem>
|
||||
<listitem><para>Place the BSP-specific file in the BSP's recipe
|
||||
<filename>.bbappend</filename> file path under a directory named
|
||||
after the machine.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Following is a specific example to help you better understand the process.
|
||||
Consider an example that customizes a recipe by adding
|
||||
To better understand this, consider an example that customizes a recipe by adding
|
||||
a BSP-specific configuration file named <filename>interfaces</filename> to the
|
||||
<filename>init-ifupdown_1.0.bb</filename> recipe for machine "xyz".
|
||||
<filename>netbase_5.0.bb</filename> recipe for machine "xyz".
|
||||
Do the following:
|
||||
<orderedlist>
|
||||
<listitem><para>Edit the <filename>init-ifupdown_1.0.bbappend</filename> file so that it
|
||||
<listitem><para>Edit the <filename>netbase_4.47.bbappend</filename> file so that it
|
||||
contains the following:
|
||||
<literallayout class='monospaced'>
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
|
||||
PRINC := "${@int(PRINC) + 2}"
|
||||
</literallayout>
|
||||
The append file needs to be in the
|
||||
<filename>meta-xyz/recipes-core/init-ifupdown</filename> directory.
|
||||
</para></listitem>
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para>Create and place the new <filename>interfaces</filename>
|
||||
configuration file in the BSP's layer here:
|
||||
<literallayout class='monospaced'>
|
||||
meta-xyz/recipes-core/init-ifupdown/files/xyz/interfaces
|
||||
</literallayout>
|
||||
The
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'><filename>FILESEXTRAPATHS</filename></ulink>
|
||||
variable in the append files extends the search path
|
||||
the build system uses to find files during the build.
|
||||
Consequently, for this example you need to have the
|
||||
<filename>files</filename> directory in the same location
|
||||
as your append file.</para></listitem>
|
||||
meta-xyz/recipes-core/netbase/files/xyz/interfaces
|
||||
</literallayout></para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
@@ -929,9 +932,9 @@
|
||||
|
||||
<para>
|
||||
For cases where you can substitute a free component and still
|
||||
maintain the system's functionality, the "Downloads" page from the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project website's</ulink>
|
||||
makes available de-featured BSPs
|
||||
maintain the system's functionality, the Yocto Project website's
|
||||
<ulink url='&YOCTO_HOME_URL;/download/all?keys=&download_type=1&download_version='>BSP
|
||||
Download Page</ulink> makes available de-featured BSPs
|
||||
that are completely free of any IP encumbrances.
|
||||
For these cases, you can use the substitution directly and
|
||||
without any further licensing requirements.
|
||||
@@ -984,9 +987,9 @@
|
||||
can build the encumbered image with no change at all
|
||||
to the normal build process.</para></listitem>
|
||||
<listitem><para><emphasis>Get a pre-built version of the BSP:</emphasis>
|
||||
You can get this type of BSP by visiting the
|
||||
"Downloads" page of the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project website</ulink>.
|
||||
You can get this type of BSP by visiting the Yocto Project website's
|
||||
<ulink url='&YOCTO_HOME_URL;/download'>Download</ulink>
|
||||
page and clicking on "BSP Downloads".
|
||||
You can download BSP tarballs that contain proprietary components
|
||||
after agreeing to the licensing
|
||||
requirements of each of the individually encumbered
|
||||
@@ -1021,7 +1024,7 @@
|
||||
The Yocto Project includes a couple of tools that enable
|
||||
you to create a <link linkend='bsp-layers'>BSP layer</link>
|
||||
from scratch and do basic configuration and maintenance
|
||||
of the kernel without ever looking at a Metadata file.
|
||||
of the kernel without ever looking at a metadata file.
|
||||
These tools are <filename>yocto-bsp</filename> and <filename>yocto-kernel</filename>,
|
||||
respectively.
|
||||
</para>
|
||||
@@ -1108,7 +1111,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For any sub-command, you can use the word "help" option just before the
|
||||
For any sub-command, you can also use the word 'help' just before the
|
||||
sub-command to get more extensive documentation:
|
||||
<literallayout class='monospaced'>
|
||||
$ yocto-bsp help create
|
||||
@@ -1130,7 +1133,7 @@
|
||||
The value of the 'karch' parameter determines the set of files
|
||||
that will be generated for the BSP, along with the specific set of
|
||||
'properties' that will be used to fill out the BSP-specific
|
||||
portions of the BSP. The possible values for the 'karch' parameter
|
||||
portions of the BSP. The possible values for the 'karch' paramter
|
||||
can be listed via 'yocto-bsp list karch'.
|
||||
|
||||
...
|
||||
@@ -1142,16 +1145,6 @@
|
||||
on them, you should find it relatively straightforward to discover the commands
|
||||
necessary to create a BSP and perform basic kernel maintenance on that BSP using
|
||||
the tools.
|
||||
<note>
|
||||
You can also use the <filename>yocto-layer</filename> tool to create
|
||||
a "generic" layer.
|
||||
For information on this tool, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#creating-a-general-layer-using-the-yocto-layer-script'>Creating a General Layer Using the yocto-layer Script</ulink>"
|
||||
section in the Yocto Project Development Guide.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The next sections provide a concrete starting point to expand on a few points that
|
||||
might not be immediately obvious or that could use further explanation.
|
||||
</para>
|
||||
@@ -1167,9 +1160,6 @@
|
||||
by the Yocto Project, as well as QEMU versions of the same.
|
||||
The default mode of the script's operation is to prompt you for information needed
|
||||
to generate the BSP layer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For the current set of BSPs, the script prompts you for various important
|
||||
parameters such as:
|
||||
<itemizedlist>
|
||||
@@ -1194,11 +1184,11 @@
|
||||
<literallayout class='monospaced'>
|
||||
$ yocto-bsp list karch
|
||||
Architectures available:
|
||||
powerpc
|
||||
i386
|
||||
x86_64
|
||||
arm
|
||||
qemu
|
||||
x86_64
|
||||
i386
|
||||
powerpc
|
||||
arm
|
||||
mips
|
||||
</literallayout>
|
||||
</para>
|
||||
@@ -1210,7 +1200,7 @@
|
||||
Of the available architectures, <filename>qemu</filename> is the only architecture
|
||||
that causes the script to prompt you further for an actual architecture.
|
||||
In every other way, this architecture is representative of how creating a BSP for
|
||||
an actual machine would work.
|
||||
a 'real' machine would work.
|
||||
The reason the example uses this architecture is because it is an emulated architecture
|
||||
and can easily be followed without requiring actual hardware.
|
||||
</para>
|
||||
@@ -1219,12 +1209,11 @@
|
||||
As the <filename>yocto-bsp create</filename> command runs, default values for
|
||||
the prompts appear in brackets.
|
||||
Pressing enter without supplying anything on the command line or pressing enter
|
||||
with an invalid response causes the script to accept the default value.
|
||||
and providing an invalid response causes the script to accept the default value.
|
||||
Once the script completes, the new <filename>meta-myarm</filename> BSP layer
|
||||
is created in the current working directory.
|
||||
This example assumes you have sourced the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
and are currently in the top-level folder of the
|
||||
This example assumes you have source the &OE_INIT_FILE; and are currently
|
||||
in the top-level folder of the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>.
|
||||
</para>
|
||||
|
||||
@@ -1232,59 +1221,54 @@
|
||||
Following is the complete example:
|
||||
<literallayout class='monospaced'>
|
||||
$ yocto-bsp create myarm qemu
|
||||
Checking basic git connectivity...
|
||||
Done.
|
||||
|
||||
Which qemu architecture would you like to use? [default: i386]
|
||||
1) i386 (32-bit)
|
||||
1) i386 (32-bit)
|
||||
2) x86_64 (64-bit)
|
||||
3) ARM (32-bit)
|
||||
4) PowerPC (32-bit)
|
||||
5) MIPS (32-bit)
|
||||
3
|
||||
Would you like to use the default (3.10) kernel? (y/n) [default: y] y
|
||||
Would you like to use the default (3.4) kernel? (y/n) [default: y]
|
||||
Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n] [default: y]
|
||||
Getting branches from remote repo git://git.yoctoproject.org/linux-yocto-3.10.git...
|
||||
Getting branches from remote repo git://git.yoctoproject.org/linux-yocto-3.4.git...
|
||||
Please choose a machine branch to base your new BSP branch on: [default: standard/base]
|
||||
1) standard/arm-versatile-926ejs
|
||||
2) standard/base
|
||||
3) standard/beagleboard
|
||||
4) standard/ck
|
||||
5) standard/crownbay
|
||||
6) standard/edf
|
||||
7) standard/emenlow
|
||||
8) standard/fri2
|
||||
9) standard/fsl-mpc8315e-rdb
|
||||
10) standard/minnow
|
||||
11) standard/mti-malta32
|
||||
12) standard/mti-malta64
|
||||
13) standard/qemuppc
|
||||
14) standard/routerstationpro
|
||||
15) standard/sys940x
|
||||
1) standard/arm-versatile-926ejs
|
||||
2) standard/base
|
||||
3) standard/beagleboard
|
||||
4) standard/cedartrail
|
||||
5) standard/crownbay
|
||||
6) standard/emenlow
|
||||
7) standard/fishriver
|
||||
8) standard/fri2
|
||||
9) standard/fsl-mpc8315e-rdb
|
||||
10) standard/mti-malta32
|
||||
11) standard/mti-malta64
|
||||
12) standard/qemuppc
|
||||
13) standard/routerstationpro
|
||||
14) standard/sys940x
|
||||
1
|
||||
Would you like SMP support? (y/n) [default: y]
|
||||
Does your BSP have a touchscreen? (y/n) [default: n]
|
||||
Does your BSP have a keyboard? (y/n) [default: y]
|
||||
|
||||
New qemu BSP created in meta-myarm
|
||||
</literallayout>
|
||||
Let's take a closer look at the example now:
|
||||
<orderedlist>
|
||||
<listitem><para>For the QEMU architecture,
|
||||
<listitem><para>For the <filename>qemu</filename> architecture,
|
||||
the script first prompts you for which emulated architecture to use.
|
||||
In the example, we use the ARM architecture.
|
||||
In the example, we use the <filename>arm</filename> architecture.
|
||||
</para></listitem>
|
||||
<listitem><para>The script then prompts you for the kernel.
|
||||
The default 3.10 kernel is acceptable.
|
||||
The default 3.4 kernel is acceptable.
|
||||
So, the example accepts the default.
|
||||
If you enter 'n', the script prompts you to further enter the kernel
|
||||
you do want to use (e.g. 3.2, 3.2_preempt-rt, and so forth.).</para></listitem>
|
||||
you do want to use (e.g. 3.0, 3.2_preempt-rt, and so forth.).</para></listitem>
|
||||
<listitem><para>Next, the script asks whether you would like to have a new
|
||||
branch created especially for your BSP in the local
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#local-kernel-files'>Linux Yocto Kernel</ulink>
|
||||
Git repository .
|
||||
If not, then the script re-uses an existing branch.</para>
|
||||
<para>In this example, the default (or "yes") is accepted.
|
||||
<para>In this example, the default (or 'yes') is accepted.
|
||||
Thus, a new branch is created for the BSP rather than using a common, shared
|
||||
branch.
|
||||
The new branch is the branch committed to for any patches you might later add.
|
||||
@@ -1296,8 +1280,8 @@
|
||||
you are now given the opportunity to select a particular machine branch on
|
||||
which to base your new BSP-specific machine branch
|
||||
(or to re-use if you had elected to not create a new branch).
|
||||
Because this example is generating an ARM-based BSP, the example
|
||||
uses <filename>#1</filename> at the prompt, which selects the ARM-versatile branch.
|
||||
Because this example is generating an <filename>arm</filename> BSP, the example
|
||||
uses <filename>#1</filename> at the prompt, which selects the arm-versatile branch.
|
||||
</para></listitem>
|
||||
<listitem><para>The remainder of the prompts are routine.
|
||||
Defaults are accepted for each.</para></listitem>
|
||||
@@ -1314,7 +1298,7 @@
|
||||
<filename>bblayers.conf</filename> file.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
BBLAYERS = ? " \
|
||||
BBLAYERS = ?" \
|
||||
/usr/local/src/yocto/meta \
|
||||
/usr/local/src/yocto/meta-yocto \
|
||||
/usr/local/src/yocto/meta-yocto-bsp \
|
||||
@@ -1328,7 +1312,7 @@
|
||||
</literallayout>
|
||||
Adding the layer to this file allows the build system to build the BSP and
|
||||
the <filename>yocto-kernel</filename> tool to be able to find the layer and
|
||||
other Metadata it needs on which to operate.
|
||||
other metadata it needs on which to operate.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -1367,13 +1351,6 @@
|
||||
patch list List the patches associated with a BSP
|
||||
patch add Patch the Yocto kernel for a BSP
|
||||
patch rm Remove patches from a BSP
|
||||
feature list List the features used by a BSP
|
||||
feature add Have a BSP use a feature
|
||||
feature rm Have a BSP stop using a feature
|
||||
features list List the features available to BSPs
|
||||
feature describe Describe a particular feature
|
||||
feature create Create a new BSP-local feature
|
||||
feature destroy Remove a BSP-local feature
|
||||
|
||||
See 'yocto-kernel help COMMAND' for more information on a specific command.
|
||||
|
||||
@@ -1450,7 +1427,7 @@
|
||||
Added items:
|
||||
CONFIG_MISC_DEVICES=y
|
||||
|
||||
$ yocto-kernel config add myarm CONFIG_YOCTO_TESTMOD=y
|
||||
$ yocto-kernel config add myarm KCONFIG_YOCTO_TESTMOD=y
|
||||
Added items:
|
||||
CONFIG_YOCTO_TESTMOD=y
|
||||
</literallayout>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user