Compare commits

..

31 Commits

Author SHA1 Message Date
Richard Purdie
d20a24310e self-hosted-image: Update poky revision to point at the 1.2 release branch
(From OE-Core rev: fd989e1bceef6df36619ba8944c8141abefd282e)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:21:45 +01:00
Richard Purdie
8e04664ffd self-hosted-image: Update poky revision to point at the 1.2 release branch
(From OE-Core rev: 117ca04008415ed0e6e10dcd373ab5f685b3225a)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:17:25 +01:00
Dongxiao Xu
3ab5d73f0c sanity.bbclass: Add a new case to issue sanity_check()
Judge if "SanityCheck" event is received, it will issue the
sanity_check() and send "SanityCheckPassed" back if succeeded.

(From OE-Core rev: 19704f9e69ecf09531687385b478b47f49fe372d)

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:15:47 +01:00
Dongxiao Xu
33f048240d Hob: Issue sanity check after parse is completed
In original scheme, sanity check is part of the parsing process. If a
sanity check fails, it means the parsing is failed and values in Hob
GUI may not correct.

With this commit, Hob will actively issue sanity_check() after the
parsing is completed.

This fixes [YOCTO #2361]

(Bitbake rev: 36968815dcc91759eeacb308bf4b294af416eee5)

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:15:42 +01:00
Dongxiao Xu
0bf04aa4ad Hob: Add proxy setting into setting's md5
If user changed the proxy setting, we will reparse configuration because
it may need sanity check.

(Bitbake rev: 0be54917cd88ea8f110027a7840ac69a411fd589)

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:15:36 +01:00
Dongxiao Xu
612555e6fe event.py: Add SanityCheck and SanityCheckPassed events
(Bitbake rev: 4d7bf9d813229b78b1cd87d06f7042e7923b7db4)

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-24 10:15:29 +01:00
Richard Purdie
35196ff703 self-hosted-image: Update poky revision to point at the 1.2 release branch
(From OE-Core rev: 85bebd85c4f6603ac8fc1290121c34b92cc434f9)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:13:00 +01:00
Lianhao Lu
0a48c697d7 pseudo: PR bump.
Bump PR value due to the commit
c6c701f424aeb502d20ff02d02712e56f4e259a5.

(From OE-Core rev: b6ee2880fccf04923ede31256ea418451cbf2e46)

Signed-off-by: Lianhao Lu <lianhao.lu@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:10:39 +01:00
Scott Rifenbark
7e56770a60 documentation: Updated Manual Revision Tables again.
After some discussion from Song and Richard, the dates in the
manual revision table has been updated to "April 2012" for the
1.2 release.

(From yocto-docs rev: b3fc2ec7c5aedb8ea0a2d502bdcd7e8f4092ed96)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:08:16 +01:00
Scott Rifenbark
9a548f0ee4 documentation: Replacements for "1.1" and "edison", etc.
I did a quick and dirty scrub over the manuals for the strings
"1.1" and "edison".  I found some instances that were not properly
variablized.  Also, discovered some references to the
linux-yocto-3.0-1.1.x.  All but one instance of this needed changed
to linux-yocto-3.2.

(From yocto-docs rev: 620fb4b7626defcefc8a039de09ae4599ee7f454)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:08:09 +01:00
Scott Rifenbark
946c650a47 documentation: Manual Revision Tables updated
Five tables updated for the five manuals that have the tables.
Used "May 2012" as the date.

(From yocto-docs rev: 0d4d46ba300c07ff9c73186506be5b409bef9d1b)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:08:02 +01:00
Scott Rifenbark
f99c947c32 documentation/yocto-project-qs/yocto-project-qs.xml: Added Build Appliance
Added a blurb about the Build Appliance to the start of the QS.

(From yocto-docs rev: b2766121c05740300fd5a6cea2f3b8a2f62db6e5)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:55 +01:00
Tom Zanussi
9ffbd2ef22 documentation/dev-manual/dev-manual-common-tasks.xml: removed kernel26
kernel26 is now obsolete so remove mention of it from the docs.
Removed from docs.

(From yocto-docs rev: 7b9da106d746192f802095584b04e3ee8347eabd)

Signed-off-by: Tom Zanussi <tom.zanussi@intel.com>
Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:48 +01:00
Scott Rifenbark
66625417b4 documentation/poky-ref-manual/ref-images.xml: added link
added the link for the Build Appliance page to the description of the
self-hosted image.

(From yocto-docs rev: 719ba4308489b29eefa7f08ddffb65bd5e41fc2c)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:40 +01:00
Joshua Lock
e95ce40abd scripts/hob: disable sanity checks when launching
This enables us to use the GUI to change any settings which might cause
sanity checks to fail, such as the proxy configuration.

(From OE-Core rev: fe98d1c7159636f123b27292bbd4cc224b532bf0)

Signed-off-by: Joshua Lock <josh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:33 +01:00
Joshua Lock
4a83ebbee0 sanity.bbclass: add variable to disable the sanity checks
It's useful for Hob to be able to disable the sanity checks completely
without marking them as passed so that the user can get into the GUI to
configure their settings, etc.

Add a variable, DISABLE_SANITY_CHECKS, to do so.

(From OE-Core rev: b022641f939bcfcdaddddc4db3af4d2dc70de832)

Signed-off-by: Joshua Lock <josh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:26 +01:00
Richard Purdie
90705b36ad python: Fix various contamination issues leading to broken/missing c modules
The move of libcrypto to /lib instead of /usr/lib has broken the _hashlib module
compilation. There were also a number of other failing modules which should
have been building correctly. This turned out partly to be the /lib issue
but also due to a number of native paths creeping into compiler commandlines.

These changes add in /lib as part of the searh directory and remove
a number of host contamination issues within setup.py. Post release we
should really further go through this file and just delete large sections
of it as its hard to be sure what strange paths python is injecting as
search paths.

This patch also fixes issues where re-execution of the compile task
would corrupt the Makefile in various ways, again leading to puzzling
paths within the configuration.

(From OE-Core rev: 20e2761e1da1cb5dcd267e161f2a6b6a429e9f39)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:19 +01:00
Richard Purdie
be5a5c7e7b bitbake.conf: Add a STAGING_BASELIBDIR variable that recipes can use to find base_libdir
(From OE-Core rev: 4697911991caa2f2a21dd43f54e0c4404d722873)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:12 +01:00
Joshua Lock
64471e9340 hob: enable sanity checks after launch
To ensure the users configuration is sanity tested enable the sanity
checks after the GUI has started but before any parsing is done.

(Bitbake rev: 244ce2b900ae6cecbeeccfe2056e61c132476261)

Signed-off-by: Joshua Lock <josh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-23 23:07:05 +01:00
Richard Purdie
4becd60e65 self-hosted-image: Update poky revision to point at the 1.2 release branch
(From OE-Core rev: b19af63)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 16:08:34 +01:00
Richard Purdie
9fcfda78b9 poky-tiny: Drop now unneeded DISTRO_FEATURES_LIBC_TOOLCHAIN (after gettext fix)
After the recent gettext dependency fix (commit 6e5cb40dfa
"gettext.bbclass: Ensure we don't overwrite other DEPENDS_GETTEXT values",
its no longer necessary to have to have these options to build meta-toolchain.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 16:05:58 +01:00
Richard Purdie
8558c3e1f4 initramfs-live-boot: Disable unionfs until its issue with the system rootdir are resolved
There are issues with the current unionfs when making a union mount over "/".
Until these are resolved we can't use unionfs for live booting so disable this
temporarily as a workaround.

unionfs is usable in other circumstances.

[YOCTO #2331 workaround]

(From OE-Core rev: 60ee26ae23132b916019d58e20b8c2e1ddd2b471)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:42 +01:00
Richard Purdie
0bfb42dbb6 pseudo: Drop nativesdk wrapper and link against old memcpy symbol
The -nativesdk pseudo wrapper setting LD_LIBRARY_PATH turned out to be a
bad idea since it can mix up different libc and lib-dl verisons which
may or may not work depending on the phase of the moon.

As an alternative to solving the original problem, this patch drops the
symbol version requirement on memcpy which allows pseudo to work with
libc's back to 2.7 which should be sufficient for our supported targets
using nativesdk.

[YOCTO #2299]
[YOCTO #2351]

(From OE-Core rev: c6c701f424aeb502d20ff02d02712e56f4e259a5)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:42 +01:00
Richard Purdie
37b069ea5d pseudo: Fix bashisms
(From OE-Core rev: 90e22bbb316088fa951d51e75de4e5424bd51ed6)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:42 +01:00
Richard Purdie
6d7260e8f6 package.bbclass: Ensure kernel modules get stripped
Kernel modules are not marked as executable but we do expect to strip them.
This patch adds in missing code to ensure we do this. Without this images
are getting sigificantly bloated in size.

(From OE-Core rev: 00b0a5f2f51bb3f88bbb9ae558c2859e3c1c406c)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:41 +01:00
Richard Purdie
236bda9ed6 gettext.bbclass: Ensure we don't overwrite other DEPENDS_GETTEXT values
In particular, this overwrites the value from cross-canadian.bbclass in
some cases which isn't the desired behaviour and unnecessarily
complicates/breaks the dependency chain.

(From OE-Core rev: 751ead4fa7d4120de906a1d9cb1d5a29357bebad)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:41 +01:00
Richard Purdie
375835092c qemu: Backport a patch to solve SSE2 instruction emulation issues
This fix addresses various issues seen in qemux86-64 images:
 * scroll bars in matchbox-terminal not working
 * files not appearing in pcmanfm
 * warnings on the console from glib/gobject about invalid gdouble values

Its due to an emulation issue in qemu which the backported patch fixes.

I managed to debug it to a specific function, Khem found the qemu patch
to backport, thanks Khem!

[YOCTO #1906]

(From OE-Core rev: 69d083f8b8d8f7d095ed5682d305870c4d93fe62)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-22 15:56:41 +01:00
Scott Rifenbark
2c3d4f5bee documentation/bsp-guide/bsp.xml: spelling corrected.
Reported-by: Robert P. J. Day <rpjday@crashcourse.ca>
(From yocto-docs rev: c0ee8ce391114f7a5b4f1c59fdf997ba4f3bcf75)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-18 16:42:15 +01:00
Scott Rifenbark
45114a9df0 documentation/poky-ref-manual/ref-images.xml: Added self-hosted image
I added the self-hosted-image to the list of images.

(From yocto-docs rev: a8265cb523705a374d23bf60aab5b7969ad937fc)

Signed-off-by: Scott Rifenbark <scott.m.rifenbark@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-18 16:42:05 +01:00
Richard Purdie
08290c6003 self-hosted-image: Update poky revision to point at the 1.2 release branch
(From OE-Core rev: 00256125873ff6f1630743a712e882e5f473a9d2)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2012-04-18 15:59:56 +01:00
Elizabeth Flanagan
729e7f774c distro.conf: Flipping for denzil
Flipping values in distro.conf for upcoming release

Signed-off-by: Elizabeth Flanagan <elizabeth.flanagan@intel.com>
2012-04-18 15:55:32 +01:00
3389 changed files with 184600 additions and 171366 deletions

21
.gitignore vendored
View File

@@ -1,23 +1,20 @@
*.pyc
*.pyo
/*.patch
build*/
build*/conf/local.conf
build*/conf/bblayers.conf
build*/downloads
build*/tmp/
build*/sstate-cache
pyshtables.py
pstage/
scripts/oe-git-proxy-socks
sources/
meta-*/
meta-*
!meta-skeleton
!meta-hob
hob-image-*.bb
!meta-demoapps
*.swp
*.orig
*.rej
*~
!meta-yocto
!meta-yocto-bsp
bitbake/doc/manual/html/
bitbake/doc/manual/pdf/
bitbake/doc/manual/txt/
bitbake/doc/manual/xhtml/
pull-*/

22
README
View File

@@ -18,7 +18,7 @@ e.g. for the hardware support. Poky is in turn a component of the Yocto Project.
The Yocto Project has extensive documentation about the system including a
reference manual which can be found at:
http://yoctoproject.org/documentation
http://yoctoproject.org/community/documentation
OpenEmbedded-Core is a layer containing the core metadata for current versions
of OpenEmbedded. It is distro-less (can build a functional image with
@@ -27,23 +27,3 @@ DISTRO = "") and contains only emulated machine support.
For information about OpenEmbedded, see the OpenEmbedded website:
http://www.openembedded.org/
Where to Send Patches
=====================
As Poky is an integration repository, patches against the various components
should be sent to their respective upstreams.
bitbake:
bitbake-devel@lists.openembedded.org
meta-yocto:
poky@yoctoproject.org
Most everything else should be sent to the OpenEmbedded Core mailing list. If
in doubt, check the oe-core git repository for the content you intend to modify.
Before sending, be sure the patches apply cleanly to the current oe-core git
repository.
openembedded-core@lists.openembedded.org
Note: The scripts directory should be treated with extra care as it is a mix
of oe-core and poky-specific files.

View File

@@ -40,17 +40,9 @@ from bb import cooker
from bb import ui
from bb import server
__version__ = "1.17.0"
__version__ = "1.15.1"
logger = logging.getLogger("BitBake")
# 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
try:
if sys.stdout.name == '<stdout>':
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
except:
pass
class BBConfiguration(object):
"""
@@ -64,11 +56,10 @@ class BBConfiguration(object):
def get_ui(config):
if not config.ui:
# modify 'ui' attribute because it is also read by cooker
config.ui = os.environ.get('BITBAKE_UI', 'knotty')
interface = config.ui
if config.ui:
interface = config.ui
else:
interface = 'knotty'
try:
# Dynamically load the UI based on the ui name. Although we
@@ -78,7 +69,7 @@ def get_ui(config):
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)
"Valid interfaces: depexp, goggle, ncurses, hob, knotty [default], knotty2." % interface)
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
@@ -126,9 +117,6 @@ Default BBFILES are the .bb files in the current directory.""")
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 = [])
@@ -150,13 +138,13 @@ Default BBFILES are the .bb files in the current directory.""")
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",
parser.add_option("-s", "--show-versions", help = "show current and preferred versions of all packages",
action = "store_true", dest = "show_versions", default = False)
parser.add_option("-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
action = "store_true", dest = "show_environment", default = False)
parser.add_option("-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list",
parser.add_option("-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax",
action = "store_true", dest = "dot_graph", default = False)
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
@@ -182,8 +170,6 @@ Default BBFILES are the .bb files in the current directory.""")
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)
@@ -214,10 +200,9 @@ Default BBFILES are the .bb files in the current directory.""")
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 "BBDEBUG" in os.environ:
level = int(os.environ["BBDEBUG"])
if level > configuration.debug:
configuration.debug = level
# Save a logfile for cooker into the current working directory. When the
# server is daemonized this logfile will be truncated.
cooker_logfile = os.path.join(os.getcwd(), "cooker.log")
bb.msg.init_msgconfig(configuration.verbose, configuration.debug,
configuration.debug_domains)
@@ -247,7 +232,7 @@ Default BBFILES are the .bb files in the current directory.""")
server.addcooker(cooker)
server.saveConnectionDetails()
server.detach()
server.detach(cooker_logfile)
# Should no longer need to ever reference cooker
del cooker

View File

@@ -1,102 +1,12 @@
#!/usr/bin/env python
# bitbake-diffsigs
# BitBake task signature data comparison utility
#
# 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
# 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 fnmatch
import optparse
import logging
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
import bb.tinfoil
import bb.siggen
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 """
if not hasattr(bb.siggen, 'find_siginfo'):
logger.error('Metadata does not support finding signature data files')
sys.exit(1)
filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-2:]
if not latestfiles:
logger.error('No sigdata files found matching %s %s' % (pn, taskname))
sys.exit(1)
elif len(latestfiles) < 2:
logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
sys.exit(1)
else:
# Define recursion callback
def recursecb(key, hash1, hash2):
hashes = [hash1, hash2]
hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
recout = []
if len(hashfiles) == 2:
out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
recout.extend(list(' ' + l for l in out2))
else:
recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
return recout
# Recurse into signature comparison
output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
if output:
print '\n'.join(output)
sys.exit(0)
parser = optparse.OptionParser(
usage = """
%prog -t recipename taskname
%prog sigdatafile1 sigdatafile2
%prog sigdatafile1""")
parser.add_option("-t", "--task",
help = "find the signature data files for last two runs of the specified task and compare them",
action="store_true", dest="taskmode")
options, args = parser.parse_args(sys.argv)
if len(args) == 1:
parser.print_help()
if len(sys.argv) > 2:
bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
else:
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:
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)
bb.siggen.dump_sigfile(sys.argv[1])

View File

@@ -6,6 +6,4 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), '
import bb.siggen
output = bb.siggen.dump_sigfile(sys.argv[1])
if output:
print '\n'.join(output)
bb.siggen.dump_sigfile(sys.argv[1])

View File

@@ -6,22 +6,10 @@
# Copyright (C) 2011 Mentor Graphics 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
# 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 cmd
import logging
import warnings
import os
import sys
import fnmatch
@@ -35,14 +23,26 @@ import bb.cache
import bb.cooker
import bb.providers
import bb.utils
import bb.tinfoil
from bb.cooker import state
import bb.fetch2
logger = logging.getLogger('BitBake')
warnings.filterwarnings("ignore", category=DeprecationWarning)
def main(args):
cmds = Commands()
# Set up logging
console = logging.StreamHandler(sys.stdout)
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
bb.msg.addDefaultlogFilter(console)
console.setFormatter(format)
logger.addHandler(console)
initialenv = os.environ.copy()
bb.utils.clean_environment()
cmds = Commands(initialenv)
if args:
# Allow user to specify e.g. show-layers instead of show_layers
args = [args[0].replace('-', '_')] + args[1:]
@@ -53,11 +53,42 @@ def main(args):
class Commands(cmd.Cmd):
def __init__(self):
def __init__(self, initialenv):
cmd.Cmd.__init__(self)
self.bbhandler = bb.tinfoil.Tinfoil()
self.returncode = 0
self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
self.config = Config(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
self.bblayers = (self.config_data.getVar('BBLAYERS', True) or "").split()
def register_idle_function(self, function, data):
pass
def prepare_cooker(self):
sys.stderr.write("Parsing recipes..")
logger.setLevel(logging.WARNING)
try:
while self.cooker.state in (state.initial, state.parsing):
self.cooker.updateCache()
except KeyboardInterrupt:
self.cooker.shutdown()
self.cooker.updateCache()
sys.exit(2)
logger.setLevel(logging.INFO)
sys.stderr.write("done.\n")
self.cooker_data = self.cooker.status
self.cooker_data.appends = self.cooker.appendlist
def check_prepare_cooker(self):
if not self.cooker_data:
self.prepare_cooker()
def default(self, line):
"""Handle unrecognised commands"""
@@ -82,13 +113,14 @@ class Commands(cmd.Cmd):
def do_show_layers(self, args):
"""show current configured layers"""
self.bbhandler.prepare(config_only = True)
self.check_prepare_cooker()
logger.plain('')
logger.plain("%s %s %s" % ("layer".ljust(20), "path".ljust(40), "priority"))
logger.plain('=' * 74)
for layerdir in self.bblayers:
layername = self.get_layer_name(layerdir)
layerpri = 0
for layer, _, regex, pri in self.bbhandler.cooker.status.bbfile_config_priorities:
for layer, _, regex, pri in self.cooker.status.bbfile_config_priorities:
if regex.match(os.path.join(layerdir, 'test')):
layerpri = pri
break
@@ -106,7 +138,7 @@ class Commands(cmd.Cmd):
def do_show_overlayed(self, args):
"""list overlayed recipes (where the same recipe exists in another layer)
"""list overlayed recipes (where the same recipe exists in another layer that has a higher layer priority)
usage: show-overlayed [-f] [-s]
@@ -119,7 +151,7 @@ Options:
recipes with the ones they overlay indented underneath
-s only list overlayed recipes where the version is the same
"""
self.bbhandler.prepare()
self.check_prepare_cooker()
show_filenames = False
show_same_ver_only = False
@@ -151,7 +183,7 @@ Options:
# factor - however, each layer.conf is free to either prepend or append to
# BBPATH (or indeed do crazy stuff with it). Thus the order in BBPATH might
# not be exactly the order present in bblayers.conf either.
bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
bbpath = str(self.config_data.getVar('BBPATH', True))
overlayed_class_found = False
for (classfile, classdirs) in classes.items():
if len(classdirs) > 1:
@@ -202,7 +234,7 @@ Options:
-m only list where multiple recipes (in the same layer or different
layers) exist for the same recipe name
"""
self.bbhandler.prepare()
self.check_prepare_cooker()
show_filenames = False
show_multi_provider_only = False
@@ -224,15 +256,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.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)
pkg_pn = self.cooker.status.pkg_pn
(latest_versions, preferred_versions) = bb.providers.findProviders(self.cooker.configuration.data, self.cooker.status, pkg_pn)
allproviders = bb.providers.allProviders(self.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.calc_bbfile_priority(fileitem) )
skiplist = self.cooker.skiplist.keys()
skiplist.sort( key=lambda fileitem: self.cooker.calc_bbfile_priority(fileitem) )
skiplist.reverse()
for fn in skiplist:
recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
@@ -340,7 +372,7 @@ build results (as the layer priority order has effectively changed).
logger.error('Directory %s exists and is non-empty, please clear it out first' % outputdir)
return
self.bbhandler.prepare()
self.check_prepare_cooker()
layers = self.bblayers
if len(arglist) > 2:
layernames = arglist[:-1]
@@ -370,8 +402,8 @@ build results (as the layer priority order has effectively changed).
appended_recipes = []
for layer in layers:
overlayed = []
for f in self.bbhandler.cooker.overlayed.iterkeys():
for of in self.bbhandler.cooker.overlayed[f]:
for f in self.cooker.overlayed.iterkeys():
for of in self.cooker.overlayed[f]:
if of.startswith(layer):
overlayed.append(of)
@@ -395,8 +427,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.appendlist:
appends = self.bbhandler.cooker.appendlist[f1]
if f1 in self.cooker_data.appends:
appends = self.cooker_data.appends[f1]
if appends:
logger.plain(' Applying appends to %s' % fdest )
for appendname in appends:
@@ -405,9 +437,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.appendlist.iterkeys():
for recipename in self.cooker_data.appends.iterkeys():
if recipename not in appended_recipes:
appends = self.bbhandler.cooker.appendlist[recipename]
appends = self.cooker_data.appends[recipename]
first_append = None
for appendname in appends:
layer = layer_path_match(appendname)
@@ -425,14 +457,14 @@ 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.status.bbfile_config_priorities:
for layername, pattern, regex, _ in self.cooker.status.bbfile_config_priorities:
if regex.match(os.path.join(layerdir, 'test')):
first_regex = regex
break
if first_regex:
# Find the BBFILES entries that match (which will have come from this conf/layer.conf file)
bbfiles = str(self.bbhandler.config_data.getVar('BBFILES', True)).split()
bbfiles = str(self.config_data.getVar('BBFILES', True)).split()
bbfiles_layer = []
for item in bbfiles:
if first_regex.match(item):
@@ -455,7 +487,7 @@ 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.status.bbfile_config_priorities:
for layer, _, regex, _ in self.cooker.status.bbfile_config_priorities:
if regex.match(filename):
for layerdir in self.bblayers:
if regex.match(os.path.join(layerdir, 'test')):
@@ -481,14 +513,14 @@ usage: show-appends
Recipes are listed with the bbappends that apply to them as subitems.
"""
self.bbhandler.prepare()
if not self.bbhandler.cooker.appendlist:
self.check_prepare_cooker()
if not self.cooker_data.appends:
logger.plain('No append files found')
return
logger.plain('=== Appended recipes ===')
logger.plain('State of append files:')
pnlist = list(self.bbhandler.cooker_data.pkg_pn.keys())
pnlist = list(self.cooker_data.pkg_pn.keys())
pnlist.sort()
for pn in pnlist:
self.show_appends_for_pn(pn)
@@ -496,19 +528,19 @@ Recipes are listed with the bbappends that apply to them as subitems.
self.show_appends_for_skipped()
def show_appends_for_pn(self, pn):
filenames = self.bbhandler.cooker_data.pkg_pn[pn]
filenames = self.cooker_data.pkg_pn[pn]
best = bb.providers.findBestProvider(pn,
self.bbhandler.cooker.configuration.data,
self.bbhandler.cooker_data,
self.bbhandler.cooker_data.pkg_pn)
self.cooker.configuration.data,
self.cooker_data,
self.cooker_data.pkg_pn)
best_filename = os.path.basename(best[3])
self.show_appends_output(filenames, best_filename)
def show_appends_for_skipped(self):
filenames = [os.path.basename(f)
for f in self.bbhandler.cooker.skiplist.iterkeys()]
for f in self.cooker.skiplist.iterkeys()]
self.show_appends_output(filenames, None, " (skipped)")
def show_appends_output(self, filenames, best_filename, name_suffix = ''):
@@ -534,7 +566,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
continue
basename = os.path.basename(filename)
appends = self.bbhandler.cooker.appendlist.get(basename)
appends = self.cooker_data.appends.get(basename)
if appends:
appended.append((basename, list(appends)))
else:
@@ -542,5 +574,22 @@ Recipes are listed with the bbappends that apply to them as subitems.
return appended, notappended
class Config(object):
def __init__(self, **options):
self.pkgs_to_build = []
self.debug_domains = []
self.extra_assume_provided = []
self.prefile = []
self.postfile = []
self.debug = 0
self.__dict__.update(options)
def __getattr__(self, attribute):
try:
return super(Config, self).__getattribute__(attribute)
except AttributeError:
return None
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]) or 0)

View File

@@ -1,38 +0,0 @@
#!/usr/bin/env python
#
# Copyright (C) 2012 Richard Purdie
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import sys, logging
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lib'))
import unittest
try:
import bb
except RuntimeError as exc:
sys.exit(str(exc))
tests = ["bb.tests.codeparser",
"bb.tests.cow",
"bb.tests.data",
"bb.tests.fetch",
"bb.tests.utils"]
for t in tests:
__import__(t)
unittest.main(argv=["bitbake-selftest"] + tests)

View File

@@ -1,122 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2012 Wind River Systems, Inc.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname( \
os.path.abspath(__file__))), 'lib'))
try:
import bb
except RuntimeError as exc:
sys.exit(str(exc))
import gtk
import optparse
import pygtk
from bb.ui.crumbs.hobwidget import HobAltButton, HobButton
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
from bb.ui.crumbs.hig.deployimagedialog import DeployImageDialog
from bb.ui.crumbs.hig.imageselectiondialog import ImageSelectionDialog
# I put all the fs bitbake supported here. Need more test.
DEPLOYABLE_IMAGE_TYPES = ["jffs2", "cramfs", "ext2", "ext3", "btrfs", "squashfs", "ubi", "vmdk"]
Title = "USB Image Writer"
class DeployWindow(gtk.Window):
def __init__(self, image_path=''):
super(DeployWindow, self).__init__()
if len(image_path) > 0:
valid = True
if not os.path.exists(image_path):
valid = False
lbl = "<b>Invalid image file path: %s.</b>\nPress <b>Select Image</b> to select an image." % image_path
else:
image_path = os.path.abspath(image_path)
extend_name = os.path.splitext(image_path)[1][1:]
if extend_name not in DEPLOYABLE_IMAGE_TYPES:
valid = False
lbl = "<b>Undeployable imge type: %s</b>\nPress <b>Select Image</b> to select an image." % extend_name
if not valid:
image_path = ''
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
crumbs_dialog.run()
crumbs_dialog.destroy()
self.deploy_dialog = DeployImageDialog(Title, image_path, self,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
| gtk.DIALOG_NO_SEPARATOR, None, standalone=True)
close_button = self.deploy_dialog.add_button("Close", gtk.RESPONSE_NO)
HobAltButton.style_button(close_button)
close_button.connect('clicked', gtk.main_quit)
write_button = self.deploy_dialog.add_button("Write USB image", gtk.RESPONSE_YES)
HobAltButton.style_button(write_button)
self.deploy_dialog.connect('select_image_clicked', self.select_image_clicked_cb)
self.deploy_dialog.connect('destroy', gtk.main_quit)
response = self.deploy_dialog.show()
def select_image_clicked_cb(self, dialog):
cwd = os.getcwd()
dialog = ImageSelectionDialog(cwd, DEPLOYABLE_IMAGE_TYPES, Title, self, gtk.FILE_CHOOSER_ACTION_SAVE )
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
HobAltButton.style_button(button)
button = dialog.add_button("Open", gtk.RESPONSE_YES)
HobAltButton.style_button(button)
response = dialog.run()
if response == gtk.RESPONSE_YES:
if not dialog.image_names:
lbl = "<b>No selections made</b>\nClicked the radio button to select a image."
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
crumbs_dialog.run()
crumbs_dialog.destroy()
dialog.destroy()
return
# get the full path of image
image_path = os.path.join(dialog.image_folder, dialog.image_names[0])
self.deploy_dialog.set_image_text_buffer(image_path)
self.deploy_dialog.set_image_path(image_path)
dialog.destroy()
def main():
parser = optparse.OptionParser(
usage = """%prog [-h] [image_file]
%prog writes bootable images to USB devices. You can
provide the image file on the command line or select it using the GUI.""")
options, args = parser.parse_args(sys.argv)
image_file = args[1] if len(args) > 1 else ''
dw = DeployWindow(image_file)
if __name__ == '__main__':
try:
main()
gtk.main()
except Exception:
import traceback
traceback.print_exc(3)

View File

@@ -1,68 +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) 2012 Wind River Systems, Inc.
#
# 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 is used for dumping the bb_cache.dat, the output format is:
# recipe_path PN PV PACKAGES
#
import os
import sys
import warnings
# For importing bb.cache
sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), '../lib'))
from bb.cache import CoreRecipeInfo
import cPickle as pickle
def main(argv=None):
"""
Get the mapping for the target recipe.
"""
if len(argv) != 1:
print >>sys.stderr, "Error, need one argument!"
return 2
cachefile = argv[0]
with open(cachefile, "rb") as cachefile:
pickled = pickle.Unpickler(cachefile)
while cachefile:
try:
key = pickled.load()
val = pickled.load()
except Exception:
break
if isinstance(val, CoreRecipeInfo) and (not val.skipped):
pn = val.pn
# Filter out the native recipes.
if key.startswith('virtual:native:') or pn.endswith("-native"):
continue
# 1.0 is the default version for a no PV recipe.
if val.__dict__.has_key("pv"):
pv = val.pv
else:
pv = "1.0"
print("%s %s %s %s" % (key, pn, pv, ' '.join(val.packages)))
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View File

@@ -10,8 +10,8 @@ if &compatible || version < 600
finish
endif
" .bb, .bbappend and .bbclass
au BufNewFile,BufRead *.{bb,bbappend,bbclass} set filetype=bitbake
" .bb and .bbclass
au BufNewFile,BufRead *.b{b,bclass} set filetype=bitbake
" .inc
au BufNewFile,BufRead *.inc set filetype=bitbake

View File

@@ -103,13 +103,7 @@ Show debug logging for the specified logging domains
.TP
.B \-P, \-\-profile
profile the command and print a report
.SH ENVIRONMENT VARIABLES
bitbake uses the following environment variables to control its
operation:
.TP
.B BITBAKE_UI
The bitbake user interface; overridden by the \fB-u\fP commandline option.
.SH AUTHORS
BitBake was written by

View File

@@ -228,7 +228,7 @@ addtask printdate before do_build</screen></para>
<para>'nostamp' - don't generate a stamp file for a task. This means the task is always rexecuted.</para>
<para>'fakeroot' - this task needs to be run in a fakeroot environment, obtained by adding the variables in FAKEROOTENV to the environment.</para>
<para>'umask' - the umask to run the task under.</para>
<para> For the 'deptask', 'rdeptask', 'depends', 'rdepends' and 'recrdeptask' flags please see the dependencies section.</para>
<para> For the 'deptask', 'rdeptask', 'recdeptask' and 'recrdeptask' flags please see the dependencies section.</para>
</section>
<section>
@@ -308,35 +308,37 @@ SRC_URI_append_1.0.7+ = "file://some_patch_which_the_new_versions_need.patch;pat
</section>
<section>
<title>Dependency handling</title>
<para>BitBake handles dependencies at the task level since to allow for efficient operation with multiple processed executing in parallel. A robust method of specifying task dependencies is therefore needed. </para>
<para>BitBake 1.7.x onwards works with the metadata at the task level since this is optimal when dealing with multiple threads of execution. A robust method of specifing task dependencies is therefore needed. </para>
<section>
<title>Dependencies internal to the .bb file</title>
<para>Where the dependencies are internal to a given .bb file, the dependencies are handled by the previously detailed addtask directive.</para>
</section>
<section>
<title>Build Dependencies</title>
<title>DEPENDS</title>
<para>DEPENDS lists build time dependencies. The 'deptask' flag for tasks is used to signify the task of each item listed in DEPENDS which must have completed before that task can be executed.</para>
<para><screen>do_configure[deptask] = "do_populate_staging"</screen></para>
<para>means the do_populate_staging task of each item in DEPENDS must have completed before do_configure can execute.</para>
</section>
<section>
<title>Runtime Dependencies</title>
<para>The PACKAGES variable lists runtime packages and each of these can have RDEPENDS and RRECOMMENDS runtime dependencies. The 'rdeptask' flag for tasks is used to signify the task of each item runtime dependency which must have completed before that task can be executed.</para>
<title>RDEPENDS</title>
<para>RDEPENDS lists runtime dependencies. The 'rdeptask' flag for tasks is used to signify the task of each item listed in RDEPENDS which must have completed before that task can be executed.</para>
<para><screen>do_package_write[rdeptask] = "do_package"</screen></para>
<para>means the do_package task of each item in RDEPENDS must have completed before do_package_write can execute.</para>
</section>
<section>
<title>Recursive Dependencies</title>
<para>These are specified with the 'recrdeptask' flag which is used signify the task(s) of dependencies which must have completed before that task can be executed. It works by looking though the build and runtime dependencies of the current recipe as well as any inter-task dependencies the task has, then adding a dependency on the listed task. It will then recurse through the dependencies of those tasks and so on.</para>
<para>It may be desireable to recurse not just through the dependencies of those tasks but through the build and runtime dependencies of dependent tasks too. If that is the case, the taskname itself should be referenced in the task list, e.g. do_a[recrdeptask] = "do_a do_b".</para>
<title>Recursive DEPENDS</title>
<para>These are specified with the 'recdeptask' flag and is used signify the task(s) of each DEPENDS which must have completed before that task can be executed. It applies recursively so the DEPENDS of each item in the original DEPENDS must be met and so on.</para>
</section>
<section>
<title>Recursive RDEPENDS</title>
<para>These are specified with the 'recrdeptask' flag and is used signify the task(s) of each RDEPENDS which must have completed before that task can be executed. It applies recursively so the RDEPENDS of each item in the original RDEPENDS must be met and so on. It also runs all DEPENDS first.</para>
</section>
<section>
<title>Inter task</title>
<para>The 'depends' flag for tasks is a more generic form of which allows an interdependency on specific tasks rather than specifying the data in DEPENDS.</para>
<para>The 'depends' flag for tasks is a more generic form of which allows an interdependency on specific tasks rather than specifying the data in DEPENDS or RDEPENDS.</para>
<para><screen>do_patch[depends] = "quilt-native:do_populate_staging"</screen></para>
<para>means the do_populate_staging task of the target quilt-native must have completed before the do_patch can execute.</para>
<para>The 'rdepends' flag works in a similar way but takes targets in the runtime namespace instead of the build time dependency namespace.</para>
</section>
</section>

View File

@@ -21,7 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
__version__ = "1.17.0"
__version__ = "1.15.1"
import sys
if sys.version_info < (2, 6, 0):
@@ -74,6 +74,11 @@ logger.setLevel(logging.DEBUG - 2)
# can result in construction of the various loggers.
import bb.msg
if "BBDEBUG" in os.environ:
level = int(os.environ["BBDEBUG"])
if level:
bb.msg.set_debug_level(level)
from bb import fetch2 as fetch
sys.modules['bb.fetch'] = sys.modules['bb.fetch2']

View File

@@ -29,7 +29,6 @@ import os
import sys
import logging
import shlex
import glob
import bb
import bb.msg
import bb.process
@@ -73,7 +72,7 @@ class TaskBase(event.Event):
self._task = t
self._package = d.getVar("PF", True)
event.Event.__init__(self)
self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
self._message = "package %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
def getTask(self):
return self._task
@@ -136,8 +135,7 @@ class LogTee(object):
def __repr__(self):
return '<LogTee {0}>'.format(self.name)
def flush(self):
self.outfile.flush()
def exec_func(func, d, dirs = None):
"""Execute an BB 'function'"""
@@ -176,19 +174,8 @@ def exec_func(func, d, dirs = None):
lockfiles = None
tempdir = data.getVar('T', d, 1)
# or func allows items to be executed outside of the normal
# task set, such as buildhistory
task = data.getVar('BB_RUNTASK', d, 1) or func
if task == func:
taskfunc = task
else:
taskfunc = "%s.%s" % (task, func)
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))
bb.utils.mkdirhier(tempdir)
runfile = os.path.join(tempdir, 'run.{0}.{1}'.format(func, os.getpid()))
with bb.utils.fileslocked(lockfiles):
if ispython:
@@ -219,8 +206,6 @@ def exec_func_python(func, d, runfile, cwd=None):
olddir = None
os.chdir(cwd)
bb.debug(2, "Executing python function %s" % func)
try:
comp = utils.better_compile(code, func, bbfile)
utils.better_exec(comp, {"d": d}, code, bbfile)
@@ -230,15 +215,13 @@ def exec_func_python(func, d, runfile, cwd=None):
raise FuncFailed(func, None)
finally:
bb.debug(2, "Python function %s finished" % func)
if cwd and olddir:
try:
os.chdir(olddir)
except OSError:
pass
def exec_func_shell(func, d, runfile, cwd=None):
def exec_func_shell(function, d, runfile, cwd=None):
"""Execute a shell function from the metadata
Note on directory behavior. The 'dirs' varflag should contain a list
@@ -251,18 +234,18 @@ def exec_func_shell(func, d, runfile, cwd=None):
with open(runfile, 'w') as script:
script.write('#!/bin/sh -e\n')
data.emit_func(func, script, d)
data.emit_func(function, script, d)
if bb.msg.loggerVerboseLogs:
script.write("set -x\n")
if cwd:
script.write("cd %s\n" % cwd)
script.write("%s\n" % func)
script.write("%s\n" % function)
os.chmod(runfile, 0775)
cmd = runfile
if d.getVarFlag(func, 'fakeroot'):
if d.getVarFlag(function, 'fakeroot'):
fakerootcmd = d.getVar('FAKEROOT', True)
if fakerootcmd:
cmd = [fakerootcmd, runfile]
@@ -272,15 +255,11 @@ def exec_func_shell(func, d, runfile, cwd=None):
else:
logfile = sys.stdout
bb.debug(2, "Executing shell function %s" % func)
try:
bb.process.run(cmd, shell=False, stdin=NULL, log=logfile)
except bb.process.CmdError:
logfn = d.getVar('BB_LOGFILE', True)
raise FuncFailed(func, logfn)
bb.debug(2, "Shell function %s finished" % func)
raise FuncFailed(function, logfn)
def _task_data(fn, task, d):
localdata = data.createCopy(d)
@@ -311,23 +290,8 @@ def _exec_task(fn, task, d, quieterr):
bb.fatal("T variable not set, unable to build")
bb.utils.mkdirhier(tempdir)
# Determine the logfile to generate
logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}'
logbase = logfmt.format(task=task, pid=os.getpid())
# Document the order of the tasks...
logorder = os.path.join(tempdir, 'log.task_order')
try:
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))
logbase = 'log.{0}.{1}'.format(task, os.getpid())
logfn = os.path.join(tempdir, logbase)
if loglink:
bb.utils.remove(loglink)
@@ -350,7 +314,6 @@ def _exec_task(fn, task, d, quieterr):
# Handle logfiles
si = file('/dev/null', 'r')
try:
bb.utils.mkdirhier(os.path.dirname(logfn))
logfile = file(logfn, 'w')
except OSError:
logger.exception("Opening log file '%s'", logfn)
@@ -377,7 +340,6 @@ def _exec_task(fn, task, d, quieterr):
bblogger.addHandler(errchk)
localdata.setVar('BB_LOGFILE', logfn)
localdata.setVar('BB_RUNTASK', task)
event.fire(TaskStarted(task, localdata), localdata)
try:
@@ -465,52 +427,15 @@ def stamp_internal(taskname, d, file_name):
stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
stampdir = os.path.dirname(stamp)
if bb.parse.cached_mtime_noerror(stampdir) == 0:
bb.utils.mkdirhier(stampdir)
bb.utils.mkdirhier(os.path.dirname(stamp))
return stamp
def stamp_cleanmask_internal(taskname, d, file_name):
"""
Internal stamp helper function to generate stamp cleaning mask
Returns the stamp path+filename
In the bitbake core, d can be a CacheData and file_name will be set.
When called in task context, d will be a data store, file_name will not be set
"""
taskflagname = taskname
if taskname.endswith("_setscene") and taskname != "do_setscene":
taskflagname = taskname.replace("_setscene", "")
if file_name:
stamp = d.stamp_base_clean[file_name].get(taskflagname) or d.stampclean[file_name]
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
else:
stamp = d.getVarFlag(taskflagname, 'stamp-base-clean', True) or d.getVar('STAMPCLEAN', True)
file_name = d.getVar('BB_FILENAME', True)
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
if not stamp:
return []
cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
def make_stamp(task, d, file_name = None):
"""
Creates/updates a stamp for a given task
(d can be a data dict or dataCache)
"""
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):
if "sigdata" in name:
continue
os.unlink(name)
stamp = stamp_internal(task, d, file_name)
# Remove the file and recreate to force timestamp
# change on broken NFS filesystems
@@ -533,24 +458,6 @@ def del_stamp(task, d, file_name = None):
stamp = stamp_internal(task, d, file_name)
bb.utils.remove(stamp)
def write_taint(task, d, file_name = None):
"""
Creates a "taint" file which will force the specified task and its
dependents to be re-run the next time by influencing the value of its
taskhash.
(d can be a data dict or dataCache)
"""
import uuid
if file_name:
taintfn = d.stamp[file_name] + '.' + task + '.taint'
else:
taintfn = d.getVar('STAMP', True) + '.' + task + '.taint'
bb.utils.mkdirhier(os.path.dirname(taintfn))
# The specific content of the taint file is not really important,
# we just need it to be random, so a random UUID is used
with open(taintfn, 'w') as taintf:
taintf.write(str(uuid.uuid4()))
def stampfile(taskname, d, file_name = None):
"""
Return the stamp for a given task
@@ -582,7 +489,6 @@ def add_tasks(tasklist, d):
deptask = data.expand(flags[name], d)
task_deps[name][task] = deptask
getTask('depends')
getTask('rdepends')
getTask('deptask')
getTask('rdeptask')
getTask('recrdeptask')
@@ -591,10 +497,9 @@ def add_tasks(tasklist, d):
getTask('noexec')
getTask('umask')
task_deps['parents'][task] = []
if 'deps' in flags:
for dep in flags['deps']:
dep = data.expand(dep, d)
task_deps['parents'][task].append(dep)
for dep in flags['deps']:
dep = data.expand(dep, d)
task_deps['parents'][task].append(dep)
# don't assume holding a reference
data.setVar('_task_deps', task_deps, d)

View File

@@ -1,12 +1,11 @@
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# BitBake Cache implementation
# BitBake 'Event' implementation
#
# Caching of bitbake variables before task execution
# Copyright (C) 2006 Richard Purdie
# Copyright (C) 2012 Intel Corporation
# but small sections based on code from bin/bitbake:
# Copyright (C) 2003, 2004 Chris Larson
@@ -43,7 +42,7 @@ except ImportError:
logger.info("Importing cPickle failed. "
"Falling back to a very slow implementation.")
__cache_version__ = "145"
__cache_version__ = "143"
def getCacheFile(path, filename, data_hash):
return os.path.join(path, filename + "." + data_hash)
@@ -76,13 +75,9 @@ class RecipeInfoCommon(object):
for task in tasks)
@classmethod
def flaglist(cls, flag, varlist, metadata, squash=False):
out_dict = dict((var, metadata.getVarFlag(var, flag, True))
def flaglist(cls, flag, varlist, metadata):
return dict((var, metadata.getVarFlag(var, flag, True))
for var in varlist)
if squash:
return dict((k,v) for (k,v) in out_dict.iteritems() if v)
else:
return out_dict
@classmethod
def getvar(cls, var, metadata):
@@ -130,11 +125,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
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)
self.stamp_base = self.flaglist('stamp-base', self.tasks, metadata)
self.stamp_base_clean = self.flaglist('stamp-base-clean', self.tasks, metadata)
self.stamp_extrainfo = self.flaglist('stamp-extra-info', self.tasks, metadata)
self.file_checksums = self.flaglist('file-checksums', self.tasks, metadata, True)
self.packages_dynamic = self.listvar('PACKAGES_DYNAMIC', metadata)
self.depends = self.depvar('DEPENDS', metadata)
self.provides = self.depvar('PROVIDES', metadata)
@@ -159,11 +151,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
cachedata.pkg_dp = {}
cachedata.stamp = {}
cachedata.stampclean = {}
cachedata.stamp_base = {}
cachedata.stamp_base_clean = {}
cachedata.stamp_extrainfo = {}
cachedata.file_checksums = {}
cachedata.fn_provides = {}
cachedata.pn_provides = defaultdict(list)
cachedata.all_depends = []
@@ -193,11 +182,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
cachedata.pkg_pepvpr[fn] = (self.pe, self.pv, self.pr)
cachedata.pkg_dp[fn] = self.defaultpref
cachedata.stamp[fn] = self.stamp
cachedata.stampclean[fn] = self.stampclean
cachedata.stamp_base[fn] = self.stamp_base
cachedata.stamp_base_clean[fn] = self.stamp_base_clean
cachedata.stamp_extrainfo[fn] = self.stamp_extrainfo
cachedata.file_checksums[fn] = self.file_checksums
provides = [self.pn]
for provide in self.provides:
@@ -405,12 +391,12 @@ class Cache(object):
"""Parse the specified filename, returning the recipe information"""
infos = []
datastores = cls.load_bbfile(filename, appends, configdata)
depends = []
depends = set()
for variant, data in sorted(datastores.iteritems(),
key=lambda i: i[0],
reverse=True):
virtualfn = cls.realfn2virtual(filename, variant)
depends = depends + (data.getVar("__depends", False) or [])
depends |= (data.getVar("__depends", False) or set())
if depends and not variant:
data.setVar("__depends", depends)
@@ -717,115 +703,4 @@ class CacheData(object):
for info in info_array:
info.add_cacheData(self, fn)
class MultiProcessCache(object):
"""
BitBake multi-process cache implementation
Used by the codeparser & file checksum caches
"""
def __init__(self):
self.cachefile = None
self.cachedata = self.create_cachedata()
self.cachedata_extras = self.create_cachedata()
def init_cache(self, d):
cachedir = (d.getVar("PERSISTENT_DIR", True) or
d.getVar("CACHE", True))
if cachedir in [None, '']:
return
bb.utils.mkdirhier(cachedir)
self.cachefile = os.path.join(cachedir, self.__class__.cache_file_name)
logger.debug(1, "Using cache in '%s'", self.cachefile)
try:
p = pickle.Unpickler(file(self.cachefile, "rb"))
data, version = p.load()
except:
return
if version != self.__class__.CACHE_VERSION:
return
self.cachedata = data
def internSet(self, items):
new = set()
for i in items:
new.add(intern(i))
return new
def compress_keys(self, data):
# Override in subclasses if desired
return
def create_cachedata(self):
data = [{}]
return data
def save_extras(self, d):
if not self.cachefile:
return
glf = bb.utils.lockfile(self.cachefile + ".lock", shared=True)
i = os.getpid()
lf = None
while not lf:
lf = bb.utils.lockfile(self.cachefile + ".lock." + str(i), retry=False)
if not lf or os.path.exists(self.cachefile + "-" + str(i)):
if lf:
bb.utils.unlockfile(lf)
lf = None
i = i + 1
continue
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)
def merge_data(self, source, dest):
for j in range(0,len(dest)):
for h in source[j]:
if h not in dest[j]:
dest[j][h] = source[j][h]
def save_merge(self, d):
if not self.cachefile:
return
glf = bb.utils.lockfile(self.cachefile + ".lock")
try:
p = pickle.Unpickler(file(self.cachefile, "rb"))
data, version = p.load()
except (IOError, EOFError):
data, version = None, None
if version != self.__class__.CACHE_VERSION:
data = self.create_cachedata()
for f in [y for y in os.listdir(os.path.dirname(self.cachefile)) if y.startswith(os.path.basename(self.cachefile) + '-')]:
f = os.path.join(os.path.dirname(self.cachefile), f)
try:
p = pickle.Unpickler(file(f, "rb"))
extradata, version = p.load()
except (IOError, EOFError):
extradata, version = self.create_cachedata(), None
if version != self.__class__.CACHE_VERSION:
continue
self.merge_data(extradata, data)
os.unlink(f)
self.compress_keys(data)
p = pickle.Pickler(file(self.cachefile, "wb"), -1)
p.dump([data, self.__class__.CACHE_VERSION])
bb.utils.unlockfile(glf)

View File

@@ -1,90 +0,0 @@
# Local file checksum cache implementation
#
# 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
# 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 stat
import bb.utils
import logging
from bb.cache import MultiProcessCache
logger = logging.getLogger("BitBake.Cache")
try:
import cPickle as pickle
except ImportError:
import pickle
logger.info("Importing cPickle failed. "
"Falling back to a very slow implementation.")
# mtime cache (non-persistent)
# based upon the assumption that files do not change during bitbake run
class FileMtimeCache(object):
cache = {}
def cached_mtime(self, f):
if f not in self.cache:
self.cache[f] = os.stat(f)[stat.ST_MTIME]
return self.cache[f]
def cached_mtime_noerror(self, f):
if f not in self.cache:
try:
self.cache[f] = os.stat(f)[stat.ST_MTIME]
except OSError:
return 0
return self.cache[f]
def update_mtime(self, f):
self.cache[f] = os.stat(f)[stat.ST_MTIME]
return self.cache[f]
def clear(self):
self.cache.clear()
# Checksum + mtime cache (persistent)
class FileChecksumCache(MultiProcessCache):
cache_file_name = "local_file_checksum_cache.dat"
CACHE_VERSION = 1
def __init__(self):
self.mtime_cache = FileMtimeCache()
MultiProcessCache.__init__(self)
def get_checksum(self, f):
entry = self.cachedata[0].get(f)
cmtime = self.mtime_cache.cached_mtime(f)
if entry:
(mtime, hashval) = entry
if cmtime == mtime:
return hashval
else:
bb.debug(2, "file %s changed mtime, recompute checksum" % f)
hashval = bb.utils.md5_file(f)
self.cachedata_extras[0][f] = (cmtime, hashval)
return hashval
def merge_data(self, source, dest):
for h in source[0]:
if h in dest:
(smtime, _) = source[0][h]
(dmtime, _) = dest[0][h]
if smtime > dmtime:
dest[0][h] = source[0][h]
else:
dest[0][h] = source[0][h]

View File

@@ -5,10 +5,10 @@ import os.path
import bb.utils, bb.data
from itertools import chain
from pysh import pyshyacc, pyshlex, sherrors
from bb.cache import MultiProcessCache
logger = logging.getLogger('BitBake.CodeParser')
PARSERCACHE_VERSION = 2
try:
import cPickle as pickle
@@ -32,56 +32,133 @@ def check_indent(codestr):
return codestr
pythonparsecache = {}
shellparsecache = {}
pythonparsecacheextras = {}
shellparsecacheextras = {}
class CodeParserCache(MultiProcessCache):
cache_file_name = "bb_codeparser.dat"
CACHE_VERSION = 2
def __init__(self):
MultiProcessCache.__init__(self)
self.pythoncache = self.cachedata[0]
self.shellcache = self.cachedata[1]
self.pythoncacheextras = self.cachedata_extras[0]
self.shellcacheextras = self.cachedata_extras[1]
def init_cache(self, d):
MultiProcessCache.init_cache(self, d)
# cachedata gets re-assigned in the parent
self.pythoncache = self.cachedata[0]
self.shellcache = self.cachedata[1]
def compress_keys(self, data):
# When the dicts are originally created, python calls intern() on the set keys
# which significantly improves memory usage. Sadly the pickle/unpickle process
# doesn't call intern() on the keys and results in the same strings being duplicated
# in memory. This also means pickle will save the same string multiple times in
# the cache file. By interning the data here, the cache file shrinks dramatically
# meaning faster load times and the reloaded cache files also consume much less
# memory. This is worth any performance hit from this loops and the use of the
# intern() data storage.
# Python 3.x may behave better in this area
for h in data[0]:
data[0][h]["refs"] = self.internSet(data[0][h]["refs"])
data[0][h]["execs"] = self.internSet(data[0][h]["execs"])
for h in data[1]:
data[1][h]["execs"] = self.internSet(data[1][h]["execs"])
return
def create_cachedata(self):
data = [{}, {}]
return data
codeparsercache = CodeParserCache()
def parser_cachefile(d):
cachedir = (d.getVar("PERSISTENT_DIR", True) or
d.getVar("CACHE", True))
if cachedir in [None, '']:
return None
bb.utils.mkdirhier(cachedir)
cachefile = os.path.join(cachedir, "bb_codeparser.dat")
logger.debug(1, "Using cache in '%s' for codeparser cache", cachefile)
return cachefile
def parser_cache_init(d):
codeparsercache.init_cache(d)
global pythonparsecache
global shellparsecache
cachefile = parser_cachefile(d)
if not cachefile:
return
try:
p = pickle.Unpickler(file(cachefile, "rb"))
data, version = p.load()
except:
return
if version != PARSERCACHE_VERSION:
return
pythonparsecache = data[0]
shellparsecache = data[1]
def parser_cache_save(d):
codeparsercache.save_extras(d)
cachefile = parser_cachefile(d)
if not cachefile:
return
glf = bb.utils.lockfile(cachefile + ".lock", shared=True)
i = os.getpid()
lf = None
while not lf:
shellcache = {}
pythoncache = {}
lf = bb.utils.lockfile(cachefile + ".lock." + str(i), retry=False)
if not lf or os.path.exists(cachefile + "-" + str(i)):
if lf:
bb.utils.unlockfile(lf)
lf = None
i = i + 1
continue
shellcache = shellparsecacheextras
pythoncache = pythonparsecacheextras
p = pickle.Pickler(file(cachefile + "-" + str(i), "wb"), -1)
p.dump([[pythoncache, shellcache], PARSERCACHE_VERSION])
bb.utils.unlockfile(lf)
bb.utils.unlockfile(glf)
def internSet(items):
new = set()
for i in items:
new.add(intern(i))
return new
def parser_cache_savemerge(d):
codeparsercache.save_merge(d)
cachefile = parser_cachefile(d)
if not cachefile:
return
glf = bb.utils.lockfile(cachefile + ".lock")
try:
p = pickle.Unpickler(file(cachefile, "rb"))
data, version = p.load()
except (IOError, EOFError):
data, version = None, None
if version != PARSERCACHE_VERSION:
data = [{}, {}]
for f in [y for y in os.listdir(os.path.dirname(cachefile)) if y.startswith(os.path.basename(cachefile) + '-')]:
f = os.path.join(os.path.dirname(cachefile), f)
try:
p = pickle.Unpickler(file(f, "rb"))
extradata, version = p.load()
except (IOError, EOFError):
extradata, version = [{}, {}], None
if version != PARSERCACHE_VERSION:
continue
for h in extradata[0]:
if h not in data[0]:
data[0][h] = extradata[0][h]
for h in extradata[1]:
if h not in data[1]:
data[1][h] = extradata[1][h]
os.unlink(f)
# When the dicts are originally created, python calls intern() on the set keys
# which significantly improves memory usage. Sadly the pickle/unpickle process
# doesn't call intern() on the keys and results in the same strings being duplicated
# in memory. This also means pickle will save the same string multiple times in
# the cache file. By interning the data here, the cache file shrinks dramatically
# meaning faster load times and the reloaded cache files also consume much less
# memory. This is worth any performance hit from this loops and the use of the
# intern() data storage.
# Python 3.x may behave better in this area
for h in data[0]:
data[0][h]["refs"] = internSet(data[0][h]["refs"])
data[0][h]["execs"] = internSet(data[0][h]["execs"])
for h in data[1]:
data[1][h]["execs"] = internSet(data[1][h]["execs"])
p = pickle.Pickler(file(cachefile, "wb"), -1)
p.dump([data, PARSERCACHE_VERSION])
bb.utils.unlockfile(glf)
Logger = logging.getLoggerClass()
class BufferedLogger(Logger):
@@ -158,14 +235,14 @@ class PythonParser():
def parse_python(self, node):
h = hash(str(node))
if h in codeparsercache.pythoncache:
self.references = codeparsercache.pythoncache[h]["refs"]
self.execs = codeparsercache.pythoncache[h]["execs"]
if h in pythonparsecache:
self.references = pythonparsecache[h]["refs"]
self.execs = pythonparsecache[h]["execs"]
return
if h in codeparsercache.pythoncacheextras:
self.references = codeparsercache.pythoncacheextras[h]["refs"]
self.execs = codeparsercache.pythoncacheextras[h]["execs"]
if h in pythonparsecacheextras:
self.references = pythonparsecacheextras[h]["refs"]
self.execs = pythonparsecacheextras[h]["execs"]
return
@@ -179,9 +256,9 @@ class PythonParser():
self.references.update(self.var_references)
self.references.update(self.var_execs)
codeparsercache.pythoncacheextras[h] = {}
codeparsercache.pythoncacheextras[h]["refs"] = self.references
codeparsercache.pythoncacheextras[h]["execs"] = self.execs
pythonparsecacheextras[h] = {}
pythonparsecacheextras[h]["refs"] = self.references
pythonparsecacheextras[h]["execs"] = self.execs
class ShellParser():
def __init__(self, name, log):
@@ -199,12 +276,12 @@ class ShellParser():
h = hash(str(value))
if h in codeparsercache.shellcache:
self.execs = codeparsercache.shellcache[h]["execs"]
if h in shellparsecache:
self.execs = shellparsecache[h]["execs"]
return self.execs
if h in codeparsercache.shellcacheextras:
self.execs = codeparsercache.shellcacheextras[h]["execs"]
if h in shellparsecacheextras:
self.execs = shellparsecacheextras[h]["execs"]
return self.execs
try:
@@ -216,8 +293,8 @@ class ShellParser():
self.process_tokens(token)
self.execs = set(cmd for cmd in self.allexecs if cmd not in self.funcdefs)
codeparsercache.shellcacheextras[h] = {}
codeparsercache.shellcacheextras[h]["execs"] = self.execs
shellparsecacheextras[h] = {}
shellparsecacheextras[h]["execs"] = self.execs
return self.execs

View File

@@ -44,9 +44,6 @@ class CommandFailed(CommandExit):
self.error = message
CommandExit.__init__(self, 1)
class CommandError(Exception):
pass
class Command:
"""
A queue of asynchronous commands for bitbake
@@ -60,26 +57,21 @@ class Command:
self.currentAsyncCommand = None
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)
try:
result = command_method(self, commandline)
except CommandError as exc:
return None, exc.args[0]
except Exception:
import traceback
return None, traceback.format_exc()
else:
return result, None
if self.currentAsyncCommand is not None:
return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
if command not in CommandsAsync.__dict__:
return None, "No such command"
self.currentAsyncCommand = (command, commandline)
self.cooker.server_registration_cb(self.cooker.runCommands, self.cooker)
return True, None
try:
command = commandline.pop(0)
if command in CommandsSync.__dict__:
# Can run synchronous commands straight away
return getattr(CommandsSync, command)(self.cmds_sync, self, commandline)
if self.currentAsyncCommand is not None:
return "Busy (%s in progress)" % self.currentAsyncCommand[0]
if command not in CommandsAsync.__dict__:
return "No such command"
self.currentAsyncCommand = (command, commandline)
self.cooker.server_registration_cb(self.cooker.runCommands, self.cooker)
return True
except:
import traceback
return traceback.format_exc()
def runAsyncCommand(self):
try:
@@ -147,13 +139,7 @@ class CommandsSync:
"""
Get any command parsed from the commandline
"""
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']
return command.cooker.commandlineAction
def getVariable(self, command, params):
"""
@@ -171,7 +157,7 @@ class CommandsSync:
Set the value of variable in configuration.data
"""
varname = params[0]
value = str(params[1])
value = params[1]
command.cooker.configuration.data.setVar(varname, value)
def initCooker(self, command, params):
@@ -200,16 +186,6 @@ class CommandsSync:
filterfunc = params[0]
bb.parse.parse_py.ConfHandler.confFilters.append(filterfunc)
def matchFile(self, command, params):
fMatch = params[0]
return command.cooker.matchFile(fMatch)
def generateNewImage(self, command, params):
image = params[0]
base_image = params[1]
package_queue = params[2]
return command.cooker.generateNewImage(image, base_image, package_queue)
class CommandsAsync:
"""
A class of asynchronous commands

View File

@@ -1,11 +1,5 @@
"""Code pulled from future python versions, here for compatibility"""
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 = {
@@ -32,210 +26,3 @@ def total_ordering(cls):
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)

View File

@@ -158,7 +158,6 @@ class BBCooker:
#
self.configuration.event_data = bb.data.createCopy(self.configuration.data)
bb.data.update_data(self.configuration.event_data)
bb.parse.init_parser(self.configuration.event_data)
# TOSTOP must not be set or our children will hang when they output
fd = sys.stdout.fileno()
@@ -177,8 +176,6 @@ class BBCooker:
def initConfigurationData(self):
self.configuration.data = bb.data.init()
if self.configuration.show_environment:
self.configuration.data.enableTracking()
if not self.server_registration_cb:
self.configuration.data.setVar("BB_WORKERCONTEXT", "1")
@@ -187,7 +184,13 @@ class BBCooker:
bb.data.inheritFromOS(self.configuration.data, self.savedenv, filtered_keys)
def loadConfigurationData(self):
self.initConfigurationData()
self.configuration.data = bb.data.init()
if not self.server_registration_cb:
self.configuration.data.setVar("BB_WORKERCONTEXT", "1")
filtered_keys = bb.utils.approved_variables()
bb.data.inheritFromOS(self.configuration.data, self.savedenv, filtered_keys)
try:
self.parseConfigurationFiles(self.configuration.prefile,
@@ -215,12 +218,6 @@ class BBCooker:
nice = int(nice) - curnice
buildlog.verbose("Renice to %s " % os.nice(nice))
if self.status:
del self.status
self.status = bb.cache.CacheData(self.caches_array)
self.handleCollections( self.configuration.data.getVar("BBFILE_COLLECTIONS", True) )
def parseCommandLine(self):
# Parse any commandline into actions
self.commandlineAction = {'action':None, 'msg':None}
@@ -235,10 +232,8 @@ class BBCooker:
self.commandlineAction['msg'] = "No target should be used with the --environment and --buildfile options."
elif len(self.configuration.pkgs_to_build) > 0:
self.commandlineAction['action'] = ["showEnvironmentTarget", self.configuration.pkgs_to_build]
self.configuration.data.setVar("BB_CONSOLELOG", None)
else:
self.commandlineAction['action'] = ["showEnvironment", self.configuration.buildfile]
self.configuration.data.setVar("BB_CONSOLELOG", None)
elif self.configuration.buildfile is not None:
self.commandlineAction['action'] = ["buildFile", self.configuration.buildfile, self.configuration.cmd]
elif self.configuration.revisions_changed:
@@ -276,8 +271,8 @@ class BBCooker:
pkg_pn = self.status.pkg_pn
(latest_versions, preferred_versions) = bb.providers.findProviders(self.configuration.data, self.status, pkg_pn)
logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
logger.plain("%-35s %25s %25s", "Package Name", "Latest Version", "Preferred Version")
logger.plain("%-35s %25s %25s\n", "============", "==============", "=================")
for p in sorted(pkg_pn):
pref = preferred_versions[p]
@@ -302,6 +297,8 @@ class BBCooker:
# Parse the configuration here. We need to do it explicitly here since
# this showEnvironment() code path doesn't use the cache
self.parseConfiguration()
self.status = bb.cache.CacheData(self.caches_array)
self.handleCollections( self.configuration.data.getVar("BBFILE_COLLECTIONS", True) )
fn, cls = bb.cache.Cache.virtualfn2realfn(buildfile)
fn = self.matchFile(fn)
@@ -309,10 +306,6 @@ class BBCooker:
elif len(pkgs_to_build) == 1:
self.updateCache()
ignore = self.configuration.data.getVar("ASSUME_PROVIDED", True) or ""
if pkgs_to_build[0] in set(ignore.split()):
bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
localdata = data.createCopy(self.configuration.data)
bb.data.update_data(localdata)
bb.data.expandKeys(localdata)
@@ -334,11 +327,6 @@ class BBCooker:
parselog.exception("Unable to read %s", fn)
raise
# Display history
with closing(StringIO()) as env:
self.configuration.data.inchistory.emit(env)
logger.plain(env.getvalue())
# emit variables and shell functions
data.update_data(envdata)
with closing(StringIO()) as env:
@@ -546,15 +534,11 @@ class BBCooker:
# Prints a flattened form of package-depends below where subpackages of a package are merged into the main pn
depends_file = file('pn-depends.dot', 'w' )
buildlist_file = file('pn-buildlist', 'w' )
print("digraph depends {", file=depends_file)
for pn in depgraph["pn"]:
fn = depgraph["pn"][pn]["filename"]
version = depgraph["pn"][pn]["version"]
print('"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn), file=depends_file)
print("%s" % pn, file=buildlist_file)
buildlist_file.close()
logger.info("PN build list saved to 'pn-buildlist'")
for pn in depgraph["depends"]:
for depend in depgraph["depends"][pn]:
print('"%s" -> "%s"' % (pn, depend), file=depends_file)
@@ -649,8 +633,7 @@ class BBCooker:
# Calculate priorities for each file
matched = set()
for p in self.status.pkg_fn:
realfn, cls = bb.cache.Cache.virtualfn2realfn(p)
self.status.bbfile_priority[p] = self.calc_bbfile_priority(realfn, matched)
self.status.bbfile_priority[p] = self.calc_bbfile_priority(p, matched)
# Don't show the warning if the BBFILE_PATTERN did match .bbappend files
unmatched = set()
@@ -697,8 +680,8 @@ class BBCooker:
# Generate a list of parsed configuration files by searching the files
# listed in the __depends and __base_depends variables with a .conf suffix.
conffiles = []
dep_files = self.configuration.data.getVar('__base_depends') or []
dep_files = dep_files + (self.configuration.data.getVar('__depends') or [])
dep_files = self.configuration.data.getVar('__depends') or set()
dep_files.union(self.configuration.data.getVar('__base_depends') or set())
for f in dep_files:
if f[0].endswith(".conf"):
@@ -946,13 +929,13 @@ class BBCooker:
errors = True
continue
if lver <> depver:
parselog.error("Layer '%s' depends on version %d of layer '%s', but version %d is enabled in your configuration", c, depver, dep, lver)
parselog.error("Layer dependency %s of layer %s is at version %d, expected %d", dep, c, lver, depver)
errors = True
else:
parselog.error("Layer '%s' depends on version %d of layer '%s', which exists in your configuration but does not specify a version", c, depver, dep)
parselog.error("Layer dependency %s of layer %s has no version, expected %d", dep, c, depver)
errors = True
else:
parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
parselog.error("Layer dependency %s of layer %s not found", dep, c)
errors = True
collection_depends[c] = depnamelist
else:
@@ -1002,12 +985,12 @@ class BBCooker:
"""
Find the .bb files which match the expression in 'buildfile'.
"""
if bf.startswith("/") or bf.startswith("../"):
bf = os.path.abspath(bf)
filelist, masked = self.collect_bbfiles()
try:
os.stat(bf)
bf = os.path.abspath(bf)
return [bf]
except OSError:
regexp = re.compile(bf)
@@ -1047,6 +1030,8 @@ class BBCooker:
# Parse the configuration here. We need to do it explicitly here since
# buildFile() doesn't use the cache
self.parseConfiguration()
self.status = bb.cache.CacheData(self.caches_array)
self.handleCollections( self.configuration.data.getVar("BBFILE_COLLECTIONS", True) )
# If we are told to do the None task then query the default task
if (task == None):
@@ -1068,10 +1053,6 @@ class BBCooker:
info_array = infos[fn]
except KeyError:
bb.fatal("%s does not exist" % fn)
if info_array[0].skipped:
bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
self.status.add_from_recipeinfo(fn, info_array)
# Tweak some variables
@@ -1085,10 +1066,10 @@ class BBCooker:
self.status.rundeps[fn] = []
self.status.runrecs[fn] = []
# Invalidate task for target if force mode active
# Remove stamp for target if force mode active
if self.configuration.force:
logger.verbose("Invalidate task %s, %s", task, fn)
bb.parse.siggen.invalidate_task('do_%s' % task, self.status, fn)
logger.verbose("Remove stamp %s, %s", task, fn)
bb.build.del_stamp('do_%s' % task, self.status, fn)
# Setup taskdata structure
taskdata = bb.taskdata.TaskData(self.configuration.abort)
@@ -1189,42 +1170,29 @@ class BBCooker:
self.server_registration_cb(buildTargetsIdle, rq)
def generateNewImage(self, image, base_image, package_queue):
'''
Create a new image with a "require" base_image statement
'''
image_name = os.path.splitext(image)[0]
timestr = time.strftime("-%Y%m%d-%H%M%S")
dest = image_name + str(timestr) + ".bb"
with open(dest, "w") as imagefile:
imagefile.write("require " + base_image + "\n")
package_install = "PACKAGE_INSTALL_forcevariable = \""
for package in package_queue:
package_install += str(package) + " "
package_install += "\"\n"
imagefile.write(package_install)
self.state = state.initial
return timestr
def updateCache(self):
if self.state == state.running:
return
if self.state in (state.shutdown, state.stop):
self.parser.shutdown(clean=False, force = True)
self.parser.shutdown(clean=False)
sys.exit(1)
if self.state != state.parsing:
self.parseConfiguration ()
if self.status:
del self.status
self.status = bb.cache.CacheData(self.caches_array)
ignore = self.configuration.data.getVar("ASSUME_PROVIDED", True) or ""
self.status.ignored_dependencies = set(ignore.split())
for dep in self.configuration.extra_assume_provided:
self.status.ignored_dependencies.add(dep)
self.handleCollections( self.configuration.data.getVar("BBFILE_COLLECTIONS", True) )
(filelist, masked) = self.collect_bbfiles()
self.configuration.data.renameVar("__depends", "__base_depends")
@@ -1233,8 +1201,6 @@ class BBCooker:
if not self.parser.parse_next():
collectlog.debug(1, "parsing complete")
if self.parser.error:
sys.exit(1)
self.show_appends_with_no_recipes()
self.buildDepgraph()
self.state = state.running
@@ -1604,7 +1570,6 @@ class CookerParser(object):
def init():
Parser.cfg = self.cfgdata
multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, args=(self.cfgdata,), exitpriority=1)
multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, args=(self.cfgdata,), exitpriority=1)
self.feeder_quit = multiprocessing.Queue(maxsize=1)
self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
@@ -1631,7 +1596,6 @@ class CookerParser(object):
self.skipped, self.masked,
self.virtuals, self.error,
self.total)
bb.event.fire(event, self.cfgdata)
self.feeder_quit.put(None)
for process in self.processes:
@@ -1644,20 +1608,16 @@ class CookerParser(object):
self.parser_quit.put(None)
self.jobs.cancel_join_thread()
sys.exit(1)
for process in self.processes:
if force:
process.join(.1)
process.terminate()
else:
process.join()
process.join()
self.feeder.join()
sync = threading.Thread(target=self.bb_cache.sync)
sync.start()
multiprocessing.util.Finalize(None, sync.join, exitpriority=-100)
bb.codeparser.parser_cache_savemerge(self.cooker.configuration.data)
bb.fetch.fetcher_parse_done(self.cooker.configuration.data)
def load_cached(self):
for filename, appends in self.fromcache:
@@ -1681,52 +1641,26 @@ class CookerParser(object):
yield result
def parse_next(self):
result = []
parsed = None
try:
parsed, result = self.results.next()
except StopIteration:
self.shutdown()
return False
except bb.BBHandledException as exc:
self.error += 1
logger.error('Failed to parse recipe: %s' % exc.recipe)
self.shutdown(clean=False)
return False
except ParsingFailure as exc:
self.error += 1
logger.error('Unable to parse %s: %s' %
(exc.recipe, bb.exceptions.to_string(exc.realexception)))
self.shutdown(clean=False)
return False
except bb.parse.ParseError as exc:
self.error += 1
except (bb.parse.ParseError, bb.data_smart.ExpansionError) as exc:
logger.error(str(exc))
self.shutdown(clean=False)
return False
except bb.data_smart.ExpansionError as exc:
self.error += 1
_, value, _ = sys.exc_info()
logger.error('ExpansionError during parsing %s: %s', value.recipe, str(exc))
self.shutdown(clean=False)
return False
except SyntaxError as exc:
self.error += 1
logger.error('Unable to parse %s', exc.recipe)
self.shutdown(clean=False)
return False
except Exception as exc:
self.error += 1
etype, value, tb = sys.exc_info()
if hasattr(value, "recipe"):
logger.error('Unable to parse %s', value.recipe,
exc_info=(etype, value, exc.traceback))
else:
# Most likely, an exception occurred during raising an exception
import traceback
logger.error('Exception during parse: %s' % traceback.format_exc())
logger.error('Unable to parse %s', value.recipe,
exc_info=(etype, value, exc.traceback))
self.shutdown(clean=False)
return False
self.current += 1
self.virtuals += len(result)

View File

@@ -158,7 +158,7 @@ def expandKeys(alterdata, readdata = None):
for key in todolist:
ekey = todolist[key]
alterdata.renameVar(key, ekey)
renameVar(key, ekey, alterdata)
def inheritFromOS(d, savedenv, permitted):
"""Inherit variables from the initial environment."""
@@ -166,9 +166,9 @@ def inheritFromOS(d, savedenv, permitted):
for s in savedenv.keys():
if s in permitted:
try:
d.setVar(s, getVar(s, savedenv, True), op = 'from env')
setVar(s, getVar(s, savedenv, True), d)
if s in exportlist:
d.setVarFlag(s, "export", True, op = 'auto env export')
setVarFlag(s, "export", True, d)
except TypeError:
pass
@@ -194,7 +194,8 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
return 0
if all:
d.varhistory.emit(var, oval, val, o)
commentVal = re.sub('\n', '\n#', str(oval))
o.write('# %s=%s\n' % (var, commentVal))
if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all:
return 0
@@ -259,7 +260,6 @@ def emit_func(func, o=sys.__stdout__, d = init()):
emit_var(func, o, d, False) and o.write('\n')
newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func, True))
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
seen = set()
while newdeps:
deps = newdeps
@@ -273,26 +273,19 @@ def emit_func(func, o=sys.__stdout__, d = init()):
def update_data(d):
"""Performs final steps upon the datastore, including application of overrides"""
d.finalize(parent = True)
d.finalize()
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)
else:
value = d.getVar(key, False)
value = d.getVar(key, False)
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:
logger.warn("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE", True)))
parser.parse_python(parsedvar.value)
deps = deps | parser.references
else:
@@ -308,23 +301,11 @@ def build_dependencies(key, keys, shelldeps, vardepvals, d):
parser = d.expandWithRefs(value, key)
deps |= parser.references
deps = deps | (keys & parser.execs)
# Add varflags, assuming an exclusion list is set
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
if varflagsexcl:
varfdeps = []
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((d.getVarFlag(key, "vardepsexclude", True) or "").split())
except Exception as e:
raise bb.data_smart.ExpansionError(key, None, e)
except:
bb.note("Error expanding variable %s" % key)
raise
return deps, value
#bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
#d.setVarFlag(key, "vardeps", deps)
@@ -357,8 +338,6 @@ def generate_dependencies(d):
def inherits_class(klass, d):
val = getVar('__inherit_cache', d) or []
needle = os.path.join('classes', '%s.bbclass' % klass)
for v in val:
if v.endswith(needle):
return True
if os.path.join('classes', '%s.bbclass' % klass) in val:
return True
return False

View File

@@ -28,7 +28,7 @@ BitBake build tools.
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
import copy, re, sys, traceback
import copy, re
from collections import MutableMapping
import logging
import hashlib
@@ -39,46 +39,10 @@ from bb.COW import COWDictBase
logger = logging.getLogger("BitBake.Data")
__setvar_keyword__ = ["_append", "_prepend"]
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?$')
__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):
"""Save the caller the trouble of specifying everything."""
# Save effort.
if 'ignore' in loginfo and loginfo['ignore']:
return
# If nothing was provided, mark this as possibly unneeded.
if not loginfo:
loginfo['ignore'] = True
return
# Infer caller's likely values for variable (var) and value (value),
# to reduce clutter in the rest of the code.
if varval and ('variable' not in loginfo or 'detail' not in loginfo):
try:
raise Exception
except Exception:
tb = sys.exc_info()[2]
if parent:
above = tb.tb_frame.f_back.f_back
else:
above = tb.tb_frame.f_back
lcls = above.f_locals.items()
for k, v in lcls:
if k == 'value' and 'detail' not in loginfo:
loginfo['detail'] = v
if k == 'var' and 'variable' not in loginfo:
loginfo['variable'] = v
# Infer file/line/function from traceback
if 'file' not in loginfo:
depth = 3
if parent:
depth = 4
file, line, func, text = traceback.extract_stack(limit = depth)[0]
loginfo['file'] = file
loginfo['line'] = line
if func not in loginfo:
loginfo['func'] = func
class VariableParse:
def __init__(self, varname, d, val = None):
@@ -138,148 +102,22 @@ class ExpansionError(Exception):
self.expression = expression
self.variablename = varname
self.exception = exception
if varname:
if expression:
self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception)
else:
self.msg = "Failure expanding variable %s: %s: %s" % (varname, type(exception).__name__, exception)
else:
self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception)
self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception)
Exception.__init__(self, self.msg)
self.args = (varname, expression, exception)
def __str__(self):
return self.msg
class IncludeHistory(object):
def __init__(self, parent = None, filename = '[TOP LEVEL]'):
self.parent = parent
self.filename = filename
self.children = []
self.current = self
def copy(self):
new = IncludeHistory(self.parent, self.filename)
for c in self.children:
new.children.append(c)
return new
def include(self, filename):
newfile = IncludeHistory(self.current, filename)
self.current.children.append(newfile)
self.current = newfile
return self
def __enter__(self):
pass
def __exit__(self, a, b, c):
if self.current.parent:
self.current = self.current.parent
else:
bb.warn("Include log: Tried to finish '%s' at top level." % filename)
return False
def emit(self, o, level = 0):
"""Emit an include history file, and its children."""
if level:
spaces = " " * (level - 1)
o.write("# %s%s" % (spaces, self.filename))
if len(self.children) > 0:
o.write(" includes:")
else:
o.write("#\n# INCLUDE HISTORY:\n#")
level = level + 1
for child in self.children:
o.write("\n")
child.emit(o, level)
class VariableHistory(object):
def __init__(self, dataroot):
self.dataroot = dataroot
self.variables = COWDictBase.copy()
def copy(self):
new = VariableHistory(self.dataroot)
new.variables = self.variables.copy()
return new
def record(self, *kwonly, **loginfo):
if not self.dataroot._tracking:
return
if len(kwonly) > 0:
raise TypeError
infer_caller_details(loginfo, parent = True)
if 'ignore' in loginfo and loginfo['ignore']:
return
if 'op' not in loginfo or not loginfo['op']:
loginfo['op'] = 'set'
if 'detail' in loginfo:
loginfo['detail'] = str(loginfo['detail'])
if 'variable' not in loginfo or 'file' not in loginfo:
raise ValueError("record() missing variable or file.")
var = loginfo['variable']
if var not in self.variables:
self.variables[var] = []
self.variables[var].append(loginfo.copy())
def variable(self, var):
if var in self.variables:
return self.variables[var]
else:
return []
def emit(self, var, oval, val, o):
history = self.variable(var)
commentVal = re.sub('\n', '\n#', str(oval))
if history:
if len(history) == 1:
o.write("#\n# $%s\n" % var)
else:
o.write("#\n# $%s [%d operations]\n" % (var, len(history)))
for event in history:
# o.write("# %s\n" % str(event))
if 'func' in event:
# If we have a function listed, this is internal
# code, not an operation in a config file, and the
# full path is distracting.
event['file'] = re.sub('.*/', '', event['file'])
display_func = ' [%s]' % event['func']
else:
display_func = ''
if 'flag' in event:
flag = '[%s] ' % (event['flag'])
else:
flag = ''
o.write("# %s %s:%s%s\n# %s\"%s\"\n" % (event['op'], event['file'], event['line'], display_func, flag, re.sub('\n', '\n# ', event['detail'])))
if len(history) > 1:
o.write("# computed:\n")
o.write('# "%s"\n' % (commentVal))
else:
o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
o.write('# "%s"\n' % (commentVal))
class DataSmart(MutableMapping):
def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ):
self.dict = {}
self.inchistory = IncludeHistory()
self.varhistory = VariableHistory(self)
self._tracking = False
# cookie monster tribute
self._special_values = special
self._seen_overrides = seen
self.expand_cache = {}
def enableTracking(self):
self._tracking = True
def disableTracking(self):
self._tracking = False
def expandWithRefs(self, s, varname):
if not isinstance(s, basestring): # sanity check
@@ -299,8 +137,6 @@ class DataSmart(MutableMapping):
break
except ExpansionError:
raise
except bb.parse.SkipPackage:
raise
except Exception as exc:
raise ExpansionError(varname, s, exc)
@@ -315,14 +151,10 @@ class DataSmart(MutableMapping):
return self.expandWithRefs(s, varname).value
def finalize(self, parent = False):
def finalize(self):
"""Performs final steps upon the datastore, including application of overrides"""
overrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
finalize_caller = {
'op': 'finalize',
}
infer_caller_details(finalize_caller, parent = parent, varval = False)
#
# Well let us see what breaks here. We used to iterate
@@ -339,9 +171,6 @@ class DataSmart(MutableMapping):
# Then we will handle _append and _prepend
#
# We only want to report finalization once per variable overridden.
finalizes_reported = {}
for o in overrides:
# calculate '_'+override
l = len(o) + 1
@@ -354,19 +183,7 @@ class DataSmart(MutableMapping):
for var in vars:
name = var[:-l]
try:
# Report only once, even if multiple changes.
if name not in finalizes_reported:
finalizes_reported[name] = True
finalize_caller['variable'] = name
finalize_caller['detail'] = 'was: ' + str(self.getVar(name, False))
self.varhistory.record(**finalize_caller)
# Copy history of the override over.
for event in self.varhistory.variable(var):
loginfo = event.copy()
loginfo['variable'] = name
loginfo['op'] = 'override[%s]:%s' % (o, loginfo['op'])
self.varhistory.record(**loginfo)
self.setVar(name, self.getVar(var, False), op = 'finalize', file = 'override[%s]' % o, line = '')
self.setVar(name, self.getVar(var, False))
self.delVar(var)
except Exception:
logger.info("Untracked delVar")
@@ -378,12 +195,7 @@ class DataSmart(MutableMapping):
for append in appends:
keep = []
for (a, o) in self.getVarFlag(append, op) or []:
match = True
if o:
for o2 in o.split("_"):
if not o2 in overrides:
match = False
if not match:
if o and not o in overrides:
keep.append((a ,o))
continue
@@ -397,9 +209,9 @@ class DataSmart(MutableMapping):
# We save overrides that may be applied at some later stage
if keep:
self.setVarFlag(append, op, keep, ignore=True)
self.setVarFlag(append, op, keep)
else:
self.delVarFlag(append, op, ignore=True)
self.delVarFlag(append, op)
def initVar(self, var):
self.expand_cache = {}
@@ -427,10 +239,7 @@ class DataSmart(MutableMapping):
else:
self.initVar(var)
def setVar(self, var, value, **loginfo):
if 'op' not in loginfo:
loginfo['op'] = "set"
def setVar(self, var, value):
self.expand_cache = {}
match = __setvar_regexp__.match(var)
if match and match.group("keyword") in __setvar_keyword__:
@@ -439,22 +248,15 @@ class DataSmart(MutableMapping):
override = match.group('add')
l = self.getVarFlag(base, keyword) or []
l.append([value, override])
self.setVarFlag(base, keyword, l, ignore=True)
# And cause that to be recorded:
loginfo['detail'] = value
loginfo['variable'] = base
if override:
loginfo['op'] = '%s[%s]' % (keyword, override)
else:
loginfo['op'] = keyword
self.varhistory.record(**loginfo)
self.setVarFlag(base, keyword, l)
# todo make sure keyword is not __doc__ or __module__
# pay the cookie monster
try:
self._special_values[keyword].add(base)
self._special_values[keyword].add( base )
except KeyError:
self._special_values[keyword] = set()
self._special_values[keyword].add(base)
self._special_values[keyword].add( base )
return
@@ -470,28 +272,23 @@ class DataSmart(MutableMapping):
self._seen_overrides[override].add( var )
# setting var
self.dict[var]["_content"] = value
self.varhistory.record(**loginfo)
self.dict[var]["content"] = value
def getVar(self, var, expand=False, noweakdefault=False):
value = self.getVarFlag(var, "_content", False, 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):
def renameVar(self, key, newkey):
"""
Rename the variable key to newkey
"""
val = self.getVar(key, 0)
if val is not None:
loginfo['variable'] = newkey
loginfo['op'] = 'rename from %s' % key
loginfo['detail'] = val
self.varhistory.record(**loginfo)
self.setVar(newkey, val, ignore=True)
self.setVar(newkey, val)
for i in ('_append', '_prepend'):
src = self.getVarFlag(key, i)
@@ -500,34 +297,23 @@ class DataSmart(MutableMapping):
dest = self.getVarFlag(newkey, i) or []
dest.extend(src)
self.setVarFlag(newkey, i, dest, ignore=True)
self.setVarFlag(newkey, i, dest)
if i in self._special_values and key in self._special_values[i]:
self._special_values[i].remove(key)
self._special_values[i].add(newkey)
loginfo['variable'] = key
loginfo['op'] = 'rename (to)'
loginfo['detail'] = newkey
self.varhistory.record(**loginfo)
self.delVar(key, ignore=True)
self.delVar(key)
def appendVar(self, var, value, **loginfo):
loginfo['op'] = 'append'
self.varhistory.record(**loginfo)
newvalue = (self.getVar(var, False) or "") + value
self.setVar(var, newvalue, ignore=True)
def appendVar(self, key, value):
value = (self.getVar(key, False) or "") + value
self.setVar(key, value)
def prependVar(self, var, value, **loginfo):
loginfo['op'] = 'prepend'
self.varhistory.record(**loginfo)
newvalue = value + (self.getVar(var, False) or "")
self.setVar(var, newvalue, ignore=True)
def prependVar(self, key, value):
value = value + (self.getVar(key, False) or "")
self.setVar(key, value)
def delVar(self, var, **loginfo):
loginfo['detail'] = ""
loginfo['op'] = 'del'
self.varhistory.record(**loginfo)
def delVar(self, var):
self.expand_cache = {}
self.dict[var] = {}
if '_' in var:
@@ -535,14 +321,10 @@ class DataSmart(MutableMapping):
if override and override in self._seen_overrides and var in self._seen_overrides[override]:
self._seen_overrides[override].remove(var)
def setVarFlag(self, var, flag, value, **loginfo):
if 'op' not in loginfo:
loginfo['op'] = "set"
loginfo['flag'] = flag
self.varhistory.record(**loginfo)
def setVarFlag(self, var, flag, flagvalue):
if not var in self.dict:
self._makeShadowCopy(var)
self.dict[var][flag] = value
self.dict[var][flag] = flagvalue
def getVarFlag(self, var, flag, expand=False, noweakdefault=False):
local_var = self._findVar(var)
@@ -550,13 +332,13 @@ class DataSmart(MutableMapping):
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:
elif flag == "content" and "defaultval" in local_var and not noweakdefault:
value = copy.copy(local_var["defaultval"])
if expand and value:
value = self.expand(value, None)
return value
def delVarFlag(self, var, flag, **loginfo):
def delVarFlag(self, var, flag):
local_var = self._findVar(var)
if not local_var:
return
@@ -564,38 +346,23 @@ class DataSmart(MutableMapping):
self._makeShadowCopy(var)
if var in self.dict and flag in self.dict[var]:
loginfo['detail'] = ""
loginfo['op'] = 'delFlag'
loginfo['flag'] = flag
self.varhistory.record(**loginfo)
del self.dict[var][flag]
def appendVarFlag(self, var, flag, value, **loginfo):
loginfo['op'] = 'append'
loginfo['flag'] = flag
self.varhistory.record(**loginfo)
newvalue = (self.getVarFlag(var, flag, False) or "") + value
self.setVarFlag(var, flag, newvalue, ignore=True)
def appendVarFlag(self, key, flag, value):
value = (self.getVarFlag(key, flag, False) or "") + value
self.setVarFlag(key, flag, value)
def prependVarFlag(self, var, flag, value, **loginfo):
loginfo['op'] = 'prepend'
loginfo['flag'] = flag
self.varhistory.record(**loginfo)
newvalue = value + (self.getVarFlag(var, flag, False) or "")
self.setVarFlag(var, flag, newvalue, ignore=True)
def prependVarFlag(self, key, flag, value):
value = value + (self.getVarFlag(key, flag, False) or "")
self.setVarFlag(key, flag, value)
def setVarFlags(self, var, flags, **loginfo):
infer_caller_details(loginfo)
def setVarFlags(self, var, flags):
if not var in self.dict:
self._makeShadowCopy(var)
for i in flags:
if i == "_content":
if i == "content":
continue
loginfo['flag'] = i
loginfo['detail'] = flags[i]
self.varhistory.record(**loginfo)
self.dict[var][i] = flags[i]
def getVarFlags(self, var):
@@ -604,7 +371,7 @@ class DataSmart(MutableMapping):
if local_var:
for i in local_var:
if i.startswith("_"):
if i == "content":
continue
flags[i] = local_var[i]
@@ -613,21 +380,18 @@ class DataSmart(MutableMapping):
return flags
def delVarFlags(self, var, **loginfo):
def delVarFlags(self, var):
if not var in self.dict:
self._makeShadowCopy(var)
if var in self.dict:
content = None
loginfo['op'] = 'delete flags'
self.varhistory.record(**loginfo)
# try to save the content
if "_content" in self.dict[var]:
content = self.dict[var]["_content"]
if "content" in self.dict[var]:
content = self.dict[var]["content"]
self.dict[var] = {}
self.dict[var]["_content"] = content
self.dict[var]["content"] = content
else:
del self.dict[var]
@@ -639,11 +403,6 @@ class DataSmart(MutableMapping):
# we really want this to be a DataSmart...
data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy())
data.dict["_data"] = self.dict
data.varhistory = self.varhistory.copy()
data.varhistory.datasmart = data
data.inchistory = self.inchistory.copy()
data._tracking = self._tracking
return data
@@ -704,16 +463,12 @@ class DataSmart(MutableMapping):
def get_hash(self):
data = {}
d = self.createCopy()
bb.data.expandKeys(d)
bb.data.update_data(d)
config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST", True) or "").split())
keys = set(key for key in iter(d) if not key.startswith("__"))
config_whitelist = set((self.getVar("BB_HASHCONFIG_WHITELIST", True) or "").split())
keys = set(key for key in iter(self) if not key.startswith("__"))
for key in keys:
if key in config_whitelist:
continue
value = d.getVar(key, False) or ""
value = self.getVar(key, False) or ""
data.update({key:value})
data_str = str([(k, data[k]) for k in sorted(data.keys())])

View File

@@ -32,7 +32,6 @@ import logging
import atexit
import traceback
import bb.utils
import bb.compat
# This is the pid for which we should generate the event. This is set when
# the runqueue forks off.
@@ -54,7 +53,7 @@ Registered = 10
AlreadyRegistered = 14
# Internal
_handlers = bb.compat.OrderedDict()
_handlers = {}
_ui_handlers = {}
_ui_handler_seq = 0
@@ -105,18 +104,6 @@ def print_ui_queue():
console = logging.StreamHandler(sys.stdout)
console.setFormatter(BBLogFormatter("%(levelname)s: %(message)s"))
logger.handlers = [console]
# First check to see if we have any proper messages
msgprint = False
for event in ui_queue:
if isinstance(event, logging.LogRecord):
if event.levelno > logging.DEBUG:
logger.handle(event)
msgprint = True
if msgprint:
return
# Nope, so just print all of the messages we have (including debug messages)
for event in ui_queue:
if isinstance(event, logging.LogRecord):
logger.handle(event)
@@ -188,7 +175,7 @@ def register(name, handler):
_handlers[name] = noop
return
env = {}
bb.utils.better_exec(code, env)
bb.utils.simple_exec(code, env)
func = bb.utils.better_eval(name, env)
_handlers[name] = func
else:
@@ -325,14 +312,6 @@ class BuildCompleted(BuildBase, OperationCompleted):
OperationCompleted.__init__(self, total, "Building Failed")
BuildBase.__init__(self, n, p, failures)
class DiskFull(Event):
"""Disk full case build aborted"""
def __init__(self, dev, type, freespace, mountpoint):
Event.__init__(self)
self._dev = dev
self._type = type
self._free = freespace
self._mountpoint = mountpoint
class NoProvider(Event):
"""No Provider for an Event"""
@@ -510,15 +489,6 @@ class MsgFatal(MsgBase):
class MsgPlain(MsgBase):
"""General output"""
class LogExecTTY(Event):
"""Send event containing program to spawn on tty of the logger"""
def __init__(self, msg, prog, sleep_delay, retries):
Event.__init__(self)
self.msg = msg
self.prog = prog
self.sleep_delay = sleep_delay
self.retries = retries
class LogHandler(logging.Handler):
"""Dispatch logging messages as bitbake events"""
@@ -557,28 +527,3 @@ class SanityCheckPassed(Event):
"""
Event to indicate sanity check is passed
"""
class SanityCheckFailed(Event):
"""
Event to indicate sanity check has failed
"""
def __init__(self, msg, network_error=False):
Event.__init__(self)
self._msg = msg
self._network_error = network_error
class NetworkTest(Event):
"""
Event to start network test
"""
class NetworkTestPassed(Event):
"""
Event to indicate network test has passed
"""
class NetworkTestFailed(Event):
"""
Event to indicate network test has failed
"""

View File

@@ -32,14 +32,7 @@ class TracebackEntry(namedtuple.abc):
def _get_frame_args(frame):
"""Get the formatted arguments and class (if available) for a frame"""
arginfo = inspect.getargvalues(frame)
try:
if not arginfo.args:
return '', None
# There have been reports from the field of python 2.6 which doesn't
# return a namedtuple here but simply a tuple so fallback gracefully if
# args isn't present.
except AttributeError:
if not arginfo.args:
return '', None
firstarg = arginfo.args[0]

View File

@@ -8,7 +8,6 @@ BitBake build tools.
"""
# Copyright (C) 2003, 2004 Chris Larson
# 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
@@ -29,14 +28,10 @@ from __future__ import absolute_import
from __future__ import print_function
import os, re
import logging
import urllib
import operator
import bb.persist_data, bb.utils
import bb.checksum
from bb import data
__version__ = "2"
_checksum_cache = bb.checksum.FileChecksumCache()
logger = logging.getLogger("BitBake.Fetcher")
@@ -55,7 +50,7 @@ class MalformedUrl(BBFetchException):
msg = "The URL: '%s' is invalid and cannot be interpreted" % url
self.url = url
BBFetchException.__init__(self, msg)
self.args = (url,)
self.args = url
class FetchError(BBFetchException):
"""General fetcher exception when something happens incorrectly"""
@@ -68,12 +63,6 @@ class FetchError(BBFetchException):
BBFetchException.__init__(self, msg)
self.args = (message, url)
class ChecksumError(FetchError):
"""Exception when mismatched checksum encountered"""
class NoChecksumError(FetchError):
"""Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set"""
class UnpackError(BBFetchException):
"""General fetcher exception when something happens incorrectly when unpacking"""
def __init__(self, message, url):
@@ -88,7 +77,7 @@ class NoMethodError(BBFetchException):
msg = "Could not find a fetcher which supports the URL: '%s'" % url
self.url = url
BBFetchException.__init__(self, msg)
self.args = (url,)
self.args = url
class MissingParameterError(BBFetchException):
"""Exception raised when a fetch method is missing a critical parameter in the url"""
@@ -110,15 +99,12 @@ 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 but access requested with command %s (for url %s)" % (cmd, url)
msg = "Network access disabled through BB_NO_NETWORK but access rquested with command %s (for url %s)" % (cmd, url)
self.url = url
self.cmd = cmd
BBFetchException.__init__(self, msg)
self.args = (url, cmd)
class NonLocalMethod(Exception):
def __init__(self):
Exception.__init__(self)
def decodeurl(url):
"""Decodes an URL into the tokens (scheme, network location, path,
@@ -158,14 +144,14 @@ def decodeurl(url):
s1, s2 = s.split('=')
p[s1] = s2
return type, host, urllib.unquote(path), user, pswd, p
return (type, host, path, user, pswd, p)
def encodeurl(decoded):
"""Encodes a URL from tokens (scheme, network location, path,
user, password, parameters).
"""
type, host, path, user, pswd, p = decoded
(type, host, path, user, pswd, p) = decoded
if not path:
raise MissingParameterError('path', "encoded from the data %s" % str(decoded))
@@ -179,67 +165,43 @@ def encodeurl(decoded):
url += "@"
if host and type != "file":
url += "%s" % host
# Standardise path to ensure comparisons work
while '//' in path:
path = path.replace("//", "/")
url += "%s" % urllib.quote(path)
url += "%s" % path
if p:
for parm in p:
url += ";%s=%s" % (parm, p[parm])
return url
def uri_replace(ud, uri_find, uri_replace, replacements, d):
def uri_replace(ud, uri_find, uri_replace, d):
if not ud.url or not uri_find or not uri_replace:
logger.error("uri_replace: passed an undefined value, not replacing")
return None
logger.debug(1, "uri_replace: passed an undefined value, not replacing")
uri_decoded = list(decodeurl(ud.url))
uri_find_decoded = list(decodeurl(uri_find))
uri_replace_decoded = list(decodeurl(uri_replace))
logger.debug(2, "For url %s comparing %s to %s" % (uri_decoded, uri_find_decoded, uri_replace_decoded))
result_decoded = ['', '', '', '', '', {}]
for loc, i in enumerate(uri_find_decoded):
for i in uri_find_decoded:
loc = uri_find_decoded.index(i)
result_decoded[loc] = uri_decoded[loc]
regexp = i
if loc == 0 and regexp and not regexp.endswith("$"):
# Leaving the type unanchored can mean "https" matching "file" can become "files"
# which is clearly undesirable.
regexp += "$"
if loc == 5:
# Handle URL parameters
if i:
# Any specified URL parameters must match
for k in uri_replace_decoded[loc]:
if uri_decoded[loc][k] != uri_replace_decoded[loc][k]:
return None
# Overwrite any specified replacement parameters
for k in uri_replace_decoded[loc]:
result_decoded[loc][k] = uri_replace_decoded[loc][k]
elif (re.match(regexp, uri_decoded[loc])):
if not uri_replace_decoded[loc]:
result_decoded[loc] = ""
if isinstance(i, basestring):
if (re.match(i, uri_decoded[loc])):
if not uri_replace_decoded[loc]:
result_decoded[loc] = ""
else:
result_decoded[loc] = re.sub(i, uri_replace_decoded[loc], uri_decoded[loc])
if uri_find_decoded.index(i) == 2:
basename = None
if ud.mirrortarball:
basename = os.path.basename(ud.mirrortarball)
elif ud.localpath:
basename = os.path.basename(ud.localpath)
if basename and result_decoded[loc].endswith("/"):
result_decoded[loc] = os.path.dirname(result_decoded[loc])
if basename and not result_decoded[loc].endswith(basename):
result_decoded[loc] = os.path.join(result_decoded[loc], basename)
else:
for k in replacements:
uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k])
#bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc]))
result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc])
if loc == 2:
# Handle path manipulations
basename = None
if uri_decoded[0] != uri_replace_decoded[0] and ud.mirrortarball:
# If the source and destination url types differ, must be a mirrortarball mapping
basename = os.path.basename(ud.mirrortarball)
# Kill parameters, they make no sense for mirror tarballs
uri_decoded[5] = {}
elif ud.localpath and ud.method.supports_checksum(ud):
basename = os.path.basename(ud.localpath)
if basename and not result_decoded[loc].endswith(basename):
result_decoded[loc] = os.path.join(result_decoded[loc], basename)
else:
return None
return ud.url
result = encodeurl(result_decoded)
if result == ud.url:
return None
logger.debug(2, "For url %s returning %s" % (ud.url, result))
return result
@@ -267,18 +229,10 @@ def fetcher_init(d):
else:
raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy)
_checksum_cache.init_cache(d)
for m in methods:
if hasattr(m, "init"):
m.init(d)
def fetcher_parse_save(d):
_checksum_cache.save_extras(d)
def fetcher_parse_done(d):
_checksum_cache.save_merge(d)
def fetcher_compare_revisions(d):
"""
Compare the revisions in the persistant cache with current values and
@@ -305,37 +259,39 @@ def verify_checksum(u, ud, d):
"""
verify the MD5 and SHA256 checksum for downloaded src
Raises a FetchError if one or both of the SRC_URI checksums do not match
the downloaded file, or if BB_STRICT_CHECKSUM is set and there are no
checksums specified.
return value:
- True: a checksum matched
- False: neither checksum matched
if checksum is missing in recipes file, "BB_STRICT_CHECKSUM" decide the return value.
if BB_STRICT_CHECKSUM = "1" then return false as unmatched, otherwise return true as
matched
"""
if not ud.method.supports_checksum(ud):
if not ud.type in ["http", "https", "ftp", "ftps"]:
return
md5data = bb.utils.md5_file(ud.localpath)
sha256data = bb.utils.sha256_file(ud.localpath)
if ud.method.recommends_checksum(ud):
# If strict checking enabled and neither sum defined, raise error
strict = d.getVar("BB_STRICT_CHECKSUM", True) or None
if (strict and ud.md5_expected == None and ud.sha256_expected == None):
raise NoChecksumError('No checksum specified for %s, please add at least one to the recipe:\n'
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
(ud.localpath, ud.md5_name, md5data,
ud.sha256_name, sha256data), u)
# If strict checking enabled and neither sum defined, raise error
strict = d.getVar("BB_STRICT_CHECKSUM", True) or None
if (strict and ud.md5_expected == None and ud.sha256_expected == None):
raise FetchError('No checksum specified for %s, please add at least one to the recipe:\n'
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
(ud.localpath, ud.md5_name, md5data,
ud.sha256_name, sha256data), u)
# Log missing sums so user can more easily add them
if ud.md5_expected == None:
logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.md5_name, md5data)
# Log missing sums so user can more easily add them
if ud.md5_expected == None:
logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.md5_name, md5data)
if ud.sha256_expected == None:
logger.warn('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.sha256_name, sha256data)
if ud.sha256_expected == None:
logger.warn('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
'SRC_URI[%s] = "%s"',
ud.localpath, ud.sha256_name, sha256data)
md5mismatch = False
sha256mismatch = False
@@ -349,20 +305,14 @@ def verify_checksum(u, ud, d):
# We want to alert the user if a checksum is defined in the recipe but
# it does not match.
msg = ""
mismatch = False
if md5mismatch and ud.md5_expected:
msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'md5', md5data, ud.md5_expected)
mismatch = True;
if sha256mismatch and ud.sha256_expected:
msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'sha256', sha256data, ud.sha256_expected)
mismatch = True;
if mismatch:
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)
raise FetchError('Checksum mismatch!%s' % msg, u)
def update_stamp(u, ud, d):
@@ -423,18 +373,10 @@ def get_srcrev(d):
if not format:
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
autoinc = False
autoinc_templ = 'AUTOINC+'
for scm in scms:
ud = urldata[scm]
for name in ud.names:
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)
return format
@@ -479,16 +421,11 @@ def runfetchcmd(cmd, d, quiet = False, cleanup = []):
success = True
except bb.process.NotFoundError as e:
error_message = "Fetch command %s" % (e.command)
except bb.process.ExecutionError as e:
if e.stdout:
output = "output:\n%s\n%s" % (e.stdout, e.stderr)
elif e.stderr:
output = "output:\n%s" % e.stderr
else:
output = "no output"
error_message = "Fetch command failed with exit code %s, %s" % (e.exitcode, output)
except bb.process.CmdError as e:
error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
except bb.process.ExecutionError as e:
error_message = "Fetch command %s failed with exit code %s, output:\n%s" % (e.command, e.exitcode, e.stderr)
if not success:
for f in cleanup:
try:
@@ -509,107 +446,6 @@ def check_network_access(d, info = "", url = None):
else:
logger.debug(1, "Fetcher accessed the network with the command %s" % info)
def build_mirroruris(origud, mirrors, ld):
uris = []
uds = []
replacements = {}
replacements["TYPE"] = origud.type
replacements["HOST"] = origud.host
replacements["PATH"] = origud.path
replacements["BASENAME"] = origud.path.split("/")[-1]
replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
def adduri(uri, ud, uris, uds):
for line in mirrors:
try:
(find, replace) = line
except ValueError:
continue
newuri = uri_replace(ud, find, replace, replacements, ld)
if not newuri or newuri in uris or newuri == origud.url:
continue
try:
newud = FetchData(newuri, ld)
newud.setup_localpath(ld)
except bb.fetch2.BBFetchException as e:
logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
logger.debug(1, str(e))
try:
ud.method.clean(ud, ld)
except UnboundLocalError:
pass
continue
uris.append(newuri)
uds.append(newud)
adduri(newuri, newud, uris, uds)
adduri(None, origud, uris, uds)
return uris, uds
def try_mirror_url(newuri, origud, ud, ld, check = False):
# Return of None or a value means we're finished
# False means try another url
try:
if check:
found = ud.method.checkstatus(newuri, ud, ld)
if found:
return found
return False
os.chdir(ld.getVar("DL_DIR", True))
if not os.path.exists(ud.donestamp) or ud.method.need_update(newuri, ud, ld):
ud.method.download(newuri, ud, ld)
if hasattr(ud.method,"build_mirror_data"):
ud.method.build_mirror_data(newuri, ud, ld)
if not ud.localpath or not os.path.exists(ud.localpath):
return False
if ud.localpath == origud.localpath:
return ud.localpath
# We may be obtaining a mirror tarball which needs further processing by the real fetcher
# If that tarball is a local file:// we need to provide a symlink to it
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):
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)
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):
# Broken symbolic link
os.unlink(origud.localpath)
os.symlink(ud.localpath, origud.localpath)
update_stamp(newuri, origud, ld)
return ud.localpath
except bb.fetch2.NetworkAccess:
raise
except bb.fetch2.BBFetchException as e:
if isinstance(e, ChecksumError):
logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (newuri, origud.url))
logger.warn(str(e))
elif isinstance(e, NoChecksumError):
raise
else:
logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
logger.debug(1, str(e))
try:
ud.method.clean(ud, ld)
except UnboundLocalError:
pass
return False
def try_mirrors(d, origud, mirrors, check = False):
"""
Try to use a mirrored version of the sources.
@@ -620,13 +456,62 @@ def try_mirrors(d, origud, mirrors, check = False):
mirrors is the list of mirrors we're going to try
"""
ld = d.createCopy()
for line in mirrors:
try:
(find, replace) = line
except ValueError:
continue
newuri = uri_replace(origud, find, replace, ld)
if newuri == origud.url:
continue
try:
ud = FetchData(newuri, ld)
ud.setup_localpath(ld)
uris, uds = build_mirroruris(origud, mirrors, ld)
if check:
found = ud.method.checkstatus(newuri, ud, ld)
if found:
return found
continue
for index, uri in enumerate(uris):
ret = try_mirror_url(uri, origud, uds[index], ld, check)
if ret != False:
return ret
if not os.path.exists(ud.donestamp) or ud.method.need_update(newuri, ud, ld):
ud.method.download(newuri, ud, ld)
if os.path.exists(ud.localpath):
open(ud.donestamp, 'w').close()
if hasattr(ud.method,"build_mirror_data"):
ud.method.build_mirror_data(newuri, ud, ld)
if not ud.localpath or not os.path.exists(ud.localpath):
continue
if ud.localpath == origud.localpath:
return ud.localpath
# We may be obtaining a mirror tarball which needs further processing by the real fetcher
# If that tarball is a local file:// we need to provide a symlink to it
dldir = ld.getVar("DL_DIR", True)
if os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
dest = os.path.join(dldir, os.path.basename(ud.localpath))
if not os.path.exists(dest):
os.symlink(ud.localpath, dest)
return None
# Otherwise the result is a local file:// and we symlink to it
if not os.path.exists(origud.localpath):
os.symlink(ud.localpath, origud.localpath)
return ud.localpath
except bb.fetch2.NetworkAccess:
raise
except bb.fetch2.BBFetchException as e:
logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
logger.debug(1, str(e))
try:
if os.path.isfile(ud.localpath):
bb.utils.remove(ud.localpath)
except UnboundLocalError:
pass
continue
return None
def srcrev_internal_helper(ud, d, name):
@@ -660,85 +545,11 @@ def srcrev_internal_helper(ud, d, name):
return rev
def get_checksum_file_list(d):
""" Get a list of files checksum in SRC_URI
Returns the resolved local paths of all local file entries in
SRC_URI as a space-separated string
"""
fetch = Fetch([], d, cache = False, localonly = True)
dl_dir = d.getVar('DL_DIR', True)
filelist = []
for u in fetch.urls:
ud = fetch.ud[u]
if ud and isinstance(ud.method, local.Local):
ud.setup_localpath(d)
f = ud.localpath
if f.startswith(dl_dir):
# The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
if os.path.exists(f):
bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
else:
bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
continue
filelist.append(f)
return " ".join(filelist)
def get_file_checksums(filelist, pn):
"""Get a list of the checksums for a list of local files
Returns the checksums for a list of local files, caching the results as
it proceeds
"""
def checksum_file(f):
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
checksums = []
for pth in filelist.split():
checksum = None
if '*' in pth:
# Handle globs
import glob
for f in glob.glob(pth):
checksum = checksum_file(f)
if checksum:
checksums.append((f, checksum))
elif os.path.isdir(pth):
# Handle directories
for root, dirs, files in os.walk(pth):
for name in files:
fullpth = os.path.join(root, name)
checksum = checksum_file(fullpth)
if checksum:
checksums.append((fullpth, checksum))
else:
checksum = checksum_file(pth)
if checksum:
checksums.append((pth, checksum))
checksums.sort(key=operator.itemgetter(1))
return checksums
class FetchData(object):
"""
A class which represents the fetcher state for a given URI.
"""
def __init__(self, url, d, localonly = False):
def __init__(self, url, d):
# localpath is the location of a downloaded result. If not set, the file is local.
self.donestamp = None
self.localfile = ""
@@ -763,14 +574,10 @@ 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"]:
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"]:
self.sha256_expected = None
else:
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
@@ -785,13 +592,6 @@ class FetchData(object):
if not self.method:
raise NoMethodError(url)
if localonly and not isinstance(self.method, local.Local):
raise NonLocalMethod()
if self.parm.get("proto", None) and "protocol" not in self.parm:
logger.warn('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN', True))
self.parm["protocol"] = self.parm.get("proto", None)
if hasattr(self.method, "urldata_init"):
self.method.urldata_init(self, d)
@@ -856,26 +656,6 @@ class FetchMethod(object):
"""
return os.path.join(data.getVar("DL_DIR", d, True), urldata.localfile)
def supports_checksum(self, urldata):
"""
Is localpath something that can be represented by a checksum?
"""
# We cannot compute checksums for directories
if os.path.isdir(urldata.localpath) == True:
return False
if urldata.localpath.find("*") != -1:
return False
return True
def recommends_checksum(self, urldata):
"""
Is the backend on where checksumming is recommended (should warnings
by displayed if there is no checksum)?
"""
return False
def _strip_leading_slashes(self, relpath):
"""
Remove leading slash as os.path.join can't cope
@@ -926,7 +706,7 @@ class FetchMethod(object):
dots = file.split(".")
if dots[-1] in ['gz', 'bz2', 'Z']:
efile = os.path.join(rootdir, os.path.basename('.'.join(dots[0:-1])))
efile = os.path.join(data.getVar('WORKDIR', True),os.path.basename('.'.join(dots[0:-1])))
else:
efile = file
cmd = None
@@ -956,34 +736,29 @@ class FetchMethod(object):
if dos:
cmd = '%s -a' % cmd
cmd = "%s '%s'" % (cmd, file)
elif file.endswith('.rpm') or file.endswith('.srpm'):
elif file.endswith('.src.rpm') or file.endswith('.srpm'):
if 'extract' in urldata.parm:
unpack_file = urldata.parm.get('extract')
cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
cmd = 'rpm2cpio.sh %s | cpio -i %s' % (file, unpack_file)
iterate = True
iterate_file = unpack_file
else:
cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
elif file.endswith('.deb') or file.endswith('.ipk'):
cmd = 'ar -p %s data.tar.gz | zcat | tar --no-same-owner -xpf -' % file
cmd = 'rpm2cpio.sh %s | cpio -i' % (file)
if not unpack or not cmd:
# If file == dest, then avoid any copies, as we already put the file into dest!
dest = os.path.join(rootdir, os.path.basename(file))
if (file != dest) and not (os.path.exists(dest) and os.path.samefile(file, dest)):
if os.path.isdir(file):
# If for example we're asked to copy file://foo/bar, we need to unpack the result into foo/bar
basepath = getattr(urldata, "basepath", None)
filesdir = os.path.realpath(data.getVar("FILESDIR", True))
destdir = "."
if basepath and basepath.endswith("/"):
basepath = basepath.rstrip("/")
elif basepath:
basepath = os.path.dirname(basepath)
if basepath and basepath.find("/") != -1:
destdir = basepath[:basepath.rfind('/')]
if file[0:len(filesdir)] == filesdir:
destdir = file[len(filesdir):file.rfind('/')]
destdir = destdir.strip('/')
if destdir != "." and not os.access("%s/%s" % (rootdir, destdir), os.F_OK):
os.makedirs("%s/%s" % (rootdir, destdir))
if len(destdir) < 1:
destdir = "."
elif not os.access("%s/%s" % (rootdir, destdir), os.F_OK):
os.makedirs("%s/%s" % (rootdir, destdir))
cmd = 'cp -pPR %s %s/%s/' % (file, rootdir, destdir)
#cmd = 'tar -cf - -C "%d" -ps . | tar -xf - -C "%s/%s/"' % (file, rootdir, destdir)
else:
@@ -1007,9 +782,7 @@ class FetchMethod(object):
bb.utils.mkdirhier(newdir)
os.chdir(newdir)
path = data.getVar('PATH', True)
if path:
cmd = "PATH=\"%s\" %s" % (path, cmd)
cmd = "PATH=\"%s\" %s" % (data.getVar('PATH', True), cmd)
bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True)
@@ -1045,6 +818,23 @@ class FetchMethod(object):
logger.info("URL %s could not be checked for status since no method exists.", url)
return True
def localcount_internal_helper(ud, d, name):
"""
Return:
a) a locked localcount if specified
b) None otherwise
"""
localcount = None
if name != '':
pn = d.getVar("PN", True)
localcount = d.getVar("LOCALCOUNT_" + name, True)
if not localcount:
localcount = d.getVar("LOCALCOUNT", True)
return localcount
localcount_internal_helper = staticmethod(localcount_internal_helper)
def latest_revision(self, url, ud, d, name):
"""
Look in the cache for the latest revision, if not present ask the SCM.
@@ -1067,18 +857,43 @@ class FetchMethod(object):
if hasattr(self, "_sortable_revision"):
return self._sortable_revision(url, ud, d)
localcounts = bb.persist_data.persist('BB_URI_LOCALCOUNT', d)
key = self.generate_revision_key(url, ud, d, name)
latest_rev = self._build_revision(url, ud, d, name)
return 'AUTOINC+%s' % str(latest_rev)
last_rev = localcounts.get(key + '_rev')
uselocalcount = d.getVar("BB_LOCALCOUNT_OVERRIDE", True) or False
count = None
if uselocalcount:
count = FetchMethod.localcount_internal_helper(ud, d, name)
if count is None:
count = localcounts.get(key + '_count') or "0"
if last_rev == latest_rev:
return str(count + "+" + latest_rev)
buildindex_provided = hasattr(self, "_sortable_buildindex")
if buildindex_provided:
count = self._sortable_buildindex(url, ud, d, latest_rev)
if count is None:
count = "0"
elif uselocalcount or buildindex_provided:
count = str(count)
else:
count = str(int(count) + 1)
localcounts[key + '_rev'] = latest_rev
localcounts[key + '_count'] = count
return str(count + "+" + latest_rev)
def generate_revision_key(self, url, ud, d, name):
key = self._revision_key(url, ud, d, name)
return "%s-%s" % (key, d.getVar("PN", True) or "")
class Fetch(object):
def __init__(self, urls, d, cache = True, localonly = False):
if localonly and cache:
raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
def __init__(self, urls, d, cache = True):
if len(urls) == 0:
urls = d.getVar("SRC_URI", True).split()
self.urls = urls
@@ -1086,19 +901,14 @@ class Fetch(object):
self.ud = {}
fn = d.getVar('FILE', True)
if cache and fn and fn in urldata_cache:
if cache and fn in urldata_cache:
self.ud = urldata_cache[fn]
for url in urls:
if url not in self.ud:
try:
self.ud[url] = FetchData(url, d, localonly)
except NonLocalMethod:
if localonly:
self.ud[url] = None
pass
self.ud[url] = FetchData(url, d)
if fn and cache:
if cache:
urldata_cache[fn] = self.ud
def localpath(self, url):
@@ -1152,8 +962,6 @@ class Fetch(object):
if premirroronly:
self.d.setVar("BB_NO_NETWORK", "1")
os.chdir(self.d.getVar("DL_DIR", True))
firsterr = None
if not localpath and ((not os.path.exists(ud.donestamp)) or m.need_update(u, ud, self.d)):
try:
@@ -1170,17 +978,12 @@ class Fetch(object):
raise
except BBFetchException as e:
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))
elif isinstance(e, NoChecksumError):
raise
else:
logger.warn('Failed to fetch URL %s, attempting MIRRORS if available' % u)
logger.debug(1, str(e))
logger.warn('Failed to fetch URL %s' % u)
logger.debug(1, str(e))
firsterr = e
# Remove any incomplete fetch
m.clean(ud, self.d)
if os.path.isfile(ud.localpath):
bb.utils.remove(ud.localpath)
logger.debug(1, "Trying MIRRORS")
mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
localpath = try_mirrors (self.d, ud, mirrors)
@@ -1192,13 +995,6 @@ class Fetch(object):
update_stamp(u, ud, self.d)
except BBFetchException as e:
if isinstance(e, NoChecksumError):
logger.error("%s" % str(e))
elif isinstance(e, ChecksumError):
logger.error("Checksum failure fetching %s" % u)
raise
finally:
bb.utils.unlockfile(lf)
@@ -1225,7 +1021,7 @@ class Fetch(object):
except:
# Finally, try checking uri, u, from MIRRORS
mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
ret = try_mirrors(self.d, ud, mirrors, True)
ret = try_mirrors (self.d, ud, mirrors, True)
if not ret:
raise FetchError("URL %s doesn't work" % u, u)

View File

@@ -60,7 +60,7 @@ class Bzr(FetchMethod):
basecmd = data.expand('${FETCHCMD_bzr}', d)
proto = ud.parm.get('protocol', 'http')
proto = ud.parm.get('proto', 'http')
bzrroot = ud.host + ud.path
@@ -73,7 +73,7 @@ class Bzr(FetchMethod):
options.append("-r %s" % ud.revision)
if command == "fetch":
bzrcmd = "%s branch %s %s://%s" % (basecmd, " ".join(options), proto, bzrroot)
bzrcmd = "%s co %s %s://%s" % (basecmd, " ".join(options), proto, bzrroot)
elif command == "update":
bzrcmd = "%s pull %s --overwrite" % (basecmd, " ".join(options))
else:

View File

@@ -29,6 +29,7 @@ BitBake build tools.
import os
import logging
import bb
from bb import data
from bb.fetch2 import FetchMethod, FetchError, MissingParameterError, logger
from bb.fetch2 import runfetchcmd
@@ -63,7 +64,7 @@ class Cvs(FetchMethod):
if 'fullpath' in ud.parm:
fullpath = '_fullpath'
ud.localfile = bb.data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d)
ud.localfile = data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d)
def need_update(self, url, ud, d):
if (ud.date == "now"):
@@ -87,10 +88,10 @@ class Cvs(FetchMethod):
cvsroot = ud.path
else:
cvsroot = ":" + method
cvsproxyhost = d.getVar('CVS_PROXY_HOST', True)
cvsproxyhost = data.getVar('CVS_PROXY_HOST', d, True)
if cvsproxyhost:
cvsroot += ";proxy=" + cvsproxyhost
cvsproxyport = d.getVar('CVS_PROXY_PORT', True)
cvsproxyport = data.getVar('CVS_PROXY_PORT', d, True)
if cvsproxyport:
cvsroot += ";proxyport=" + cvsproxyport
cvsroot += ":" + ud.user
@@ -110,9 +111,15 @@ class Cvs(FetchMethod):
if ud.tag:
options.append("-r %s" % ud.tag)
cvsbasecmd = d.getVar("FETCHCMD_cvs", True)
cvscmd = cvsbasecmd + " '-d" + cvsroot + "' co " + " ".join(options) + " " + ud.module
cvsupdatecmd = cvsbasecmd + " '-d" + cvsroot + "' update -d -P " + " ".join(options)
localdata = data.createCopy(d)
data.setVar('OVERRIDES', "cvs:%s" % data.getVar('OVERRIDES', localdata), localdata)
data.update_data(localdata)
data.setVar('CVSROOT', cvsroot, localdata)
data.setVar('CVSCOOPTS', " ".join(options), localdata)
data.setVar('CVSMODULE', ud.module, localdata)
cvscmd = data.getVar('FETCHCOMMAND', localdata, True)
cvsupdatecmd = data.getVar('UPDATECOMMAND', localdata, True)
if cvs_rsh:
cvscmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvscmd)
@@ -120,8 +127,8 @@ class Cvs(FetchMethod):
# create module directory
logger.debug(2, "Fetch: checking for module directory")
pkg = d.getVar('PN', True)
pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
pkg = data.expand('${PN}', d)
pkgdir = os.path.join(data.expand('${CVSDIR}', localdata), pkg)
moddir = os.path.join(pkgdir, localdir)
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
logger.info("Update " + loc)
@@ -162,9 +169,12 @@ class Cvs(FetchMethod):
def clean(self, ud, d):
""" Clean CVS Files and tarballs """
pkg = d.getVar('PN', True)
pkgdir = os.path.join(d.getVar("CVSDIR", True), pkg)
pkg = data.expand('${PN}', d)
localdata = data.createCopy(d)
data.setVar('OVERRIDES', "cvs:%s" % data.getVar('OVERRIDES', localdata), localdata)
data.update_data(localdata)
pkgdir = os.path.join(data.expand('${CVSDIR}', localdata), pkg)
bb.utils.remove(pkgdir, True)
bb.utils.remove(ud.localpath)

View File

@@ -11,8 +11,8 @@ Supported SRC_URI options are:
- branch
The git branch to retrieve from. The default is "master"
This option also supports multiple branch fetching, with branches
separated by commas. In multiple branches case, the name option
this option also support multiple branches fetching, branches
are seperated by comma. in multiple branches case, the name option
must have the same number of names to match the branches, which is
used to specify the SRC_REV for the branch
e.g:
@@ -25,13 +25,13 @@ Supported SRC_URI options are:
- protocol
The method to use to access the repository. Common options are "git",
"http", "https", "file", "ssh" and "rsync". The default is "git".
"http", "file" and "rsync". The default is "git"
- rebaseable
rebaseable indicates that the upstream git repo may rebase in the future,
and current revision may disappear from upstream repo. This option will
remind fetcher to preserve local cache carefully for future use.
The default value is "0", set rebaseable=1 for rebaseable git repo.
reminder fetcher to preserve local cache carefully for future use.
The default value is "0", set rebaseable=1 for rebaseable git repo
- nocheckout
Don't checkout source code when unpacking. set this option for the recipe
@@ -71,17 +71,17 @@ from bb.fetch2 import logger
class Git(FetchMethod):
"""Class to fetch a module or modules from git repositories"""
def init(self, d):
pass
#
# Only enable _sortable revision if the key is set
#
if d.getVar("BB_GIT_CLONE_FOR_SRCREV", True):
self._sortable_buildindex = self._sortable_buildindex_disabled
def supports(self, url, ud, d):
"""
Check to see if a given url can be fetched with git.
"""
return ud.type in ['git']
def supports_checksum(self, urldata):
return False
def urldata_init(self, ud, d):
"""
init git specific variable within url data
@@ -123,11 +123,10 @@ class Git(FetchMethod):
for name in ud.names:
# Ensure anything that doesn't look like a sha256 checksum/revision is translated into one
if not ud.revisions[name] or len(ud.revisions[name]) != 40 or (False in [c in "abcdef0123456789" for c in ud.revisions[name]]):
if ud.revisions[name]:
ud.branches[name] = ud.revisions[name]
ud.branches[name] = ud.revisions[name]
ud.revisions[name] = self.latest_revision(ud.url, ud, d, name)
gitsrcname = '%s%s' % (ud.host.replace(':','.'), ud.path.replace('/', '.').replace('*', '.'))
gitsrcname = '%s%s' % (ud.host.replace(':','.'), ud.path.replace('/', '.'))
# for rebaseable git repo, it is necessary to keep mirror tar ball
# per revision, so that even the revision disappears from the
# upstream repo in the future, the mirror will remain intact and still
@@ -136,9 +135,8 @@ class Git(FetchMethod):
for name in ud.names:
gitsrcname = gitsrcname + '_' + ud.revisions[name]
ud.mirrortarball = 'git2_%s.tar.gz' % (gitsrcname)
ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
gitdir = d.getVar("GITDIR", True) or (d.getVar("DL_DIR", True) + "/git2/")
ud.clonedir = os.path.join(gitdir, gitsrcname)
ud.fullmirror = os.path.join(data.getVar("DL_DIR", d, True), ud.mirrortarball)
ud.clonedir = os.path.join(data.expand('${GITDIR}', d), gitsrcname)
ud.localfile = ud.clonedir
@@ -185,12 +183,8 @@ class Git(FetchMethod):
# If the repo still doesn't exist, fallback to cloning it
if not os.path.exists(ud.clonedir):
# We do this since git will use a "-l" option automatically for local urls where possible
if repourl.startswith("file://"):
repourl = repourl[7:]
clone_cmd = "%s clone --bare --mirror %s %s" % (ud.basecmd, repourl, ud.clonedir)
if ud.proto.lower() != 'file':
bb.fetch2.check_network_access(d, clone_cmd)
bb.fetch2.check_network_access(d, clone_cmd)
runfetchcmd(clone_cmd, d)
os.chdir(ud.clonedir)
@@ -201,14 +195,14 @@ class Git(FetchMethod):
needupdate = True
if needupdate:
try:
runfetchcmd("%s remote prune origin" % ud.basecmd, d)
runfetchcmd("%s remote rm origin" % ud.basecmd, d)
except bb.fetch2.FetchError:
logger.debug(1, "No Origin")
runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d)
fetch_cmd = "%s fetch -f --prune %s refs/*:refs/*" % (ud.basecmd, repourl)
if ud.proto.lower() != 'file':
bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
runfetchcmd(fetch_cmd, d)
runfetchcmd("%s prune-packed" % ud.basecmd, d)
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
@@ -242,23 +236,7 @@ class Git(FetchMethod):
if ud.bareclone:
cloneflags += " --mirror"
# Versions of git prior to 1.7.9.2 have issues where foo.git and foo get confused
# and you end up with some horrible union of the two when you attempt to clone it
# The least invasive workaround seems to be a symlink to the real directory to
# fool git into ignoring any .git version that may also be present.
#
# The issue is fixed in more recent versions of git so we can drop this hack in future
# when that version becomes common enough.
clonedir = ud.clonedir
if not ud.path.endswith(".git"):
indirectiondir = destdir[:-1] + ".indirectionsymlink"
if os.path.exists(indirectiondir):
os.remove(indirectiondir)
bb.utils.mkdirhier(os.path.dirname(indirectiondir))
os.symlink(ud.clonedir, indirectiondir)
clonedir = indirectiondir
runfetchcmd("git clone %s %s/ %s" % (cloneflags, clonedir, destdir), d)
runfetchcmd("git clone %s %s/ %s" % (cloneflags, ud.clonedir, destdir), d)
if not ud.nocheckout:
os.chdir(destdir)
if subdir != "":
@@ -303,8 +281,7 @@ class Git(FetchMethod):
basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
cmd = "%s ls-remote %s://%s%s%s %s" % \
(basecmd, ud.proto, username, ud.host, ud.path, ud.branches[name])
if ud.proto.lower() != 'file':
bb.fetch2.check_network_access(d, cmd)
bb.fetch2.check_network_access(d, cmd)
output = runfetchcmd(cmd, d, True)
if not output:
raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, url)
@@ -313,6 +290,38 @@ class Git(FetchMethod):
def _build_revision(self, url, ud, d, name):
return ud.revisions[name]
def _sortable_buildindex_disabled(self, url, ud, d, rev):
"""
Return a suitable buildindex for the revision specified. This is done by counting revisions
using "git rev-list" which may or may not work in different circumstances.
"""
cwd = os.getcwd()
# Check if we have the rev already
if not os.path.exists(ud.clonedir):
logger.debug(1, "GIT repository for %s does not exist in %s. \
Downloading.", url, ud.clonedir)
self.download(None, ud, d)
if not os.path.exists(ud.clonedir):
logger.error("GIT repository for %s does not exist in %s after \
download. Cannot get sortable buildnumber, using \
old value", url, ud.clonedir)
return None
os.chdir(ud.clonedir)
if not self._contains_ref(rev, d):
self.download(None, ud, d)
output = runfetchcmd("%s rev-list %s -- 2> /dev/null | wc -l" % (ud.basecmd, rev), d, quiet=True)
os.chdir(cwd)
buildindex = "%s" % output.split()[0]
logger.debug(1, "GIT repository for %s in %s is returning %s revisions in rev-list before %s", url, ud.clonedir, buildindex, rev)
return buildindex
def checkstatus(self, uri, ud, d):
fetchcmd = "%s ls-remote %s" % (ud.basecmd, uri)
try:

View File

@@ -82,7 +82,7 @@ class Hg(FetchMethod):
basecmd = data.expand('${FETCHCMD_hg}', d)
proto = ud.parm.get('protocol', 'http')
proto = ud.parm.get('proto', 'http')
host = ud.host
if proto == "file":
@@ -98,12 +98,7 @@ class Hg(FetchMethod):
return "%s identify -i %s://%s/%s" % (basecmd, proto, hgroot, ud.module)
options = [];
# Don't specify revision for the fetch; clone the entire repo.
# This avoids an issue if the specified revision is a tag, because
# the tag actually exists in the specified revision + 1, so it won't
# be available when used in any successive commands.
if ud.revision and command != "fetch":
if ud.revision:
options.append("-r %s" % ud.revision)
if command == "fetch":

View File

@@ -26,12 +26,10 @@ BitBake build tools.
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
import os
import urllib
import bb
import bb.utils
from bb import data
from bb.fetch2 import FetchMethod, FetchError
from bb.fetch2 import logger
from bb.fetch2 import FetchMethod
class Local(FetchMethod):
def supports(self, url, urldata, d):
@@ -42,37 +40,27 @@ class Local(FetchMethod):
def urldata_init(self, ud, d):
# We don't set localfile as for this fetcher the file is already local!
ud.decodedurl = urllib.unquote(ud.url.split("://")[1].split(";")[0])
ud.basename = os.path.basename(ud.decodedurl)
ud.basepath = ud.decodedurl
ud.basename = os.path.basename(ud.url.split("://")[1].split(";")[0])
return
def localpath(self, url, urldata, d):
"""
Return the local filename of a given url assuming a successful fetch.
"""
path = urldata.decodedurl
path = url.split("://")[1]
path = path.split(";")[0]
newpath = path
if path[0] != "/":
filespath = data.getVar('FILESPATH', d, True)
if filespath:
logger.debug(2, "Searching for %s in paths: \n%s" % (path, "\n ".join(filespath.split(":"))))
newpath = bb.utils.which(filespath, path)
if not newpath:
filesdir = data.getVar('FILESDIR', d, True)
if filesdir:
logger.debug(2, "Searching for %s in path: %s" % (path, filesdir))
newpath = os.path.join(filesdir, path)
if (not newpath or not os.path.exists(newpath)) and path.find("*") != -1:
# For expressions using '*', best we can do is take the first directory in FILESPATH that exists
newpath = bb.utils.which(filespath, ".")
logger.debug(2, "Searching for %s in path: %s" % (path, newpath))
return newpath
if not os.path.exists(newpath):
dldirfile = os.path.join(d.getVar("DL_DIR", True), path)
logger.debug(2, "Defaulting to %s for %s" % (dldirfile, path))
bb.utils.mkdirhier(os.path.dirname(dldirfile))
return dldirfile
if not os.path.exists(newpath) and path.find("*") == -1:
dldirfile = os.path.join(data.getVar("DL_DIR", d, True), os.path.basename(path))
return dldirfile
return newpath
def need_update(self, url, ud, d):
@@ -85,20 +73,7 @@ class Local(FetchMethod):
def download(self, url, urldata, d):
"""Fetch urls (no-op for Local method)"""
# no need to fetch local files, we'll deal with them in place.
if self.supports_checksum(urldata) and not os.path.exists(urldata.localpath):
locations = []
filespath = data.getVar('FILESPATH', d, True)
if filespath:
locations = filespath.split(":")
filesdir = data.getVar('FILESDIR', d, True)
if filesdir:
locations.append(filesdir)
locations.append(d.getVar("DL_DIR", True))
msg = "Unable to find file " + url + " anywhere. The paths that were searched were:\n " + "\n ".join(locations)
raise FetchError(msg)
return True
return 1
def checkstatus(self, url, urldata, d):
"""

View File

@@ -57,7 +57,7 @@ class Osc(FetchMethod):
basecmd = data.expand('${FETCHCMD_osc}', d)
proto = ud.parm.get('protocol', 'ocs')
proto = ud.parm.get('proto', 'ocs')
options = []

View File

@@ -27,7 +27,6 @@ BitBake build tools.
from future_builtins import zip
import os
import subprocess
import logging
import bb
from bb import data
@@ -91,8 +90,8 @@ class Perforce(FetchMethod):
p4cmd = data.getVar('FETCHCOMMAND_p4', d, True)
logger.debug(1, "Running %s%s changes -m 1 %s", p4cmd, p4opt, depot)
p4file, errors = bb.process.run("%s%s changes -m 1 %s" % (p4cmd, p4opt, depot))
cset = p4file.strip()
p4file = os.popen("%s%s changes -m 1 %s" % (p4cmd, p4opt, depot))
cset = p4file.readline().strip()
logger.debug(1, "READ %s", cset)
if not cset:
return -1
@@ -155,8 +154,8 @@ class Perforce(FetchMethod):
logger.debug(2, "Fetch: creating temporary directory")
bb.utils.mkdirhier(data.expand('${WORKDIR}', localdata))
data.setVar('TMPBASE', data.expand('${WORKDIR}/oep4.XXXXXX', localdata), localdata)
tmpfile, errors = bb.process.run(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmpfile.strip()
tmppipe = os.popen(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmppipe.readline().strip()
if not tmpfile:
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", loc)
@@ -169,8 +168,7 @@ class Perforce(FetchMethod):
os.chdir(tmpfile)
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 = p4file.strip()
p4file = os.popen("%s%s files %s" % (p4cmd, p4opt, depot))
if not p4file:
raise FetchError("Fetch: unable to get the P4 files from %s" % depot, loc)
@@ -186,7 +184,7 @@ class Perforce(FetchMethod):
dest = list[0][len(path)+1:]
where = dest.find("#")
subprocess.call("%s%s print -o %s/%s %s" % (p4cmd, p4opt, module, dest[:where], list[0]), shell=True)
os.system("%s%s print -o %s/%s %s" % (p4cmd, p4opt, module, dest[:where], list[0]))
count = count + 1
if count == 0:

View File

@@ -69,9 +69,6 @@ class SSH(FetchMethod):
def supports(self, url, urldata, d):
return __pattern__.match(url) != None
def supports_checksum(self, urldata):
return False
def localpath(self, url, urldata, d):
m = __pattern__.match(urldata.url)
path = m.group('path')

View File

@@ -77,8 +77,8 @@ class Svk(FetchMethod):
logger.debug(2, "Fetch: creating temporary directory")
bb.utils.mkdirhier(data.expand('${WORKDIR}', localdata))
data.setVar('TMPBASE', data.expand('${WORKDIR}/oesvk.XXXXXX', localdata), localdata)
tmpfile, errors = bb.process.run(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmpfile.strip()
tmppipe = os.popen(data.getVar('MKTEMPDIRCMD', localdata, True) or "false")
tmpfile = tmppipe.readline().strip()
if not tmpfile:
logger.error()
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", loc)

View File

@@ -49,8 +49,6 @@ class Svn(FetchMethod):
if not "module" in ud.parm:
raise MissingParameterError('module', ud.url)
ud.basecmd = d.getVar('FETCHCMD_svn', True)
ud.module = ud.parm["module"]
# Create paths to svn checkouts
@@ -71,7 +69,9 @@ class Svn(FetchMethod):
command is "fetch", "update", "info"
"""
proto = ud.parm.get('protocol', 'svn')
basecmd = data.expand('${FETCHCMD_svn}', d)
proto = ud.parm.get('proto', 'svn')
svn_rsh = None
if proto == "svn+ssh" and "rsh" in ud.parm:
@@ -88,7 +88,7 @@ class Svn(FetchMethod):
options.append("--password %s" % ud.pswd)
if command == "info":
svncmd = "%s info %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module)
svncmd = "%s info %s %s://%s/%s/" % (basecmd, " ".join(options), proto, svnroot, ud.module)
else:
suffix = ""
if ud.revision:
@@ -96,9 +96,9 @@ class Svn(FetchMethod):
suffix = "@%s" % (ud.revision)
if command == "fetch":
svncmd = "%s co %s %s://%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module, suffix, ud.module)
svncmd = "%s co %s %s://%s/%s%s %s" % (basecmd, " ".join(options), proto, svnroot, ud.module, suffix, ud.module)
elif command == "update":
svncmd = "%s update %s" % (ud.basecmd, " ".join(options))
svncmd = "%s update %s" % (basecmd, " ".join(options))
else:
raise FetchError("Invalid svn command %s" % command, ud.url)
@@ -117,11 +117,6 @@ class Svn(FetchMethod):
logger.info("Update " + loc)
# update sources there
os.chdir(ud.moddir)
# We need to attempt to run svn upgrade first in case its an older working format
try:
runfetchcmd(ud.basecmd + " upgrade", d)
except FetchError:
pass
logger.debug(1, "Running %s", svnupdatecmd)
bb.fetch2.check_network_access(d, svnupdatecmd, ud.url)
runfetchcmd(svnupdatecmd, d)

View File

@@ -45,55 +45,47 @@ class Wget(FetchMethod):
"""
return ud.type in ['http', 'https', 'ftp']
def recommends_checksum(self, urldata):
return True
def urldata_init(self, ud, d):
if 'protocol' in ud.parm:
if ud.parm['protocol'] == 'git':
raise bb.fetch2.ParameterError("Invalid protocol - if you wish to fetch from a git repository using http, you need to instead use the git:// prefix with protocol=http", ud.url)
if 'downloadfilename' in ud.parm:
ud.basename = ud.parm['downloadfilename']
else:
ud.basename = os.path.basename(ud.path)
ud.basename = os.path.basename(ud.path)
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
def download(self, uri, ud, d, checkonly = False):
"""Fetch urls"""
basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
def fetch_uri(uri, ud, d):
if checkonly:
fetchcmd = data.getVar("CHECKCOMMAND", d, True)
elif os.path.exists(ud.localpath):
# file exists, but we didnt complete it.. trying again..
fetchcmd = data.getVar("RESUMECOMMAND", d, True)
else:
fetchcmd = data.getVar("FETCHCOMMAND", d, True)
if 'downloadfilename' in ud.parm:
basecmd += " -O ${DL_DIR}/" + ud.localfile
uri = uri.split(";")[0]
uri_decoded = list(decodeurl(uri))
uri_type = uri_decoded[0]
uri_host = uri_decoded[1]
if checkonly:
fetchcmd = d.getVar("CHECKCOMMAND_wget", True) or d.expand(basecmd + " --spider '${URI}'")
elif os.path.exists(ud.localpath):
# file exists, but we didnt complete it.. trying again..
fetchcmd = d.getVar("RESUMECOMMAND_wget", True) or d.expand(basecmd + " -c -P ${DL_DIR} '${URI}'")
else:
fetchcmd = d.getVar("FETCHCOMMAND_wget", True) or d.expand(basecmd + " -P ${DL_DIR} '${URI}'")
fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0])
fetchcmd = fetchcmd.replace("${FILE}", ud.basename)
if not checkonly:
logger.info("fetch " + uri)
logger.debug(2, "executing " + fetchcmd)
bb.fetch2.check_network_access(d, fetchcmd)
runfetchcmd(fetchcmd, d, quiet=checkonly)
uri = uri.split(";")[0]
uri_decoded = list(decodeurl(uri))
uri_type = uri_decoded[0]
uri_host = uri_decoded[1]
# Sanity check since wget can pretend it succeed when it didn't
# Also, this used to happen if sourceforge sent us to the mirror page
if not os.path.exists(ud.localpath) and not checkonly:
raise FetchError("The fetch command returned success for url %s but %s doesn't exist?!" % (uri, ud.localpath), uri)
fetchcmd = fetchcmd.replace("${URI}", uri.split(";")[0])
fetchcmd = fetchcmd.replace("${FILE}", ud.basename)
if not checkonly:
logger.info("fetch " + uri)
logger.debug(2, "executing " + fetchcmd)
bb.fetch2.check_network_access(d, fetchcmd)
runfetchcmd(fetchcmd, d, quiet=checkonly)
# Sanity check since wget can pretend it succeed when it didn't
# Also, this used to happen if sourceforge sent us to the mirror page
if not os.path.exists(ud.localpath) and not checkonly:
raise FetchError("The fetch command returned success for url %s but %s doesn't exist?!" % (uri, ud.localpath), uri)
localdata = data.createCopy(d)
data.setVar('OVERRIDES', "wget:" + data.getVar('OVERRIDES', localdata), localdata)
data.update_data(localdata)
fetch_uri(uri, ud, localdata)
return True
def checkstatus(self, uri, ud, d):

View File

@@ -33,7 +33,9 @@
from bb.utils import better_compile, better_exec
from bb import error
# A dict of function names we have seen
# A dict of modules we have handled
# it is the number of .bbclasses + x in size
_parsed_methods = { }
_parsed_fns = { }
def insert_method(modulename, code, fn):
@@ -50,22 +52,33 @@ def insert_method(modulename, code, fn):
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]))
error( "Error Method already seen: %s in' %s' now in '%s'" % (name, _parsed_fns[name], modulename))
else:
_parsed_fns[name] = modulename
# A dict of modules the parser has finished with
_parsed_methods = {}
def check_insert_method(modulename, code, fn):
"""
Add the code if it wasnt added before. The module
name will be used for that
Variables:
@modulename a short name e.g. base.bbclass
@code The actual python code
@fn The filename from the outer file
"""
if not modulename in _parsed_methods:
return insert_method(modulename, code, fn)
_parsed_methods[modulename] = 1
def parsed_module(modulename):
"""
Has module been parsed?
Inform me file xyz was parsed
"""
return modulename in _parsed_methods
def set_parsed_module(modulename):
"""
Set module as parsed
"""
_parsed_methods[modulename] = True
def get_parsed_dict():
"""
shortcut
"""
return _parsed_methods

View File

@@ -176,7 +176,6 @@ class diskMonitor:
def __init__(self, configuration):
self.enableMonitor = False
self.configuration = configuration
BBDirs = configuration.getVar("BB_DISKMON_DIRS", True) or None
if BBDirs:
@@ -220,12 +219,10 @@ class diskMonitor:
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, 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[dev] = True
rq.finish_runqueue(True)
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
@@ -240,10 +237,8 @@ class diskMonitor:
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', 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[dev] = True
rq.finish_runqueue(True)
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeSpace, self.devDict[dev][1]), self.configuration)
return

View File

@@ -23,7 +23,6 @@ Message handling infrastructure for bitbake
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import sys
import copy
import logging
import collections
from itertools import groupby
@@ -56,25 +55,6 @@ class BBLogFormatter(logging.Formatter):
CRITICAL: 'ERROR',
}
color_enabled = False
BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(29,38)
COLORS = {
DEBUG3 : CYAN,
DEBUG2 : CYAN,
DEBUG : CYAN,
VERBOSE : BASECOLOR,
NOTE : BASECOLOR,
PLAIN : BASECOLOR,
WARNING : YELLOW,
ERROR : RED,
CRITICAL: RED,
}
BLD = '\033[1;%dm'
STD = '\033[%dm'
RST = '\033[0m'
def getLevelName(self, levelno):
try:
return self.levelnames[levelno]
@@ -87,8 +67,6 @@ class BBLogFormatter(logging.Formatter):
if record.levelno == self.PLAIN:
msg = record.getMessage()
else:
if self.color_enabled:
record = self.colorize(record)
msg = logging.Formatter.format(self, record)
if hasattr(record, 'bb_exc_info'):
@@ -97,17 +75,6 @@ class BBLogFormatter(logging.Formatter):
msg += '\n' + ''.join(formatted)
return msg
def colorize(self, record):
color = self.COLORS[record.levelno]
if self.color_enabled and color is not None:
record = copy.copy(record)
record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
record.msg = "".join([self.STD % color, record.msg, self.RST])
return record
def enable_color(self):
self.color_enabled = True
class BBLogFilter(object):
def __init__(self, handler, level, debug_domains):
self.stdlevel = level

View File

@@ -73,7 +73,8 @@ def update_mtime(f):
def mark_dependency(d, f):
if f.startswith('./'):
f = "%s/%s" % (os.getcwd(), f[2:])
deps = (d.getVar('__depends') or []) + [(f, cached_mtime(f))]
deps = d.getVar('__depends') or set()
deps.update([(f, cached_mtime(f))])
d.setVar('__depends', deps)
def supports(fn, data):
@@ -87,8 +88,7 @@ def handle(fn, data, include = 0):
"""Call the handler that is appropriate for this file"""
for h in handlers:
if h['supports'](fn, data):
with data.inchistory.include(fn):
return h['handle'](fn, data, include)
return h['handle'](fn, data, include)
raise ParseError("not a BitBake file", fn)
def init(fn, data):
@@ -134,8 +134,8 @@ def vars_from_file(mypkg, d):
def get_file_depends(d):
'''Return the dependent files'''
dep_files = []
depends = d.getVar('__base_depends', True) or []
depends = depends + (d.getVar('__depends', True) or [])
depends = d.getVar('__depends', True) or set()
depends = depends.union(d.getVar('__base_depends', True) or set())
for (fn, _) in depends:
dep_files.append(os.path.abspath(fn))
return " ".join(dep_files)

View File

@@ -31,6 +31,7 @@ import itertools
from bb import methodpool
from bb.parse import logger
__parsed_methods__ = bb.methodpool.get_parsed_dict()
_bbversions_re = re.compile(r"\[(?P<from>[0-9]+)-(?P<to>[0-9]+)\]")
class StatementGroup(list):
@@ -68,7 +69,7 @@ class ExportNode(AstNode):
self.var = var
def eval(self, data):
data.setVarFlag(self.var, "export", 1, op = 'exported')
data.setVarFlag(self.var, "export", 1)
class DataNode(AstNode):
"""
@@ -90,53 +91,33 @@ class DataNode(AstNode):
def eval(self, data):
groupd = self.groupd
key = groupd["var"]
loginfo = {
'variable': key,
'file': self.filename,
'line': self.lineno,
}
if "exp" in groupd and groupd["exp"] != None:
data.setVarFlag(key, "export", 1, op = 'exported', **loginfo)
op = "set"
data.setVarFlag(key, "export", 1)
if "ques" in groupd and groupd["ques"] != None:
val = self.getFunc(key, data)
op = "set?"
if val == None:
val = groupd["value"]
elif "colon" in groupd and groupd["colon"] != None:
e = data.createCopy()
bb.data.update_data(e)
op = "immediate"
val = e.expand(groupd["value"], key + "[:=]")
elif "append" in groupd and groupd["append"] != None:
op = "append"
val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"])
elif "prepend" in groupd and groupd["prepend"] != None:
op = "prepend"
val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
elif "postdot" in groupd and groupd["postdot"] != None:
op = "postdot"
val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
elif "predot" in groupd and groupd["predot"] != None:
op = "predot"
val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
else:
val = groupd["value"]
flag = None
if 'flag' in groupd and groupd['flag'] != None:
flag = groupd['flag']
data.setVarFlag(key, groupd['flag'], val)
elif groupd["lazyques"]:
flag = "defaultval"
loginfo['op'] = op
loginfo['detail'] = groupd["value"]
if flag:
data.setVarFlag(key, flag, val, **loginfo)
data.setVarFlag(key, "defaultval", val)
else:
data.setVar(key, val, **loginfo)
data.setVar(key, val)
class MethodNode(AstNode):
def __init__(self, filename, lineno, func_name, body):
@@ -145,25 +126,23 @@ class MethodNode(AstNode):
self.body = body
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('/.+-', '____'))))
if not funcname in bb.methodpool._parsed_fns:
text = "def %s(d):\n" % (funcname) + text
text = "def %s(d):\n" % (funcname) + '\n'.join(self.body)
bb.methodpool.insert_method(funcname, text, self.filename)
anonfuncs = data.getVar('__BBANONFUNCS') or []
anonfuncs.append(funcname)
data.setVar('__BBANONFUNCS', anonfuncs)
data.setVar(funcname, text)
else:
data.setVarFlag(self.func_name, "func", 1)
data.setVar(self.func_name, text)
data.setVar(self.func_name, '\n'.join(self.body))
class PythonMethodNode(AstNode):
def __init__(self, filename, lineno, function, modulename, body):
def __init__(self, filename, lineno, function, define, body):
AstNode.__init__(self, filename, lineno)
self.function = function
self.modulename = modulename
self.define = define
self.body = body
def eval(self, data):
@@ -171,8 +150,8 @@ class PythonMethodNode(AstNode):
# 'this' file. This means we will not parse methods from
# bb classes twice
text = '\n'.join(self.body)
if not bb.methodpool.parsed_module(self.modulename):
bb.methodpool.insert_method(self.modulename, text, self.filename)
if not bb.methodpool.parsed_module(self.define):
bb.methodpool.insert_method(self.define, text, self.filename)
data.setVarFlag(self.function, "func", 1)
data.setVarFlag(self.function, "python", 1)
data.setVar(self.function, text)
@@ -199,35 +178,44 @@ class MethodFlagsNode(AstNode):
data.delVarFlag(self.key, "fakeroot")
class ExportFuncsNode(AstNode):
def __init__(self, filename, lineno, fns, classname):
def __init__(self, filename, lineno, fns, classes):
AstNode.__init__(self, filename, lineno)
self.n = fns.split()
self.classname = classname
self.classes = classes
def eval(self, data):
for f in self.n:
allvars = []
allvars.append(f)
allvars.append(self.classes[-1] + "_" + f)
for func in self.n:
calledfunc = self.classname + "_" + func
vars = [[ allvars[0], allvars[1] ]]
if len(self.classes) > 1 and self.classes[-2] is not None:
allvars.append(self.classes[-2] + "_" + f)
vars = []
vars.append([allvars[2], allvars[1]])
vars.append([allvars[0], allvars[2]])
if data.getVar(func) and not data.getVarFlag(func, 'export_func'):
continue
for (var, calledvar) in vars:
if data.getVar(var) and not data.getVarFlag(var, 'export_func'):
continue
if data.getVar(func):
data.setVarFlag(func, 'python', None)
data.setVarFlag(func, 'func', None)
if data.getVar(var):
data.setVarFlag(var, 'python', None)
data.setVarFlag(var, 'func', None)
for flag in [ "func", "python" ]:
if data.getVarFlag(calledfunc, flag):
data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag))
for flag in [ "dirs" ]:
if data.getVarFlag(func, flag):
data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag))
for flag in [ "func", "python" ]:
if data.getVarFlag(calledvar, flag):
data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag))
for flag in [ "dirs" ]:
if data.getVarFlag(var, flag):
data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag))
if data.getVarFlag(calledfunc, "python"):
data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n")
else:
data.setVar(func, " " + calledfunc + "\n")
data.setVarFlag(func, 'export_func', '1')
if data.getVarFlag(calledvar, "python"):
data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n")
else:
data.setVar(var, "\t" + calledvar + "\n")
data.setVarFlag(var, 'export_func', '1')
class AddTaskNode(AstNode):
def __init__(self, filename, lineno, func, before, after):
@@ -293,14 +281,14 @@ def handleData(statements, filename, lineno, groupd):
def handleMethod(statements, filename, lineno, func_name, body):
statements.append(MethodNode(filename, lineno, func_name, body))
def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
def handlePythonMethod(statements, filename, lineno, funcname, root, body):
statements.append(PythonMethodNode(filename, lineno, funcname, root, body))
def handleMethodFlags(statements, filename, lineno, key, m):
statements.append(MethodFlagsNode(filename, lineno, key, m))
def handleExportFuncs(statements, filename, lineno, m, classname):
statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
def handleExportFuncs(statements, filename, lineno, m, classes):
statements.append(ExportFuncsNode(filename, lineno, m.group(1), classes))
def handleAddTask(statements, filename, lineno, m):
func = m.group("func")
@@ -332,7 +320,7 @@ def finalize(fn, d, variant = None):
code = []
for funcname in d.getVar("__BBANONFUNCS") or []:
code.append("%s(d)" % funcname)
bb.utils.better_exec("\n".join(code), {"d": d})
bb.utils.simple_exec("\n".join(code), {"d": d})
bb.data.update_data(d)
tasklist = d.getVar('__BBTASKS') or []

View File

@@ -51,6 +51,7 @@ __infunc__ = ""
__inpython__ = False
__body__ = []
__classname__ = ""
classes = [ None, ]
cached_statements = {}
@@ -68,25 +69,18 @@ def supports(fn, d):
return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
def inherit(files, fn, lineno, d):
__inherit_cache = d.getVar('__inherit_cache') or []
__inherit_cache = data.getVar('__inherit_cache', d) or []
files = d.expand(files).split()
for file in files:
if not os.path.isabs(file) and not file.endswith(".bbclass"):
file = os.path.join('classes', '%s.bbclass' % file)
if not os.path.isabs(file):
dname = os.path.dirname(fn)
bbpath = "%s:%s" % (dname, d.getVar("BBPATH", True))
abs_fn = bb.utils.which(bbpath, file)
if abs_fn:
file = abs_fn
if not file in __inherit_cache:
logger.log(logging.DEBUG -1, "BB %s:%d: inheriting %s", fn, lineno, file)
__inherit_cache.append( file )
d.setVar('__inherit_cache', __inherit_cache)
data.setVar('__inherit_cache', __inherit_cache, d)
include(fn, file, lineno, d, "inherit")
__inherit_cache = d.getVar('__inherit_cache') or []
__inherit_cache = data.getVar('__inherit_cache', d) or []
def get_statements(filename, absolute_filename, base_name):
global cached_statements
@@ -113,7 +107,7 @@ def get_statements(filename, absolute_filename, base_name):
return statements
def handle(fn, d, include):
global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__, __classname__
global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__
__body__ = []
__infunc__ = ""
__classname__ = ""
@@ -131,13 +125,14 @@ def handle(fn, d, include):
if ext == ".bbclass":
__classname__ = root
__inherit_cache = d.getVar('__inherit_cache') or []
classes.append(__classname__)
__inherit_cache = data.getVar('__inherit_cache', d) or []
if not fn in __inherit_cache:
__inherit_cache.append(fn)
d.setVar('__inherit_cache', __inherit_cache)
data.setVar('__inherit_cache', __inherit_cache, d)
if include != 0:
oldfile = d.getVar('FILE')
oldfile = data.getVar('FILE', d)
else:
oldfile = None
@@ -151,29 +146,27 @@ def handle(fn, d, include):
# DONE WITH PARSING... time to evaluate
if ext != ".bbclass":
d.setVar('FILE', abs_fn)
data.setVar('FILE', abs_fn, d)
try:
statements.eval(d)
except bb.parse.SkipPackage:
bb.data.setVar("__SKIPPED", True, d)
statements.eval(d)
if ext == ".bbclass":
classes.remove(__classname__)
else:
if include == 0:
return { "" : d }
if ext != ".bbclass" and include == 0:
return ast.multi_finalize(fn, d)
return ast.multi_finalize(fn, d)
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)
bb.methodpool.get_parsed_dict()[base_name] = 1
return d
def feeder(lineno, s, fn, root, statements):
global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__
global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, classes, bb, __residue__
if __infunc__:
if s == '}':
__body__.append('')
@@ -200,10 +193,7 @@ def feeder(lineno, s, fn, root, statements):
if s and s[0] == '#':
if len(__residue__) != 0 and __residue__[0][0] != "#":
bb.fatal("There is a comment on line %s of file %s (%s) which is in the middle of a multiline expression.\nBitbake used to ignore these but no longer does so, please fix your metadata as errors are likely as a result of this change." % (lineno, fn, s))
if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"):
bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
bb.error("There is a comment on line %s of file %s (%s) which is in the middle of a multiline expression.\nBitbake used to ignore these but no longer does so, please fix your metadata as errors are likely as a result of this change." % (lineno, fn, s))
if s and s[-1] == '\\':
__residue__.append(s[:-1])
@@ -235,7 +225,7 @@ def feeder(lineno, s, fn, root, statements):
m = __export_func_regexp__.match(s)
if m:
ast.handleExportFuncs(statements, fn, lineno, m, __classname__)
ast.handleExportFuncs(statements, fn, lineno, m, classes)
return
m = __addtask_regexp__.match(s)

View File

@@ -29,7 +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)$")
__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\-_+.${}/]+)$" )
@@ -98,22 +98,15 @@ def handle(fn, data, include):
while True:
lineno = lineno + 1
s = f.readline()
if not s:
break
if not s: break
w = s.strip()
# skip empty lines
if not w:
continue
if not w: continue # skip empty lines
s = s.rstrip()
if s[0] == '#': continue # skip comments
while s[-1] == '\\':
s2 = f.readline().strip()
lineno = lineno + 1
if (not s2 or s2 and s2[0] != "#") and s[0] == "#" :
bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
s = s[:-1] + s2
# skip comments
if s[0] == '#':
continue
feeder(lineno, s, fn, statements)
# DONE WITH PARSING... time to evaluate

View File

@@ -125,11 +125,6 @@ class SQLTable(collections.MutableMapping):
return len(self) < len(other)
def get_by_pattern(self, pattern):
data = self._execute("SELECT * FROM %s WHERE key LIKE ?;" %
self.table, [pattern])
return [row[1] for row in data]
def values(self):
return list(self.itervalues())

View File

@@ -1,8 +1,6 @@
import logging
import signal
import subprocess
import errno
import select
logger = logging.getLogger('BitBake.Process')
@@ -70,38 +68,20 @@ def _logged_communicate(pipe, log, input):
pipe.stdin.write(input)
pipe.stdin.close()
bufsize = 512
outdata, errdata = [], []
rin = []
while pipe.poll() is None:
if pipe.stdout is not None:
data = pipe.stdout.read(bufsize)
if data is not None:
outdata.append(data)
log.write(data)
if pipe.stdout is not None:
bb.utils.nonblockingfd(pipe.stdout.fileno())
rin.append(pipe.stdout)
if pipe.stderr is not None:
bb.utils.nonblockingfd(pipe.stderr.fileno())
rin.append(pipe.stderr)
try:
while pipe.poll() is None:
rlist = rin
try:
r,w,e = select.select (rlist, [], [])
except OSError, e:
if e.errno != errno.EINTR:
raise
if pipe.stdout in r:
data = pipe.stdout.read()
if data is not None:
outdata.append(data)
log.write(data)
if pipe.stderr in r:
data = pipe.stderr.read()
if data is not None:
errdata.append(data)
log.write(data)
finally:
log.flush()
if pipe.stderr is not None:
data = pipe.stderr.read(bufsize)
if data is not None:
errdata.append(data)
log.write(data)
return ''.join(outdata), ''.join(errdata)
def run(cmd, input=None, log=None, **options):

View File

@@ -35,8 +35,6 @@ class NoProvider(bb.BBHandledException):
class NoRProvider(bb.BBHandledException):
"""Exception raised when no provider of a runtime dependency can be found"""
class MultipleRProvider(bb.BBHandledException):
"""Exception raised when multiple providers of a runtime dependency can be found"""
def findProviders(cfgData, dataCache, pkg_pn = None):
"""
@@ -130,7 +128,7 @@ def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
if m:
if m.group(1):
preferred_e = m.group(1)[:-1]
preferred_e = int(m.group(1)[:-1])
else:
preferred_e = None
preferred_v = m.group(2)

View File

@@ -375,8 +375,9 @@ class RunQueueData:
"""
runq_build = []
recursivetasks = {}
recursivetasksselfref = set()
recursive_tdepends = {}
runq_recrdepends = []
tdepends_fnid = {}
taskData = self.taskData
@@ -405,10 +406,11 @@ class RunQueueData:
depdata = taskData.build_targets[depid][0]
if depdata is None:
continue
dep = taskData.fn_index[depdata]
for taskname in tasknames:
taskid = taskData.gettask_id_fromfnid(depdata, taskname)
taskid = taskData.gettask_id(dep, taskname, False)
if taskid is not None:
depends.add(taskid)
depends.append(taskid)
def add_runtime_dependencies(depids, tasknames, depends):
for depid in depids:
@@ -417,20 +419,15 @@ class RunQueueData:
depdata = taskData.run_targets[depid][0]
if depdata is None:
continue
dep = taskData.fn_index[depdata]
for taskname in tasknames:
taskid = taskData.gettask_id_fromfnid(depdata, taskname)
taskid = taskData.gettask_id(dep, taskname, False)
if taskid is not None:
depends.add(taskid)
def add_resolved_dependencies(depids, tasknames, depends):
for depid in depids:
for taskname in tasknames:
taskid = taskData.gettask_id_fromfnid(depid, taskname)
if taskid is not None:
depends.add(taskid)
depends.append(taskid)
for task in xrange(len(taskData.tasks_name)):
depends = set()
depends = []
recrdepends = []
fnid = taskData.tasks_fnid[task]
fn = taskData.fn_index[fnid]
task_deps = self.dataCache.task_deps[fn]
@@ -442,7 +439,7 @@ class RunQueueData:
# Resolve task internal dependencies
#
# e.g. addtask before X after Y
depends = set(taskData.tasks_tdepends[task])
depends = taskData.tasks_tdepends[task]
# Resolve 'deptask' dependencies
#
@@ -457,91 +454,99 @@ class RunQueueData:
# e.g. do_sometask[rdeptask] = "do_someothertask"
# (makes sure sometask runs after someothertask of all RDEPENDS)
if 'rdeptask' in task_deps and taskData.tasks_name[task] in task_deps['rdeptask']:
tasknames = task_deps['rdeptask'][taskData.tasks_name[task]].split()
add_runtime_dependencies(taskData.rdepids[fnid], tasknames, depends)
taskname = task_deps['rdeptask'][taskData.tasks_name[task]]
add_runtime_dependencies(taskData.rdepids[fnid], [taskname], depends)
# Resolve inter-task dependencies
#
# e.g. do_sometask[depends] = "targetname:do_someothertask"
# (makes sure sometask runs after targetname's someothertask)
if fnid not in tdepends_fnid:
tdepends_fnid[fnid] = set()
idepends = taskData.tasks_idepends[task]
for (depid, idependtask) in idepends:
if depid in taskData.build_targets and not depid in taskData.failed_deps:
if depid in taskData.build_targets:
# Won't be in build_targets if ASSUME_PROVIDED
depdata = taskData.build_targets[depid][0]
if depdata is not None:
taskid = taskData.gettask_id_fromfnid(depdata, idependtask)
dep = taskData.fn_index[depdata]
taskid = taskData.gettask_id(dep, idependtask, False)
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, dep))
depends.add(taskid)
irdepends = taskData.tasks_irdepends[task]
for (depid, idependtask) in irdepends:
if depid in taskData.run_targets:
# Won't be in run_targets if ASSUME_PROVIDED
depdata = taskData.run_targets[depid][0]
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, dep))
depends.add(taskid)
depends.append(taskid)
if depdata != fnid:
tdepends_fnid[fnid].add(taskid)
# Resolve recursive 'recrdeptask' dependencies (Part A)
# Resolve recursive 'recrdeptask' dependencies (A)
#
# e.g. do_sometask[recrdeptask] = "do_someothertask"
# (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
# We cover the recursive part of the dependencies below
if 'recrdeptask' in task_deps and taskData.tasks_name[task] in task_deps['recrdeptask']:
tasknames = task_deps['recrdeptask'][taskData.tasks_name[task]].split()
recursivetasks[task] = tasknames
add_build_dependencies(taskData.depids[fnid], tasknames, depends)
add_runtime_dependencies(taskData.rdepids[fnid], tasknames, depends)
if taskData.tasks_name[task] in tasknames:
recursivetasksselfref.add(task)
for taskname in task_deps['recrdeptask'][taskData.tasks_name[task]].split():
recrdepends.append(taskname)
add_build_dependencies(taskData.depids[fnid], [taskname], depends)
add_runtime_dependencies(taskData.rdepids[fnid], [taskname], depends)
# Rmove all self references
if task in depends:
newdep = []
logger.debug(2, "Task %s (%s %s) contains self reference! %s", task, taskData.fn_index[taskData.tasks_fnid[task]], taskData.tasks_name[task], depends)
for dep in depends:
if task != dep:
newdep.append(dep)
depends = newdep
self.runq_fnid.append(taskData.tasks_fnid[task])
self.runq_task.append(taskData.tasks_name[task])
self.runq_depends.append(depends)
self.runq_depends.append(set(depends))
self.runq_revdeps.append(set())
self.runq_hash.append("")
runq_build.append(0)
runq_recrdepends.append(recrdepends)
# Resolve recursive 'recrdeptask' dependencies (Part B)
#
# Build a list of recursive cumulative dependencies for each fnid
# We do this by fnid, since if A depends on some task in B
# we're interested in later tasks B's fnid might have but B itself
# doesn't depend on
#
# Algorithm is O(tasks) + O(tasks)*O(fnids)
#
reccumdepends = {}
for task in xrange(len(self.runq_fnid)):
fnid = self.runq_fnid[task]
if fnid not in reccumdepends:
if fnid in tdepends_fnid:
reccumdepends[fnid] = tdepends_fnid[fnid]
else:
reccumdepends[fnid] = set()
reccumdepends[fnid].update(self.runq_depends[task])
for task in xrange(len(self.runq_fnid)):
taskfnid = self.runq_fnid[task]
for fnid in reccumdepends:
if task in reccumdepends[fnid]:
reccumdepends[fnid].add(task)
if taskfnid in reccumdepends:
reccumdepends[fnid].update(reccumdepends[taskfnid])
# Resolve recursive 'recrdeptask' dependencies (B)
#
# e.g. do_sometask[recrdeptask] = "do_someothertask"
# (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
# We need to do this separately since we need all of self.runq_depends to be complete before this is processed
extradeps = {}
for task in recursivetasks:
extradeps[task] = set(self.runq_depends[task])
tasknames = recursivetasks[task]
seendeps = set()
seenfnid = []
def generate_recdeps(t):
newdeps = set()
add_resolved_dependencies([taskData.tasks_fnid[t]], tasknames, newdeps)
extradeps[task].update(newdeps)
seendeps.add(t)
newdeps.add(t)
for i in newdeps:
for n in self.runq_depends[i]:
if n not in seendeps:
generate_recdeps(n)
generate_recdeps(task)
# Remove circular references so that do_a[recrdeptask] = "do_a do_b" can work
for task in recursivetasks:
extradeps[task].difference_update(recursivetasksselfref)
for task in xrange(len(taskData.tasks_name)):
# Add in extra dependencies
if task in extradeps:
self.runq_depends[task] = extradeps[task]
# Remove all self references
if task in self.runq_depends[task]:
logger.debug(2, "Task %s (%s %s) contains self reference! %s", task, taskData.fn_index[taskData.tasks_fnid[task]], taskData.tasks_name[task], self.runq_depends[task])
self.runq_depends[task].remove(task)
for task in xrange(len(self.runq_fnid)):
if len(runq_recrdepends[task]) > 0:
taskfnid = self.runq_fnid[task]
for dep in reccumdepends[taskfnid]:
# Ignore self references
if dep == task:
continue
for taskname in runq_recrdepends[task]:
if taskData.tasks_name[dep] == taskname:
self.runq_depends[task].add(dep)
# Step B - Mark all active tasks
#
@@ -692,36 +697,13 @@ class RunQueueData:
stampfnwhitelist.append(fn)
self.stampfnwhitelist = stampfnwhitelist
# Iterate over the task list looking for tasks with a 'setscene' function
# Interate over the task list looking for tasks with a 'setscene' function
self.runq_setscene = []
if not self.cooker.configuration.nosetscene:
for task in range(len(self.runq_fnid)):
setscene = taskData.gettask_id(self.taskData.fn_index[self.runq_fnid[task]], self.runq_task[task] + "_setscene", False)
if not setscene:
continue
self.runq_setscene.append(task)
def invalidate_task(fn, taskname, error_nostamp):
taskdep = self.dataCache.task_deps[fn]
if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
if error_nostamp:
bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
else:
bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
else:
logger.verbose("Invalidate task %s, %s", taskname, fn)
bb.parse.siggen.invalidate_task(taskname, self.dataCache, fn)
# Invalidate task if force mode active
if self.cooker.configuration.force:
for (fn, target) in self.target_pairs:
invalidate_task(fn, target, False)
# Invalidate task if invalidate mode active
if self.cooker.configuration.invalidate_stamp:
for (fn, target) in self.target_pairs:
for st in self.cooker.configuration.invalidate_stamp.split(','):
invalidate_task(fn, "do_%s" % st, True)
for task in range(len(self.runq_fnid)):
setscene = taskData.gettask_id(self.taskData.fn_index[self.runq_fnid[task]], self.runq_task[task] + "_setscene", False)
if not setscene:
continue
self.runq_setscene.append(task)
# Interate over the task list and call into the siggen code
dealtwith = set()
@@ -749,6 +731,12 @@ class RunQueueData:
deps.append(depidentifier)
self.hash_deps[identifier] = deps
# Remove stamps for targets if force mode active
if self.cooker.configuration.force:
for (fn, target) in self.target_pairs:
logger.verbose("Remove stamp %s, %s", target, fn)
bb.build.del_stamp(target, self.dataCache, fn)
return len(self.runq_fnid)
def dump_data(self, taskQueue):
@@ -785,7 +773,6 @@ class RunQueue:
self.stamppolicy = cfgData.getVar("BB_STAMP_POLICY", True) or "perfile"
self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION", True) or None
self.setsceneverify = cfgData.getVar("BB_SETSCENE_VERIFY_FUNCTION", True) or None
self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID", True) or None
self.state = runQueuePrepare
@@ -794,7 +781,101 @@ class RunQueue:
self.rqexe = None
def check_stamp_task(self, task, taskname = None, recurse = False, cache = None):
def check_stamps(self):
unchecked = {}
current = []
notcurrent = []
buildable = []
if self.stamppolicy == "perfile":
fulldeptree = False
else:
fulldeptree = True
stampwhitelist = []
if self.stamppolicy == "whitelist":
stampwhitelist = self.rqdata.stampfnwhitelist
for task in xrange(len(self.rqdata.runq_fnid)):
unchecked[task] = ""
if len(self.rqdata.runq_depends[task]) == 0:
buildable.append(task)
def check_buildable(self, task, buildable):
for revdep in self.rqdata.runq_revdeps[task]:
alldeps = 1
for dep in self.rqdata.runq_depends[revdep]:
if dep in unchecked:
alldeps = 0
if alldeps == 1:
if revdep in unchecked:
buildable.append(revdep)
for task in xrange(len(self.rqdata.runq_fnid)):
if task not in unchecked:
continue
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
taskname = self.rqdata.runq_task[task]
stampfile = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
# If the stamp is missing its not current
if not os.access(stampfile, os.F_OK):
del unchecked[task]
notcurrent.append(task)
check_buildable(self, task, buildable)
continue
# If its a 'nostamp' task, it's not current
taskdep = self.rqdata.dataCache.task_deps[fn]
if 'nostamp' in taskdep and task in taskdep['nostamp']:
del unchecked[task]
notcurrent.append(task)
check_buildable(self, task, buildable)
continue
while (len(buildable) > 0):
nextbuildable = []
for task in buildable:
if task in unchecked:
fn = self.taskData.fn_index[self.rqdata.runq_fnid[task]]
taskname = self.rqdata.runq_task[task]
stampfile = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
iscurrent = True
t1 = os.stat(stampfile)[stat.ST_MTIME]
for dep in self.rqdata.runq_depends[task]:
if iscurrent:
fn2 = self.taskData.fn_index[self.rqdata.runq_fnid[dep]]
taskname2 = self.rqdata.runq_task[dep]
stampfile2 = bb.build.stampfile(taskname2, self.rqdata.dataCache, fn2)
if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist):
if dep in notcurrent:
iscurrent = False
else:
t2 = os.stat(stampfile2)[stat.ST_MTIME]
if t1 < t2:
iscurrent = False
del unchecked[task]
if iscurrent:
current.append(task)
else:
notcurrent.append(task)
check_buildable(self, task, nextbuildable)
buildable = nextbuildable
#for task in range(len(self.runq_fnid)):
# fn = self.taskData.fn_index[self.runq_fnid[task]]
# taskname = self.runq_task[task]
# print "%s %s.%s" % (task, taskname, fn)
#print "Unchecked: %s" % unchecked
#print "Current: %s" % current
#print "Not current: %s" % notcurrent
if len(unchecked) > 0:
bb.msg.fatal("RunQueue", "check_stamps fatal internal error")
return current
def check_stamp_task(self, task, taskname = None, recurse = False):
def get_timestamp(f):
try:
if not os.access(f, os.F_OK):
@@ -830,9 +911,6 @@ class RunQueue:
if taskname != "do_setscene" and taskname.endswith("_setscene"):
return True
if cache is None:
cache = {}
iscurrent = True
t1 = get_timestamp(stampfile)
for dep in self.rqdata.runq_depends[task]:
@@ -853,18 +931,10 @@ class RunQueue:
logger.debug(2, 'Stampfile %s < %s', stampfile, stampfile2)
iscurrent = False
if recurse and iscurrent:
if dep in cache:
iscurrent = cache[dep]
if not iscurrent:
logger.debug(2, 'Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
else:
iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
cache[dep] = iscurrent
if recurse:
cache[task] = iscurrent
iscurrent = self.check_stamp_task(dep, recurse=True)
return iscurrent
def _execute_runqueue(self):
def execute_runqueue(self):
"""
Run the tasks in a queue prepared by rqdata.prepare()
Upon failure, optionally try to recover the build using any alternate providers
@@ -928,19 +998,6 @@ class RunQueue:
# Loop
return retval
def execute_runqueue(self):
# Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
try:
return self._execute_runqueue()
except bb.runqueue.TaskFailure:
raise
except SystemExit:
raise
except:
logger.error("An uncaught exception occured in runqueue, please see the failure below:")
self.state = runQueueComplete
raise
def finish_runqueue(self, now = False):
if not self.rqexe:
return
@@ -984,36 +1041,23 @@ class RunQueueExecute:
self.build_stamps = {}
self.failed_fnids = []
self.stampcache = {}
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):
result = os.waitpid(-1, os.WNOHANG)
if result[0] == 0 and result[1] == 0:
return None
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 pid in self.build_stamps:
del self.build_stamps[pid]
if status != 0:
self.task_fail(task, status)
task = self.build_pids[result[0]]
del self.build_pids[result[0]]
self.build_pipes[result[0]].close()
del self.build_pipes[result[0]]
# self.build_stamps[result[0]] may not exist when use shared work directory.
if result[0] in self.build_stamps.keys():
del self.build_stamps[result[0]]
if result[1] != 0:
self.task_fail(task, result[1]>>8)
else:
self.task_complete(task)
return True
@@ -1120,6 +1164,8 @@ class RunQueueExecute:
os.umask(umask)
self.cooker.configuration.data.setVar("BB_WORKERCONTEXT", "1")
self.cooker.configuration.data.setVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY", self)
self.cooker.configuration.data.setVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY2", fn)
bb.parse.siggen.set_taskdata(self.rqdata.hashes, self.rqdata.hash_deps)
ret = 0
try:
@@ -1162,26 +1208,6 @@ class RunQueueExecute:
return pid, pipein, pipeout
def check_dependencies(self, task, taskdeps, setscene = False):
if not self.rq.depvalidate:
return False
taskdata = {}
taskdeps.add(task)
for dep in taskdeps:
if setscene:
depid = self.rqdata.runq_setscene[dep]
else:
depid = dep
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[depid]]
pn = self.rqdata.dataCache.pkg_fn[fn]
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.configuration.data }
valid = bb.utils.better_eval(call, locs)
return valid
class RunQueueExecuteDummy(RunQueueExecute):
def __init__(self, rq):
self.rq = rq
@@ -1197,8 +1223,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
self.stats = RunQueueStats(len(self.rqdata.runq_fnid))
self.stampcache = {}
# Mark initial buildable tasks
for task in xrange(self.stats.total):
self.runq_running.append(0)
@@ -1207,7 +1231,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
self.runq_buildable.append(1)
else:
self.runq_buildable.append(0)
if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered) and task not in self.rq.scenequeue_notcovered:
if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered):
self.rq.scenequeue_covered.add(task)
found = True
@@ -1218,39 +1242,26 @@ class RunQueueExecuteTasks(RunQueueExecute):
continue
logger.debug(1, 'Considering %s (%s): %s' % (task, self.rqdata.get_user_idstring(task), str(self.rqdata.runq_revdeps[task])))
if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered) and task not in self.rq.scenequeue_notcovered:
found = True
self.rq.scenequeue_covered.add(task)
if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered):
ok = True
for revdep in self.rqdata.runq_revdeps[task]:
if self.rqdata.runq_fnid[task] != self.rqdata.runq_fnid[revdep]:
logger.debug(1, 'Found "bad" dep %s (%s) for %s (%s)' % (revdep, self.rqdata.get_user_idstring(revdep), task, self.rqdata.get_user_idstring(task)))
ok = False
break
if ok:
found = True
self.rq.scenequeue_covered.add(task)
logger.debug(1, 'Skip list (pre setsceneverify) %s', sorted(self.rq.scenequeue_covered))
# Allow the metadata to elect for setscene tasks to run anyway
covered_remove = set()
if self.rq.setsceneverify:
invalidtasks = []
for task in xrange(len(self.rqdata.runq_task)):
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
taskname = self.rqdata.runq_task[task]
taskdep = self.rqdata.dataCache.task_deps[fn]
if 'noexec' in taskdep and taskname in taskdep['noexec']:
continue
if self.rq.check_stamp_task(task, taskname + "_setscene", cache=self.stampcache):
logger.debug(2, 'Setscene stamp current for task %s(%s)', task, self.rqdata.get_user_idstring(task))
continue
if self.rq.check_stamp_task(task, taskname, recurse = True, cache=self.stampcache):
logger.debug(2, 'Normal stamp current for task %s(%s)', task, self.rqdata.get_user_idstring(task))
continue
invalidtasks.append(task)
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.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)
call = 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.configuration.data }
covered_remove = bb.utils.better_eval(call, locs)
for task in covered_remove:
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
@@ -1362,7 +1373,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
self.task_skip(task)
return True
if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
if self.rq.check_stamp_task(task, taskname):
logger.debug(2, "Stamp current task %s (%s)", task,
self.rqdata.get_user_idstring(task))
self.task_skip(task)
@@ -1421,7 +1432,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.scenequeue_covered = set()
self.scenequeue_notcovered = set()
self.scenequeue_notneeded = set()
# If we don't have any setscene functions, skip this step
if len(self.rqdata.runq_setscene) == 0:
@@ -1431,6 +1441,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.stats = RunQueueStats(len(self.rqdata.runq_setscene))
endpoints = {}
sq_revdeps = []
sq_revdeps_new = []
sq_revdeps_squash = []
@@ -1445,15 +1456,12 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.runq_complete.append(0)
self.runq_buildable.append(0)
# First process the chains up to the first setscene task.
endpoints = {}
for task in xrange(len(self.rqdata.runq_fnid)):
sq_revdeps.append(copy.copy(self.rqdata.runq_revdeps[task]))
sq_revdeps_new.append(set())
if (len(self.rqdata.runq_revdeps[task]) == 0) and task not in self.rqdata.runq_setscene:
endpoints[task] = set()
# Secondly process the chains between setscene tasks.
for task in self.rqdata.runq_setscene:
for dep in self.rqdata.runq_depends[task]:
if dep not in endpoints:
@@ -1469,8 +1477,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
if sq_revdeps_new[point]:
tasks |= sq_revdeps_new[point]
sq_revdeps_new[point] = set()
if point in self.rqdata.runq_setscene:
sq_revdeps_new[point] = tasks
for dep in self.rqdata.runq_depends[point]:
if point in sq_revdeps[dep]:
sq_revdeps[dep].remove(point)
@@ -1483,42 +1489,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
process_endpoints(endpoints)
# Build a list of setscene tasks which as "unskippable"
# These are direct endpoints referenced by the build
endpoints2 = {}
sq_revdeps2 = []
sq_revdeps_new2 = []
def process_endpoints2(endpoints):
newendpoints = {}
for point, task in endpoints.items():
tasks = set([point])
if task:
tasks |= task
if sq_revdeps_new2[point]:
tasks |= sq_revdeps_new2[point]
sq_revdeps_new2[point] = set()
if point in self.rqdata.runq_setscene:
sq_revdeps_new2[point] = tasks
for dep in self.rqdata.runq_depends[point]:
if point in sq_revdeps2[dep]:
sq_revdeps2[dep].remove(point)
if tasks:
sq_revdeps_new2[dep] |= tasks
if (len(sq_revdeps2[dep]) == 0 or len(sq_revdeps_new2[dep]) != 0) and dep not in self.rqdata.runq_setscene:
newendpoints[dep] = tasks
if len(newendpoints) != 0:
process_endpoints2(newendpoints)
for task in xrange(len(self.rqdata.runq_fnid)):
sq_revdeps2.append(copy.copy(self.rqdata.runq_revdeps[task]))
sq_revdeps_new2.append(set())
if (len(self.rqdata.runq_revdeps[task]) == 0) and task not in self.rqdata.runq_setscene:
endpoints2[task] = set()
process_endpoints2(endpoints2)
self.unskippable = []
for task in self.rqdata.runq_setscene:
if sq_revdeps_new2[task]:
self.unskippable.append(self.rqdata.runq_setscene.index(task))
for task in xrange(len(self.rqdata.runq_fnid)):
if task in self.rqdata.runq_setscene:
deps = set()
@@ -1544,7 +1514,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
dep = self.rqdata.taskData.fn_index[depdata]
taskid = self.rqdata.get_task_id(self.rqdata.taskData.getfn_id(dep), idependtask.replace("_setscene", ""))
if taskid is None:
bb.msg.fatal("RunQueue", "Task %s:%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))
bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (self.rqdata.taskData.tasks_name[realid], dep, idependtask))
sq_revdeps_squash[self.rqdata.runq_setscene.index(task)].add(self.rqdata.runq_setscene.index(taskid))
# Have to zero this to avoid circular dependencies
@@ -1587,18 +1557,12 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
bb.build.make_stamp(taskname + "_setscene", self.rqdata.dataCache, fn)
continue
if self.rq.check_stamp_task(realtask, taskname + "_setscene", cache=self.stampcache):
if self.rq.check_stamp_task(realtask, taskname + "_setscene"):
logger.debug(2, 'Setscene stamp current for task %s(%s)', task, self.rqdata.get_user_idstring(realtask))
stamppresent.append(task)
self.task_skip(task)
continue
if self.rq.check_stamp_task(realtask, taskname, recurse = True, cache=self.stampcache):
logger.debug(2, 'Normal stamp current for task %s(%s)', task, self.rqdata.get_user_idstring(realtask))
stamppresent.append(task)
self.task_skip(task)
continue
sq_fn.append(fn)
sq_hashfn.append(self.rqdata.dataCache.hashfn[fn])
sq_hash.append(self.rqdata.runq_hash[realtask])
@@ -1679,13 +1643,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
# Find the next setscene to run
for nexttask in xrange(self.stats.total):
if self.runq_buildable[nexttask] == 1 and self.runq_running[nexttask] != 1:
if nexttask in self.unskippable:
logger.debug(2, "Setscene task %s is unskippable" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))
if nexttask not in self.unskippable and len(self.sq_revdeps[nexttask]) > 0 and self.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sq_revdeps[nexttask], True):
logger.debug(2, "Skipping setscene for task %s" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))
self.task_skip(nexttask)
self.scenequeue_notneeded.add(nexttask)
return True
task = nexttask
break
if task is not None:
@@ -1693,7 +1650,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realtask]]
taskname = self.rqdata.runq_task[realtask] + "_setscene"
if self.rq.check_stamp_task(realtask, self.rqdata.runq_task[realtask], recurse = True, cache=self.stampcache):
if self.rq.check_stamp_task(realtask, self.rqdata.runq_task[realtask], recurse = True):
logger.debug(2, 'Stamp for underlying task %s(%s) is current, so skipping setscene variant',
task, self.rqdata.get_user_idstring(realtask))
self.task_failoutright(task)
@@ -1705,7 +1662,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.task_failoutright(task)
return True
if self.rq.check_stamp_task(realtask, taskname, cache=self.stampcache):
if self.rq.check_stamp_task(realtask, taskname):
logger.debug(2, 'Setscene stamp current task %s(%s), so skip it and its dependencies',
task, self.rqdata.get_user_idstring(realtask))
self.task_skip(task)
@@ -1736,9 +1693,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
self.rq.scenequeue_covered = set()
for task in oldcovered:
self.rq.scenequeue_covered.add(self.rqdata.runq_setscene[task])
self.rq.scenequeue_notcovered = set()
for task in self.scenequeue_notcovered:
self.rq.scenequeue_notcovered.add(self.rqdata.runq_setscene[task])
logger.debug(1, 'We can skip tasks %s', sorted(self.rq.scenequeue_covered))
@@ -1822,6 +1776,15 @@ class runQueueTaskCompleted(runQueueEvent):
Event notifing a task completed
"""
def check_stamp_fn(fn, taskname, d):
rqexe = d.getVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY")
fn = d.getVar("__RUNQUEUE_DO_NOT_USE_EXTERNALLY2")
fnid = rqexe.rqdata.taskData.getfn_id(fn)
taskid = rqexe.rqdata.get_task_id(fnid, taskname)
if taskid is not None:
return rqexe.rq.check_stamp_task(taskid)
return None
class runQueuePipe():
"""
Abstraction for a pipe between a worker thread and the server
@@ -1829,7 +1792,7 @@ class runQueuePipe():
def __init__(self, pipein, pipeout, d):
self.input = pipein
pipeout.close()
bb.utils.nonblockingfd(self.input)
fcntl.fcntl(self.input, fcntl.F_SETFL, fcntl.fcntl(self.input, fcntl.F_GETFL) | os.O_NONBLOCK)
self.queue = ""
self.d = d

View File

@@ -191,8 +191,8 @@ class BitBakeServer(object):
def saveConnectionDetails(self):
return
def detach(self):
return
def detach(self, cooker_logfile):
self.logfile = cooker_logfile
def establishConnection(self):
self.connection = BitBakeServerConnection(self)

View File

@@ -45,10 +45,10 @@ class ServerCommunicator():
while True:
# don't let the user ctrl-c while we're waiting for a response
try:
if self.connection.poll(20):
if self.connection.poll(.5):
return self.connection.recv()
else:
bb.fatal("Timeout while attempting to communicate with bitbake server")
return None
except KeyboardInterrupt:
pass
@@ -256,7 +256,7 @@ class BitBakeServer(object):
def saveConnectionDetails(self):
return
def detach(self):
def detach(self, cooker_logfile):
self.server.start()
return

View File

@@ -280,8 +280,8 @@ class BitBakeServer(object):
def saveConnectionDetails(self):
self.serverinfo = BitbakeServerInfo(self.server.host, self.server.port)
def detach(self):
daemonize.createDaemon(self.server.serve_forever, "bitbake-cookerdaemon.log")
def detach(self, cooker_logfile):
daemonize.createDaemon(self.server.serve_forever, cooker_logfile)
del self.cooker
del self.server

View File

@@ -2,7 +2,6 @@ import hashlib
import logging
import os
import re
import tempfile
import bb.data
logger = logging.getLogger('BitBake.SigGen')
@@ -48,16 +47,9 @@ class SignatureGenerator(object):
def stampfile(self, stampbase, file_name, taskname, extrainfo):
return ("%s.%s.%s" % (stampbase, taskname, extrainfo)).rstrip('.')
def stampcleanmask(self, stampbase, file_name, taskname, extrainfo):
return ("%s.%s.%s" % (stampbase, taskname, extrainfo)).rstrip('.')
def dump_sigtask(self, fn, task, stampbase, runtime):
return
def invalidate_task(self, task, d, fn):
bb.build.del_stamp(task, d, fn)
class SignatureGeneratorBasic(SignatureGenerator):
"""
"""
@@ -68,7 +60,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
self.taskhash = {}
self.taskdeps = {}
self.runtaskdeps = {}
self.file_checksum_values = {}
self.gendeps = {}
self.lookupcache = {}
self.pkgnameextract = re.compile("(?P<fn>.*)\..*")
@@ -116,10 +107,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
data = data + dep
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
@@ -161,20 +148,10 @@ class SignatureGeneratorBasic(SignatureGenerator):
return False
return True
def read_taint(self, fn, task, stampbase):
taint = None
try:
with open(stampbase + '.' + task + '.taint', 'r') as taintf:
taint = taintf.read()
except IOError:
pass
return taint
def get_taskhash(self, fn, task, deps, dataCache):
k = fn + "." + task
data = dataCache.basetaskhash[k]
self.runtaskdeps[k] = []
self.file_checksum_values[k] = {}
recipename = dataCache.pkg_fn[fn]
for dep in sorted(deps, key=clean_basepath):
depname = dataCache.pkg_fn[self.pkgnameextract.search(dep).group('fn')]
@@ -184,17 +161,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
bb.fatal("%s is not in taskhash, caller isn't calling in dependency order?", dep)
data = data + self.taskhash[dep]
self.runtaskdeps[k].append(dep)
if task in dataCache.file_checksums[fn]:
checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
for (f,cs) in checksums:
self.file_checksum_values[k][f] = cs
data = data + cs
taint = self.read_taint(fn, task, dataCache.stamp[fn])
if taint:
data = data + taint
h = hashlib.md5(data).hexdigest()
self.taskhash[k] = h
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
@@ -231,29 +197,12 @@ class SignatureGeneratorBasic(SignatureGenerator):
if runtime and k in self.taskhash:
data['runtaskdeps'] = self.runtaskdeps[k]
data['file_checksum_values'] = self.file_checksum_values[k]
data['runtaskhashes'] = {}
for dep in data['runtaskdeps']:
data['runtaskhashes'][dep] = self.taskhash[dep]
taint = self.read_taint(fn, task, stampbase)
if taint:
data['taint'] = taint
fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(sigfile), prefix="sigtask.")
try:
with os.fdopen(fd, "wb") as stream:
p = pickle.dump(data, stream, -1)
stream.flush()
os.fsync(fd)
os.chmod(tmpfile, 0664)
os.rename(tmpfile, sigfile)
except (OSError, IOError), err:
try:
os.unlink(tmpfile)
except OSError:
pass
raise err
p = pickle.Pickler(file(sigfile, "wb"), -1)
p.dump(data)
def dump_sigs(self, dataCache):
for fn in self.taskdeps:
@@ -269,27 +218,18 @@ class SignatureGeneratorBasic(SignatureGenerator):
class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
name = "basichash"
def stampfile(self, stampbase, fn, taskname, extrainfo, clean=False):
def stampfile(self, stampbase, fn, taskname, extrainfo):
if taskname != "do_setscene" and taskname.endswith("_setscene"):
k = fn + "." + taskname[:-9]
else:
k = fn + "." + taskname
if clean:
h = "*"
elif k in self.taskhash:
if k in self.taskhash:
h = self.taskhash[k]
else:
# If k is not in basehash, then error
h = self.basehash[k]
return ("%s.%s.%s.%s" % (stampbase, taskname, h, extrainfo)).rstrip('.')
def stampcleanmask(self, stampbase, fn, taskname, extrainfo):
return self.stampfile(stampbase, fn, taskname, extrainfo, clean=True)
def invalidate_task(self, task, d, fn):
bb.note("Tainting hash to force rebuild of task %s, %s" % (fn, task))
bb.build.write_taint(task, d, fn)
def dump_this_task(outfile, d):
import bb.parse
fn = d.getVar("BB_FILENAME", True)
@@ -298,7 +238,7 @@ def dump_this_task(outfile, d):
def clean_basepath(a):
if a.startswith("virtual:"):
b = a.rsplit(":", 1)[0] + ":" + a.rsplit("/", 1)[1]
b = a.rsplit(":", 1)[0] + a.rsplit("/", 1)[1]
else:
b = a.rsplit("/", 1)[1]
return b
@@ -309,12 +249,10 @@ def clean_basepaths(a):
b[clean_basepath(x)] = a[x]
return b
def compare_sigfiles(a, b, recursecb = None):
output = []
p1 = pickle.Unpickler(open(a, "rb"))
def compare_sigfiles(a, b):
p1 = pickle.Unpickler(file(a, "rb"))
a_data = p1.load()
p2 = pickle.Unpickler(open(b, "rb"))
p2 = pickle.Unpickler(file(b, "rb"))
b_data = p2.load()
def dict_diff(a, b, whitelist=set()):
@@ -330,123 +268,89 @@ 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']))
print "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']))
print "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']))
print "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']))
print "changed items: %s" % a_data['taskwhitelist'].symmetric_difference(b_data['taskwhitelist'])
if a_data['taskdeps'] != b_data['taskdeps']:
output.append("Task dependencies changed from:\n%s\nto:\n%s" % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
print "Task dependencies changed from:\n%s\nto:\n%s" % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps']))
if a_data['basehash'] != b_data['basehash']:
output.append("basehash changed from %s to %s" % (a_data['basehash'], b_data['basehash']))
print "basehash changed from %s to %s" % (a_data['basehash'], b_data['basehash'])
changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'], 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]))
print "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]))
print "changed items: %s" % a_data['gendeps'][dep].symmetric_difference(b_data['gendeps'][dep])
if added:
for dep in added:
output.append("Dependency on variable %s was added" % (dep))
print "Dependency on variable %s was added" % (dep)
if removed:
for dep in removed:
output.append("Dependency on Variable %s was removed" % (dep))
print "Dependency on Variable %s was removed" % (dep)
changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
if changed:
for dep in changed:
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:
for f in changed:
output.append("Checksum for file %s changed from %s to %s" % (f, a_data['file_checksum_values'][f], b_data['file_checksum_values'][f]))
if added:
for f in added:
output.append("Dependency on checksum of file %s was added" % (f))
if removed:
for f in removed:
output.append("Dependency on checksum of file %s was removed" % (f))
print "Variable %s value changed from %s to %s" % (dep, a_data['varvals'][dep], b_data['varvals'][dep])
if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data:
a = a_data['runtaskhashes']
b = b_data['runtaskhashes']
a = clean_basepaths(a_data['runtaskhashes'])
b = clean_basepaths(b_data['runtaskhashes'])
changed, added, removed = dict_diff(a, b)
if added:
for dep in added:
bdep_found = False
if removed:
for bdep in removed:
if a[dep] == b[bdep]:
#output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
bdep_found = True
if not bdep_found:
output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), a[dep]))
bdep_found = False
if removed:
for bdep in removed:
if a[dep] == b[bdep]:
#print "Dependency on task %s was replaced by %s with same hash" % (dep, bdep)
bdep_found = True
if not bdep_found:
print "Dependency on task %s was added with hash %s" % (dep, a[dep])
if removed:
for dep in removed:
adep_found = False
if added:
for adep in added:
if a[adep] == b[dep]:
#output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
adep_found = True
if not adep_found:
output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), b[dep]))
adep_found = False
if added:
for adep in added:
if a[adep] == b[dep]:
#print "Dependency on task %s was replaced by %s with same hash" % (adep, dep)
adep_found = True
if not adep_found:
print "Dependency on task %s was removed with hash %s" % (dep, b[dep])
if changed:
for dep in changed:
output.append("Hash for dependent task %s changed from %s to %s" % (clean_basepath(dep), a[dep], b[dep]))
if callable(recursecb):
recout = recursecb(dep, a[dep], b[dep])
if recout:
output.extend(recout)
a_taint = a_data.get('taint', None)
b_taint = b_data.get('taint', None)
if a_taint != b_taint:
output.append("Taint (by forced/invalidated task) changed from %s to %s" % (a_taint, b_taint))
return output
print "Hash for dependent task %s changed from %s to %s" % (dep, a[dep], b[dep])
def dump_sigfile(a):
output = []
p1 = pickle.Unpickler(open(a, "rb"))
p1 = pickle.Unpickler(file(a, "rb"))
a_data = p1.load()
output.append("basewhitelist: %s" % (a_data['basewhitelist']))
print "basewhitelist: %s" % (a_data['basewhitelist'])
output.append("taskwhitelist: %s" % (a_data['taskwhitelist']))
print "taskwhitelist: %s" % (a_data['taskwhitelist'])
output.append("Task dependencies: %s" % (sorted(a_data['taskdeps'])))
print "Task dependencies: %s" % (sorted(a_data['taskdeps']))
output.append("basehash: %s" % (a_data['basehash']))
print "basehash: %s" % (a_data['basehash'])
for dep in a_data['gendeps']:
output.append("List of dependencies for variable %s is %s" % (dep, a_data['gendeps'][dep]))
print "List of dependencies for variable %s is %s" % (dep, a_data['gendeps'][dep])
for dep in a_data['varvals']:
output.append("Variable %s value is %s" % (dep, a_data['varvals'][dep]))
print "Variable %s value is %s" % (dep, a_data['varvals'][dep])
if 'runtaskdeps' in a_data:
output.append("Tasks this task depends on: %s" % (a_data['runtaskdeps']))
if 'file_checksum_values' in a_data:
output.append("This task depends on the checksums of files: %s" % (a_data['file_checksum_values']))
print "Tasks this task depends on: %s" % (a_data['runtaskdeps'])
if 'runtaskhashes' in a_data:
for dep in a_data['runtaskhashes']:
output.append("Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep]))
if 'taint' in a_data:
output.append("Tainted (by forced/invalidated task): %s" % a_data['taint'])
return output
print "Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep])

View File

@@ -55,7 +55,6 @@ class TaskData:
self.tasks_name = []
self.tasks_tdepends = []
self.tasks_idepends = []
self.tasks_irdepends = []
# Cache to speed up task ID lookups
self.tasks_lookup = {}
@@ -116,16 +115,6 @@ class TaskData:
ids.append(self.tasks_lookup[fnid][task])
return ids
def gettask_id_fromfnid(self, fnid, task):
"""
Return an ID number for the task matching fnid and task.
"""
if fnid in self.tasks_lookup:
if task in self.tasks_lookup[fnid]:
return self.tasks_lookup[fnid][task]
return None
def gettask_id(self, fn, task, create = True):
"""
Return an ID number for the task matching fn and task.
@@ -145,7 +134,6 @@ class TaskData:
self.tasks_fnid.append(fnid)
self.tasks_tdepends.append([])
self.tasks_idepends.append([])
self.tasks_irdepends.append([])
listid = len(self.tasks_name) - 1
@@ -176,9 +164,6 @@ class TaskData:
# Work out task dependencies
parentids = []
for dep in task_deps['parents'][task]:
if dep not in task_deps['tasks']:
bb.debug(2, "Not adding dependeny of %s on %s since %s does not exist" % (task, dep, dep))
continue
parentid = self.gettask_id(fn, dep)
parentids.append(parentid)
taskid = self.gettask_id(fn, task)
@@ -193,15 +178,6 @@ class TaskData:
bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (fn, dep))
ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
self.tasks_idepends[taskid].extend(ids)
if 'rdepends' in task_deps and task in task_deps['rdepends']:
ids = []
for dep in task_deps['rdepends'][task].split():
if dep:
if ":" not in dep:
bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'rdepends' should be specified in the form 'packagename:task'" % (fn, dep))
ids.append(((self.getrun_id(dep.split(":")[0])), dep.split(":")[1]))
self.tasks_irdepends[taskid].extend(ids)
# Work out build dependencies
if not fnid in self.depids:
@@ -485,7 +461,6 @@ class TaskData:
providers_list.append(dataCache.pkg_fn[fn])
bb.event.fire(bb.event.MultipleProviders(item, providers_list, runtime=True), cfgData)
self.consider_msgs_cache.append(item)
raise bb.providers.MultipleRProvider(item)
# run through the list until we find one that we can build
for fn in eligible:
@@ -558,11 +533,6 @@ class TaskData:
dependees = self.get_rdependees(targetid)
for fnid in dependees:
self.fail_fnid(fnid, missing_list)
for taskid in xrange(len(self.tasks_irdepends)):
irdepends = self.tasks_irdepends[taskid]
for (idependid, idependtask) in irdepends:
if idependid == targetid:
self.fail_fnid(self.tasks_fnid[taskid], missing_list)
def add_unresolved(self, cfgData, dataCache):
"""
@@ -584,7 +554,7 @@ class TaskData:
try:
self.add_rprovider(cfgData, dataCache, target)
added = added + 1
except (bb.providers.NoRProvider, bb.providers.MultipleRProvider):
except bb.providers.NoRProvider:
self.remove_runtarget(self.getrun_id(target))
logger.debug(1, "Resolved " + str(added) + " extra dependencies")
if added == 0:

View File

@@ -1,369 +0,0 @@
#
# BitBake Test for codeparser.py
#
# Copyright (C) 2010 Chris Larson
# Copyright (C) 2012 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 unittest
import logging
import bb
logger = logging.getLogger('BitBake.TestCodeParser')
import bb.data
class ReferenceTest(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
def setEmptyVars(self, varlist):
for k in varlist:
self.d.setVar(k, "")
def setValues(self, values):
for k, v in values.items():
self.d.setVar(k, v)
def assertReferences(self, refs):
self.assertEqual(self.references, refs)
def assertExecs(self, execs):
self.assertEqual(self.execs, execs)
class VariableReferenceTest(ReferenceTest):
def parseExpression(self, exp):
parsedvar = self.d.expandWithRefs(exp, None)
self.references = parsedvar.references
def test_simple_reference(self):
self.setEmptyVars(["FOO"])
self.parseExpression("${FOO}")
self.assertReferences(set(["FOO"]))
def test_nested_reference(self):
self.setEmptyVars(["BAR"])
self.d.setVar("FOO", "BAR")
self.parseExpression("${${FOO}}")
self.assertReferences(set(["FOO", "BAR"]))
def test_python_reference(self):
self.setEmptyVars(["BAR"])
self.parseExpression("${@bb.data.getVar('BAR', d, True) + 'foo'}")
self.assertReferences(set(["BAR"]))
class ShellReferenceTest(ReferenceTest):
def parseExpression(self, exp):
parsedvar = self.d.expandWithRefs(exp, None)
parser = bb.codeparser.ShellParser("ParserTest", logger)
parser.parse_shell(parsedvar.value)
self.references = parsedvar.references
self.execs = parser.execs
def test_quotes_inside_assign(self):
self.parseExpression('foo=foo"bar"baz')
self.assertReferences(set([]))
def test_quotes_inside_arg(self):
self.parseExpression('sed s#"bar baz"#"alpha beta"#g')
self.assertExecs(set(["sed"]))
def test_arg_continuation(self):
self.parseExpression("sed -i -e s,foo,bar,g \\\n *.pc")
self.assertExecs(set(["sed"]))
def test_dollar_in_quoted(self):
self.parseExpression('sed -i -e "foo$" *.pc')
self.assertExecs(set(["sed"]))
def test_quotes_inside_arg_continuation(self):
self.setEmptyVars(["bindir", "D", "libdir"])
self.parseExpression("""
sed -i -e s#"moc_location=.*$"#"moc_location=${bindir}/moc4"# \\
-e s#"uic_location=.*$"#"uic_location=${bindir}/uic4"# \\
${D}${libdir}/pkgconfig/*.pc
""")
self.assertReferences(set(["bindir", "D", "libdir"]))
def test_assign_subshell_expansion(self):
self.parseExpression("foo=$(echo bar)")
self.assertExecs(set(["echo"]))
def test_shell_unexpanded(self):
self.setEmptyVars(["QT_BASE_NAME"])
self.parseExpression('echo "${QT_BASE_NAME}"')
self.assertExecs(set(["echo"]))
self.assertReferences(set(["QT_BASE_NAME"]))
def test_incomplete_varexp_single_quotes(self):
self.parseExpression("sed -i -e 's:IP{:I${:g' $pc")
self.assertExecs(set(["sed"]))
def test_until(self):
self.parseExpression("until false; do echo true; done")
self.assertExecs(set(["false", "echo"]))
self.assertReferences(set())
def test_case(self):
self.parseExpression("""
case $foo in
*)
bar
;;
esac
""")
self.assertExecs(set(["bar"]))
self.assertReferences(set())
def test_assign_exec(self):
self.parseExpression("a=b c='foo bar' alpha 1 2 3")
self.assertExecs(set(["alpha"]))
def test_redirect_to_file(self):
self.setEmptyVars(["foo"])
self.parseExpression("echo foo >${foo}/bar")
self.assertExecs(set(["echo"]))
self.assertReferences(set(["foo"]))
def test_heredoc(self):
self.setEmptyVars(["theta"])
self.parseExpression("""
cat <<END
alpha
beta
${theta}
END
""")
self.assertReferences(set(["theta"]))
def test_redirect_from_heredoc(self):
v = ["B", "SHADOW_MAILDIR", "SHADOW_MAILFILE", "SHADOW_UTMPDIR", "SHADOW_LOGDIR", "bindir"]
self.setEmptyVars(v)
self.parseExpression("""
cat <<END >${B}/cachedpaths
shadow_cv_maildir=${SHADOW_MAILDIR}
shadow_cv_mailfile=${SHADOW_MAILFILE}
shadow_cv_utmpdir=${SHADOW_UTMPDIR}
shadow_cv_logdir=${SHADOW_LOGDIR}
shadow_cv_passwd_dir=${bindir}
END
""")
self.assertReferences(set(v))
self.assertExecs(set(["cat"]))
# def test_incomplete_command_expansion(self):
# self.assertRaises(reftracker.ShellSyntaxError, reftracker.execs,
# bbvalue.shparse("cp foo`", self.d), self.d)
# def test_rogue_dollarsign(self):
# self.setValues({"D" : "/tmp"})
# self.parseExpression("install -d ${D}$")
# self.assertReferences(set(["D"]))
# self.assertExecs(set(["install"]))
class PythonReferenceTest(ReferenceTest):
def setUp(self):
self.d = bb.data.init()
if hasattr(bb.utils, "_context"):
self.context = bb.utils._context
else:
import __builtin__
self.context = __builtin__.__dict__
def parseExpression(self, exp):
parsedvar = self.d.expandWithRefs(exp, None)
parser = bb.codeparser.PythonParser("ParserTest", logger)
parser.parse_python(parsedvar.value)
self.references = parsedvar.references | parser.references
self.execs = parser.execs
@staticmethod
def indent(value):
"""Python Snippets have to be indented, python values don't have to
be. These unit tests are testing snippets."""
return " " + value
def test_getvar_reference(self):
self.parseExpression("bb.data.getVar('foo', d, True)")
self.assertReferences(set(["foo"]))
self.assertExecs(set())
def test_getvar_computed_reference(self):
self.parseExpression("bb.data.getVar('f' + 'o' + 'o', d, True)")
self.assertReferences(set())
self.assertExecs(set())
def test_getvar_exec_reference(self):
self.parseExpression("eval('bb.data.getVar(\"foo\", d, True)')")
self.assertReferences(set())
self.assertExecs(set(["eval"]))
def test_var_reference(self):
self.context["foo"] = lambda x: x
self.setEmptyVars(["FOO"])
self.parseExpression("foo('${FOO}')")
self.assertReferences(set(["FOO"]))
self.assertExecs(set(["foo"]))
del self.context["foo"]
def test_var_exec(self):
for etype in ("func", "task"):
self.d.setVar("do_something", "echo 'hi mom! ${FOO}'")
self.d.setVarFlag("do_something", etype, True)
self.parseExpression("bb.build.exec_func('do_something', d)")
self.assertReferences(set(["do_something"]))
def test_function_reference(self):
self.context["testfunc"] = lambda msg: bb.msg.note(1, None, msg)
self.d.setVar("FOO", "Hello, World!")
self.parseExpression("testfunc('${FOO}')")
self.assertReferences(set(["FOO"]))
self.assertExecs(set(["testfunc"]))
del self.context["testfunc"]
def test_qualified_function_reference(self):
self.parseExpression("time.time()")
self.assertExecs(set(["time.time"]))
def test_qualified_function_reference_2(self):
self.parseExpression("os.path.dirname('/foo/bar')")
self.assertExecs(set(["os.path.dirname"]))
def test_qualified_function_reference_nested(self):
self.parseExpression("time.strftime('%Y%m%d',time.gmtime())")
self.assertExecs(set(["time.strftime", "time.gmtime"]))
def test_function_reference_chained(self):
self.context["testget"] = lambda: "\tstrip me "
self.parseExpression("testget().strip()")
self.assertExecs(set(["testget"]))
del self.context["testget"]
class DependencyReferenceTest(ReferenceTest):
pydata = """
bb.data.getVar('somevar', d, True)
def test(d):
foo = 'bar %s' % 'foo'
def test2(d):
d.getVar(foo, True)
d.getVar('bar', False)
test2(d)
def a():
\"\"\"some
stuff
\"\"\"
return "heh"
test(d)
bb.data.expand(bb.data.getVar("something", False, d), d)
bb.data.expand("${inexpand} somethingelse", d)
bb.data.getVar(a(), d, False)
"""
def test_python(self):
self.d.setVar("FOO", self.pydata)
self.setEmptyVars(["inexpand", "a", "test2", "test"])
self.d.setVarFlags("FOO", {"func": True, "python": True})
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
self.assertEquals(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"]))
shelldata = """
foo () {
bar
}
{
echo baz
$(heh)
eval `moo`
}
a=b
c=d
(
true && false
test -f foo
testval=something
$testval
) || aiee
! inverted
echo ${somevar}
case foo in
bar)
echo bar
;;
baz)
echo baz
;;
foo*)
echo foo
;;
esac
"""
def test_shell(self):
execs = ["bar", "echo", "heh", "moo", "true", "aiee"]
self.d.setVar("somevar", "heh")
self.d.setVar("inverted", "echo inverted...")
self.d.setVarFlag("inverted", "func", True)
self.d.setVar("FOO", self.shelldata)
self.d.setVarFlags("FOO", {"func": True})
self.setEmptyVars(execs)
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
self.assertEquals(deps, set(["somevar", "inverted"] + execs))
def test_vardeps(self):
self.d.setVar("oe_libinstall", "echo test")
self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
self.d.setVarFlag("FOO", "vardeps", "oe_libinstall")
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
self.assertEquals(deps, set(["oe_libinstall"]))
def test_vardeps_expand(self):
self.d.setVar("oe_libinstall", "echo test")
self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
self.d.setVarFlag("FOO", "vardeps", "${@'oe_libinstall'}")
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
self.assertEquals(deps, set(["oe_libinstall"]))
#Currently no wildcard support
#def test_vardeps_wildcards(self):
# self.d.setVar("oe_libinstall", "echo test")
# self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
# self.d.setVarFlag("FOO", "vardeps", "oe_*")
# self.assertEquals(deps, set(["oe_libinstall"]))

View File

@@ -1,134 +0,0 @@
#
# BitBake Tests for Copy-on-Write (cow.py)
#
# Copyright 2006 Holger Freyther <freyther@handhelds.org>
#
# 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 unittest
import os
class COWTestCase(unittest.TestCase):
"""
Test case for the COW module from mithro
"""
def testGetSet(self):
"""
Test and set
"""
from bb.COW import COWDictBase
a = COWDictBase.copy()
self.assertEquals(False, a.has_key('a'))
a['a'] = 'a'
a['b'] = 'b'
self.assertEquals(True, a.has_key('a'))
self.assertEquals(True, a.has_key('b'))
self.assertEquals('a', a['a'] )
self.assertEquals('b', a['b'] )
def testCopyCopy(self):
"""
Test the copy of copies
"""
from bb.COW import COWDictBase
# create two COW dict 'instances'
b = COWDictBase.copy()
c = COWDictBase.copy()
# assign some keys to one instance, some keys to another
b['a'] = 10
b['c'] = 20
c['a'] = 30
# test separation of the two instances
self.assertEquals(False, c.has_key('c'))
self.assertEquals(30, c['a'])
self.assertEquals(10, b['a'])
# test copy
b_2 = b.copy()
c_2 = c.copy()
self.assertEquals(False, c_2.has_key('c'))
self.assertEquals(10, b_2['a'])
b_2['d'] = 40
self.assertEquals(False, c_2.has_key('d'))
self.assertEquals(True, b_2.has_key('d'))
self.assertEquals(40, b_2['d'])
self.assertEquals(False, b.has_key('d'))
self.assertEquals(False, c.has_key('d'))
c_2['d'] = 30
self.assertEquals(True, c_2.has_key('d'))
self.assertEquals(True, b_2.has_key('d'))
self.assertEquals(30, c_2['d'])
self.assertEquals(40, b_2['d'])
self.assertEquals(False, b.has_key('d'))
self.assertEquals(False, c.has_key('d'))
# test copy of the copy
c_3 = c_2.copy()
b_3 = b_2.copy()
b_3_2 = b_2.copy()
c_3['e'] = 4711
self.assertEquals(4711, c_3['e'])
self.assertEquals(False, c_2.has_key('e'))
self.assertEquals(False, b_3.has_key('e'))
self.assertEquals(False, b_3_2.has_key('e'))
self.assertEquals(False, b_2.has_key('e'))
b_3['e'] = 'viel'
self.assertEquals('viel', b_3['e'])
self.assertEquals(4711, c_3['e'])
self.assertEquals(False, c_2.has_key('e'))
self.assertEquals(True, b_3.has_key('e'))
self.assertEquals(False, b_3_2.has_key('e'))
self.assertEquals(False, b_2.has_key('e'))
def testCow(self):
from bb.COW import COWDictBase
c = COWDictBase.copy()
c['123'] = 1027
c['other'] = 4711
c['d'] = { 'abc' : 10, 'bcd' : 20 }
copy = c.copy()
self.assertEquals(1027, c['123'])
self.assertEquals(4711, c['other'])
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
self.assertEquals(1027, copy['123'])
self.assertEquals(4711, copy['other'])
self.assertEquals({'abc':10, 'bcd':20}, copy['d'])
# cow it now
copy['123'] = 1028
copy['other'] = 4712
copy['d']['abc'] = 20
self.assertEquals(1027, c['123'])
self.assertEquals(4711, c['other'])
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
self.assertEquals(1028, copy['123'])
self.assertEquals(4712, copy['other'])
self.assertEquals({'abc':20, 'bcd':20}, copy['d'])

View File

@@ -1,252 +0,0 @@
#
# BitBake Tests for the Data Store (data.py/data_smart.py)
#
# Copyright (C) 2010 Chris Larson
# Copyright (C) 2012 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 unittest
import bb
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'"
def test_one_var(self):
val = self.d.expand("${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'")
def test_indirect_and_another(self):
val = self.d.expand("${${foo}} ${bar}")
self.assertEqual(str(val), "value of 'value of foo' value of bar")
def test_python_snippet(self):
val = self.d.expand("${@5*12}")
self.assertEqual(str(val), "60")
def test_expand_in_python_snippet(self):
val = self.d.expand("${@'boo ' + '${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")
def test_python_snippet_syntax_error(self):
self.d.setVar("FOO", "${@foo = 5}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_python_snippet_runtime_error(self):
self.d.setVar("FOO", "${@int('test')}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_python_snippet_error_path(self):
self.d.setVar("FOO", "foo value ${BAR}")
self.d.setVar("BAR", "bar value ${@int('test')}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
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")
def test_reference_undefined_var(self):
val = self.d.expand("${undefinedvar} meh")
self.assertEqual(str(val), "${undefinedvar} meh")
def test_double_reference(self):
self.d.setVar("BAR", "bar value")
self.d.setVar("FOO", "${BAR} foo ${BAR}")
val = self.d.getVar("FOO", True)
self.assertEqual(str(val), "bar value foo bar value")
def test_direct_recursion(self):
self.d.setVar("FOO", "${FOO}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_indirect_recursion(self):
self.d.setVar("FOO", "${BAR}")
self.d.setVar("BAR", "${BAZ}")
self.d.setVar("BAZ", "${FOO}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_recursion_exception(self):
self.d.setVar("FOO", "${BAR}")
self.d.setVar("BAR", "${${@'FOO'}}")
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_incomplete_varexp_single_quotes(self):
self.d.setVar("FOO", "sed -i -e 's:IP{:I${:g' $pc")
val = self.d.getVar("FOO", True)
self.assertEqual(str(val), "sed -i -e 's:IP{:I${:g' $pc")
def test_nonstring(self):
self.d.setVar("TEST", 5)
val = self.d.getVar("TEST", True)
self.assertEqual(str(val), "5")
def test_rename(self):
self.d.renameVar("foo", "newfoo")
self.assertEqual(self.d.getVar("newfoo"), "value of foo")
self.assertEqual(self.d.getVar("foo"), None)
def test_deletion(self):
self.d.delVar("foo")
self.assertEqual(self.d.getVar("foo"), None)
def test_keys(self):
keys = self.d.keys()
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"
def test_refs(self):
val = self.d.expand("${value of ${foo}${bar}}")
self.assertEqual(str(val), "187")
#def test_python_refs(self):
# val = self.d.expand("${@${@3}**2 + ${@4}**2}")
# self.assertEqual(str(val), "25")
def test_ref_in_python_ref(self):
val = self.d.expand("${@'${foo}' + 'bar'}")
self.assertEqual(str(val), "foobar")
def test_python_ref_in_ref(self):
val = self.d.expand("${${@'f'+'o'+'o'}}")
self.assertEqual(str(val), "foo")
def test_deep_nesting(self):
depth = 100
val = self.d.expand("${" * depth + "foo" + "}" * depth)
self.assertEqual(str(val), "foo")
#def test_deep_python_nesting(self):
# depth = 50
# val = self.d.expand("${@" * depth + "1" + "+1}" * depth)
# self.assertEqual(str(val), str(depth + 1))
def test_mixed(self):
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'}}")
self.assertEqual(str(val), "187")
class TestMemoize(unittest.TestCase):
def test_memoized(self):
d = bb.data.init()
d.setVar("FOO", "bar")
self.assertTrue(d.getVar("FOO") is d.getVar("FOO"))
def test_not_memoized(self):
d1 = bb.data.init()
d2 = bb.data.init()
d1.setVar("FOO", "bar")
d2.setVar("FOO", "bar2")
self.assertTrue(d1.getVar("FOO") is not d2.getVar("FOO"))
def test_changed_after_memoized(self):
d = bb.data.init()
d.setVar("foo", "value of foo")
self.assertEqual(str(d.getVar("foo")), "value of foo")
d.setVar("foo", "second value of foo")
self.assertEqual(str(d.getVar("foo")), "second value of foo")
def test_same_value(self):
d = bb.data.init()
d.setVar("foo", "value of")
d.setVar("bar", "value of")
self.assertEqual(d.getVar("foo"),
d.getVar("bar"))
class TestConcat(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d.setVar("FOO", "foo")
self.d.setVar("VAL", "val")
self.d.setVar("BAR", "bar")
def test_prepend(self):
self.d.setVar("TEST", "${VAL}")
self.d.prependVar("TEST", "${FOO}:")
self.assertEqual(self.d.getVar("TEST", True), "foo:val")
def test_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.appendVar("TEST", ":${BAR}")
self.assertEqual(self.d.getVar("TEST", True), "val:bar")
def test_multiple_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.prependVar("TEST", "${FOO}:")
self.d.appendVar("TEST", ":val2")
self.d.appendVar("TEST", ":${BAR}")
self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
class TestOverrides(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d.setVar("OVERRIDES", "foo:bar:local")
self.d.setVar("TEST", "testvalue")
def test_no_override(self):
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "testvalue")
def test_one_override(self):
self.d.setVar("TEST_bar", "testvalue2")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "testvalue2")
def test_multiple_override(self):
self.d.setVar("TEST_bar", "testvalue2")
self.d.setVar("TEST_local", "testvalue3")
self.d.setVar("TEST_foo", "testvalue4")
bb.data.update_data(self.d)
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
class TestFlags(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d.setVar("foo", "value of foo")
self.d.setVarFlag("foo", "flag1", "value of flag1")
self.d.setVarFlag("foo", "flag2", "value of flag2")
def test_setflag(self):
self.assertEqual(self.d.getVarFlag("foo", "flag1"), "value of flag1")
self.assertEqual(self.d.getVarFlag("foo", "flag2"), "value of flag2")
def test_delflag(self):
self.d.delVarFlag("foo", "flag2")
self.assertEqual(self.d.getVarFlag("foo", "flag1"), "value of flag1")
self.assertEqual(self.d.getVarFlag("foo", "flag2"), None)

View File

@@ -1,191 +0,0 @@
#
# BitBake Tests for the Fetcher (fetch2/)
#
# Copyright (C) 2012 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 unittest
import tempfile
import subprocess
import os
import bb
class FetcherTest(unittest.TestCase):
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",
("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http")
: "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http")
: "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/\\2;protocol=http")
: "git://somewhere.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890", "git://someserver.org/bitbake", "git://git.openembedded.org/bitbake")
: "git://git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890",
("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache")
: "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache/")
: "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org/somedir3")
: "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz",
("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz")
: "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz",
("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://www.apache.org/dist", "http://archive.apache.org/dist")
: "http://archive.apache.org/dist/subversion/subversion-1.7.1.tar.bz2",
("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://.*/.*", "file:///somepath/downloads/")
: "file:///somepath/downloads/subversion-1.7.1.tar.bz2",
("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http")
: "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http")
: "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/MIRRORNAME;protocol=http")
: "git://somewhere.org/somedir/git.invalid.infradead.org.foo.mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http",
#Renaming files doesn't work
#("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz") : "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz"
#("file://sstate-xyz.tgz", "file://.*/.*", "file:///somewhere/1234/sstate-cache") : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
}
mirrorvar = "http://.*/.* file:///somepath/downloads/ \n" \
"git://someserver.org/bitbake git://git.openembedded.org/bitbake \n" \
"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)
ud.setup_localpath(self.d)
mirrors = bb.fetch2.mirror_from_string("%s %s" % (k[1], k[2]))
newuris, uds = bb.fetch2.build_mirroruris(ud, mirrors, self.d)
self.assertEqual([v], newuris)
def test_urilist1(self):
fetcher = bb.fetch.FetchData("http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d)
mirrors = bb.fetch2.mirror_from_string(self.mirrorvar)
uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
self.assertEqual(uris, ['file:///somepath/downloads/bitbake-1.0.tar.gz', 'file:///someotherpath/downloads/bitbake-1.0.tar.gz'])
def test_urilist2(self):
# Catch https:// -> files:// bug
fetcher = bb.fetch.FetchData("https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d)
mirrors = bb.fetch2.mirror_from_string(self.mirrorvar)
uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
self.assertEqual(uris, ['file:///someotherpath/downloads/bitbake-1.0.tar.gz'])
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'})
}
def test_decodeurl(self):
for k, v in self.datatable.items():
result = bb.fetch.decodeurl(k)
self.assertEqual(result, v)
def test_encodeurl(self):
for k, v in self.datatable.items():
result = bb.fetch.encodeurl(v)
self.assertEqual(result, k)

View File

@@ -1,51 +0,0 @@
#
# BitBake Tests for utils.py
#
# Copyright (C) 2012 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 unittest
import bb
class VerCmpString(unittest.TestCase):
def test_vercmpstring(self):
result = bb.utils.vercmp_string('1', '2')
self.assertTrue(result < 0)
result = bb.utils.vercmp_string('2', '1')
self.assertTrue(result > 0)
result = bb.utils.vercmp_string('1', '1.0')
self.assertTrue(result < 0)
result = bb.utils.vercmp_string('1', '1.1')
self.assertTrue(result < 0)
result = bb.utils.vercmp_string('1.1', '1_p2')
self.assertTrue(result < 0)
def test_explode_dep_versions(self):
correctresult = {"foo" : ["= 1.10"]}
result = bb.utils.explode_dep_versions2("foo (= 1.10)")
self.assertEqual(result, correctresult)
result = bb.utils.explode_dep_versions2("foo (=1.10)")
self.assertEqual(result, correctresult)
result = bb.utils.explode_dep_versions2("foo ( = 1.10)")
self.assertEqual(result, correctresult)
result = bb.utils.explode_dep_versions2("foo ( =1.10)")
self.assertEqual(result, correctresult)
result = bb.utils.explode_dep_versions2("foo ( = 1.10 )")
self.assertEqual(result, correctresult)
result = bb.utils.explode_dep_versions2("foo ( =1.10 )")
self.assertEqual(result, correctresult)

View File

@@ -1,98 +0,0 @@
# tinfoil: a simple wrapper around cooker for bitbake-based command-line utilities
#
# Copyright (C) 2012 Intel Corporation
# Copyright (C) 2011 Mentor Graphics 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 logging
import warnings
import os
import sys
import bb.cache
import bb.cooker
import bb.providers
import bb.utils
from bb.cooker import state
import bb.fetch2
class Tinfoil:
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(sys.stdout)
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
bb.msg.addDefaultlogFilter(console)
console.setFormatter(format)
self.logger.addHandler(console)
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
def register_idle_function(self, function, data):
pass
def parseRecipes(self):
sys.stderr.write("Parsing recipes..")
self.logger.setLevel(logging.WARNING)
try:
while self.cooker.state in (state.initial, state.parsing):
self.cooker.updateCache()
except KeyboardInterrupt:
self.cooker.shutdown()
self.cooker.updateCache()
sys.exit(2)
self.logger.setLevel(logging.INFO)
sys.stderr.write("done.\n")
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.status
else:
self.parseRecipes()
class TinfoilConfig(object):
def __init__(self, **options):
self.pkgs_to_build = []
self.debug_domains = []
self.extra_assume_provided = []
self.prefile = []
self.postfile = []
self.debug = 0
self.__dict__.update(options)
def __getattr__(self, attribute):
try:
return super(TinfoilConfig, self).__getattribute__(attribute)
except AttributeError:
return None

View File

@@ -23,13 +23,11 @@
import gtk
import pango
import gobject
import bb.process
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.hobwidget import hic, HobNotebook, HobAltButton, HobWarpCellRendererText, HobButton, HobInfoButton
from bb.ui.crumbs.hobwidget import hic, HobNotebook, HobAltButton, HobWarpCellRendererText
from bb.ui.crumbs.runningbuild import RunningBuildTreeView
from bb.ui.crumbs.runningbuild import BuildFailureTreeView
from bb.ui.crumbs.hobpages import HobPage
from bb.ui.crumbs.hobcolor import HobColors
class BuildConfigurationTreeView(gtk.TreeView):
def __init__ (self):
@@ -98,12 +96,11 @@ class BuildConfigurationTreeView(gtk.TreeView):
for path in src_config_info.layers:
import os, os.path
if os.path.exists(path):
branch = bb.process.run('cd %s; git branch | grep "^* " | tr -d "* "' % path)[0]
if branch.startswith("fatal:"):
branch = "(unknown)"
if branch:
branch = branch.strip('\n')
f = os.popen('cd %s; git branch 2>&1 | grep "^* " | tr -d "* "' % path)
if f:
branch = f.readline().lstrip('\n').rstrip('\n')
vars.append(self.set_vars("Branch:", branch))
f.close()
break
self.set_config_model(vars)
@@ -147,7 +144,7 @@ class BuildDetailsPage (HobPage):
self.scrolled_view_config = gtk.ScrolledWindow ()
self.scrolled_view_config.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
self.scrolled_view_config.add(self.config_tv)
self.notebook.append_page(self.scrolled_view_config, "Build configuration")
self.notebook.append_page(self.scrolled_view_config, gtk.Label("Build configuration"))
self.failure_tv = BuildFailureTreeView()
self.failure_model = self.builder.handler.build.model.failure_model()
@@ -155,19 +152,19 @@ class BuildDetailsPage (HobPage):
self.scrolled_view_failure = gtk.ScrolledWindow ()
self.scrolled_view_failure.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
self.scrolled_view_failure.add(self.failure_tv)
self.notebook.append_page(self.scrolled_view_failure, "Issues")
self.notebook.append_page(self.scrolled_view_failure, gtk.Label("Issues"))
self.build_tv = RunningBuildTreeView(readonly=True, hob=True)
self.build_tv.set_model(self.builder.handler.build.model)
self.scrolled_view_build = gtk.ScrolledWindow ()
self.scrolled_view_build.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
self.scrolled_view_build.add(self.build_tv)
self.notebook.append_page(self.scrolled_view_build, "Log")
self.notebook.append_page(self.scrolled_view_build, gtk.Label("Log"))
self.builder.handler.build.model.connect_after("row-changed", self.scroll_to_present_row, self.scrolled_view_build.get_vadjustment(), self.build_tv)
self.button_box = gtk.HBox(False, 6)
self.back_button = HobAltButton('&lt;&lt; Back')
self.back_button = HobAltButton("<< Back to image configuration")
self.back_button.connect("clicked", self.back_button_clicked_cb)
self.button_box.pack_start(self.back_button, expand=False, fill=False)
@@ -201,155 +198,6 @@ class BuildDetailsPage (HobPage):
for child in children:
self.remove(child)
def add_build_fail_top_bar(self, actions, log_file=None):
primary_action = "Edit %s" % actions
color = HobColors.ERROR
build_fail_top = gtk.EventBox()
#build_fail_top.set_size_request(-1, 200)
build_fail_top.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
build_fail_tab = gtk.Table(14, 46, True)
build_fail_top.add(build_fail_tab)
icon = gtk.Image()
icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INDI_ERROR_FILE)
icon.set_from_pixbuf(icon_pix_buffer)
build_fail_tab.attach(icon, 1, 4, 0, 6)
label = gtk.Label()
label.set_alignment(0.0, 0.5)
label.set_markup("<span size='x-large'><b>%s</b></span>" % self.title)
build_fail_tab.attach(label, 4, 26, 0, 6)
label = gtk.Label()
label.set_alignment(0.0, 0.5)
# Ensure variable disk_full is defined
if not hasattr(self.builder, 'disk_full'):
self.builder.disk_full = False
if self.builder.disk_full:
markup = "<span size='medium'>There is no disk space left, so Hob cannot finish building your image. Free up some disk space\n"
markup += "and restart the build. Check the \"Issues\" tab for more details</span>"
label.set_markup(markup)
else:
label.set_markup("<span size='medium'>Check the \"Issues\" information for more details</span>")
build_fail_tab.attach(label, 4, 40, 4, 9)
# create button 'Edit packages'
action_button = HobButton(primary_action)
#action_button.set_size_request(-1, 40)
action_button.set_tooltip_text("Edit the %s parameters" % actions)
action_button.connect('clicked', self.failure_primary_action_button_clicked_cb, primary_action)
if log_file:
open_log_button = HobAltButton("Open log")
open_log_button.set_relief(gtk.RELIEF_HALF)
open_log_button.set_tooltip_text("Open the build's log file")
open_log_button.connect('clicked', self.open_log_button_clicked_cb, log_file)
attach_pos = (24 if log_file else 14)
file_bug_button = HobAltButton('File a bug')
file_bug_button.set_relief(gtk.RELIEF_HALF)
file_bug_button.set_tooltip_text("Open the Yocto Project bug tracking website")
file_bug_button.connect('clicked', self.failure_activate_file_bug_link_cb)
if not self.builder.disk_full:
build_fail_tab.attach(action_button, 4, 13, 9, 12)
if log_file:
build_fail_tab.attach(open_log_button, 14, 23, 9, 12)
build_fail_tab.attach(file_bug_button, attach_pos, attach_pos + 9, 9, 12)
else:
restart_build = HobButton("Restart the build")
restart_build.set_tooltip_text("Restart the build")
restart_build.connect('clicked', self.restart_build_button_clicked_cb)
build_fail_tab.attach(restart_build, 4, 13, 9, 12)
build_fail_tab.attach(action_button, 14, 23, 9, 12)
if log_file:
build_fail_tab.attach(open_log_button, attach_pos, attach_pos + 9, 9, 12)
self.builder.disk_full = False
return build_fail_top
def show_fail_page(self, title):
self._remove_all_widget()
self.title = "Hob cannot build your %s" % title
self.build_fail_bar = self.add_build_fail_top_bar(title, self.builder.current_logfile)
self.pack_start(self.group_align, expand=True, fill=True)
self.box_group_area.pack_start(self.build_fail_bar, expand=False, fill=False)
self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
self.vbox.pack_start(self.notebook, expand=True, fill=True)
self.show_all()
self.notebook.set_page("Issues")
self.back_button.hide()
def add_build_stop_top_bar(self, action, log_file=None):
color = HobColors.LIGHT_GRAY
build_stop_top = gtk.EventBox()
#build_stop_top.set_size_request(-1, 200)
build_stop_top.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
build_stop_top.set_flags(gtk.CAN_DEFAULT)
build_stop_top.grab_default()
build_stop_tab = gtk.Table(11, 46, True)
build_stop_top.add(build_stop_tab)
icon = gtk.Image()
icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INFO_HOVER_FILE)
icon.set_from_pixbuf(icon_pix_buffer)
build_stop_tab.attach(icon, 1, 4, 0, 6)
label = gtk.Label()
label.set_alignment(0.0, 0.5)
label.set_markup("<span size='x-large'><b>%s</b></span>" % self.title)
build_stop_tab.attach(label, 4, 26, 0, 6)
action_button = HobButton("Edit %s" % action)
action_button.set_size_request(-1, 40)
if action == "image":
action_button.set_tooltip_text("Edit the image parameters")
elif action == "recipes":
action_button.set_tooltip_text("Edit the included recipes")
elif action == "packages":
action_button.set_tooltip_text("Edit the included packages")
action_button.connect('clicked', self.stop_primary_action_button_clicked_cb, action)
build_stop_tab.attach(action_button, 4, 13, 6, 9)
if log_file:
open_log_button = HobAltButton("Open log")
open_log_button.set_relief(gtk.RELIEF_HALF)
open_log_button.set_tooltip_text("Open the build's log file")
open_log_button.connect('clicked', self.open_log_button_clicked_cb, log_file)
build_stop_tab.attach(open_log_button, 14, 23, 6, 9)
attach_pos = (24 if log_file else 14)
build_button = HobAltButton("Build new image")
#build_button.set_size_request(-1, 40)
build_button.set_tooltip_text("Create a new image from scratch")
build_button.connect('clicked', self.new_image_button_clicked_cb)
build_stop_tab.attach(build_button, attach_pos, attach_pos + 9, 6, 9)
return build_stop_top, action_button
def show_stop_page(self, action):
self._remove_all_widget()
self.title = "Build stopped"
self.build_stop_bar, action_button = self.add_build_stop_top_bar(action, self.builder.current_logfile)
self.pack_start(self.group_align, expand=True, fill=True)
self.box_group_area.pack_start(self.build_stop_bar, expand=False, fill=False)
self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
self.vbox.pack_start(self.notebook, expand=True, fill=True)
self.show_all()
self.back_button.hide()
return action_button
def show_page(self, step):
self._remove_all_widget()
if step == self.builder.PACKAGE_GENERATING or step == self.builder.FAST_IMAGE_GENERATING:
@@ -370,7 +218,6 @@ class BuildDetailsPage (HobPage):
self.box_group_area.pack_end(self.button_box, expand=False, fill=False)
self.show_all()
self.notebook.set_page("Log")
self.back_button.hide()
self.reset_build_status()
@@ -384,9 +231,6 @@ class BuildDetailsPage (HobPage):
def back_button_clicked_cb(self, button):
self.builder.show_configuration()
def new_image_button_clicked_cb(self, button):
self.builder.reset()
def show_back_button(self):
self.back_button.show()
@@ -407,30 +251,3 @@ class BuildDetailsPage (HobPage):
def show_configurations(self, configurations, params):
self.config_tv.show(configurations, params)
def failure_primary_action_button_clicked_cb(self, button, action):
if "Edit recipes" in action:
self.builder.show_recipes()
elif "Edit packages" in action:
self.builder.show_packages()
elif "Edit image" in action:
self.builder.show_configuration()
def restart_build_button_clicked_cb(self, button):
self.builder.just_bake()
def stop_primary_action_button_clicked_cb(self, button, action):
if "recipes" in action:
self.builder.show_recipes()
elif "packages" in action:
self.builder.show_packages(ask=False)
elif "image" in action:
self.builder.show_configuration()
def open_log_button_clicked_cb(self, button, log_file):
if log_file:
log_file = "file:///" + log_file
gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
def failure_activate_file_bug_link_cb(self, button):
button.child.emit('activate-link', "http://bugzilla.yoctoproject.org")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,336 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
import hashlib
from bb.ui.crumbs.hobwidget import HobInfoButton, HobButton
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
from bb.ui.crumbs.hig.proxydetailsdialog import ProxyDetailsDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
def details_cb(self, button, parent, protocol):
dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
user = self.configuration.proxies[protocol][1],
passwd = self.configuration.proxies[protocol][2],
parent = parent,
flags = gtk.DIALOG_MODAL
| gtk.DIALOG_DESTROY_WITH_PARENT
| gtk.DIALOG_NO_SEPARATOR)
dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.configuration.proxies[protocol][1] = dialog.user
self.configuration.proxies[protocol][2] = dialog.passwd
self.refresh_proxy_components()
dialog.destroy()
def set_save_button(self, button):
self.save_button = button
def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
combo_item = self.rootfs_combo.get_active_text()
modified = False
for child in check_hbox.get_children():
if isinstance(child, gtk.CheckButton):
check_hbox.remove(child)
modified = True
for format in all_package_format:
if format != combo_item:
check_button = gtk.CheckButton(format)
check_hbox.pack_start(check_button, expand=False, fill=False)
modified = True
if modified:
check_hbox.remove(self.pkgfmt_info)
check_hbox.pack_start(self.pkgfmt_info, expand=False, fill=False)
check_hbox.show_all()
def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
pkgfmt_vbox = gtk.VBox(False, 6)
label = self.gen_label_widget("Root file system package format")
pkgfmt_vbox.pack_start(label, expand=False, fill=False)
rootfs_format = ""
if curr_package_format:
rootfs_format = curr_package_format.split()[0]
rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
pkgfmt_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
label = self.gen_label_widget("Additional package formats")
pkgfmt_vbox.pack_start(label, expand=False, fill=False)
check_hbox = gtk.HBox(False, 12)
pkgfmt_vbox.pack_start(check_hbox, expand=False, fill=False)
for format in all_package_format:
if format != rootfs_format:
check_button = gtk.CheckButton(format)
is_active = (format in curr_package_format.split())
check_button.set_active(is_active)
check_hbox.pack_start(check_button, expand=False, fill=False)
self.pkgfmt_info = HobInfoButton(tooltip_extra, self)
check_hbox.pack_start(self.pkgfmt_info, expand=False, fill=False)
rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
pkgfmt_vbox.show_all()
return pkgfmt_vbox, rootfs_combo, check_hbox
def __init__(self, title, configuration, all_image_types,
all_package_formats, all_distros, all_sdk_machines,
max_threads, parent, flags, buttons=None):
super(AdvancedSettingsDialog, self).__init__(title, parent, flags, buttons)
# class members from other objects
# bitbake settings from Builder.Configuration
self.configuration = configuration
self.image_types = all_image_types
self.all_package_formats = all_package_formats
self.all_distros = all_distros[:]
self.all_sdk_machines = all_sdk_machines
self.max_threads = max_threads
# class members for internal use
self.distro_combo = None
self.dldir_text = None
self.sstatedir_text = None
self.sstatemirror_text = None
self.bb_spinner = None
self.pmake_spinner = None
self.rootfs_size_spinner = None
self.extra_size_spinner = None
self.gplv3_checkbox = None
self.toolchain_checkbox = None
self.image_types_checkbuttons = {}
self.md5 = self.config_md5()
self.settings_changed = False
# create visual elements on the dialog
self.save_button = None
self.create_visual_elements()
self.connect("response", self.response_cb)
def _get_sorted_value(self, var):
return " ".join(sorted(str(var).split())) + "\n"
def config_md5(self):
data = ""
data += ("PACKAGE_CLASSES: " + self.configuration.curr_package_format + '\n')
data += ("DISTRO: " + self._get_sorted_value(self.configuration.curr_distro))
data += ("IMAGE_ROOTFS_SIZE: " + self._get_sorted_value(self.configuration.image_rootfs_size))
data += ("IMAGE_EXTRA_SIZE: " + self._get_sorted_value(self.configuration.image_extra_size))
data += ("INCOMPATIBLE_LICENSE: " + self._get_sorted_value(self.configuration.incompat_license))
data += ("SDK_MACHINE: " + self._get_sorted_value(self.configuration.curr_sdk_machine))
data += ("TOOLCHAIN_BUILD: " + self._get_sorted_value(self.configuration.toolchain_build))
data += ("IMAGE_FSTYPES: " + self._get_sorted_value(self.configuration.image_fstypes))
return hashlib.md5(data).hexdigest()
def create_visual_elements(self):
self.nb = gtk.Notebook()
self.nb.set_show_tabs(True)
self.nb.append_page(self.create_image_types_page(), gtk.Label("Image types"))
self.nb.append_page(self.create_output_page(), gtk.Label("Output"))
self.nb.set_current_page(0)
self.vbox.pack_start(self.nb, expand=True, fill=True)
self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
self.show_all()
def get_num_checked_image_types(self):
total = 0
for b in self.image_types_checkbuttons.values():
if b.get_active():
total = total + 1
return total
def set_save_button_state(self):
if self.save_button:
self.save_button.set_sensitive(self.get_num_checked_image_types() > 0)
def image_type_checkbutton_clicked_cb(self, button):
self.set_save_button_state()
if self.get_num_checked_image_types() == 0:
# Show an error dialog
lbl = "<b>Select an image type</b>\n\nYou need to select at least one image type."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
button = dialog.add_button("OK", gtk.RESPONSE_OK)
HobButton.style_button(button)
response = dialog.run()
dialog.destroy()
def create_image_types_page(self):
main_vbox = gtk.VBox(False, 16)
main_vbox.set_border_width(6)
advanced_vbox = gtk.VBox(False, 6)
advanced_vbox.set_border_width(6)
distro_vbox = gtk.VBox(False, 6)
label = self.gen_label_widget("Distro:")
tooltip = "Selects the Yocto Project distribution you want"
try:
i = self.all_distros.index( "defaultsetup" )
except ValueError:
i = -1
if i != -1:
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, 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)
rows = (len(self.image_types)+1)/3
table = gtk.Table(rows + 1, 10, True)
advanced_vbox.pack_start(table, expand=False, fill=False)
tooltip = "Image file system types you want."
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)
align.add(label)
table.attach(info, 4, 5, 0, 1)
i = 1
j = 1
for image_type in sorted(self.image_types):
self.image_types_checkbuttons[image_type] = gtk.CheckButton(image_type)
self.image_types_checkbuttons[image_type].connect("toggled", self.image_type_checkbutton_clicked_cb)
article = ""
if image_type.startswith(("a", "e", "i", "o", "u")):
article = "n"
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)
i += 1
if i > rows:
i = 1
j = j + 4
main_vbox.pack_start(advanced_vbox, expand=False, fill=False)
self.set_save_button_state()
return main_vbox
def create_output_page(self):
advanced_vbox = gtk.VBox(False, 6)
advanced_vbox.set_border_width(6)
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Package format</span>'), expand=False, fill=False)
sub_vbox = gtk.VBox(False, 6)
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, 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 = "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 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)
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Licensing</span>'), expand=False, fill=False)
self.gplv3_checkbox = gtk.CheckButton("Exclude GPLv3 packages")
self.gplv3_checkbox.set_tooltip_text("Check this box to prevent GPLv3 packages from being included in your image")
if "GPLv3" in self.configuration.incompat_license.split():
self.gplv3_checkbox.set_active(True)
else:
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">Toolchain</span>'), expand=False, fill=False)
sub_hbox = gtk.HBox(False, 6)
advanced_vbox.pack_start(sub_hbox, 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 = "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
def response_cb(self, dialog, response_id):
package_format = []
package_format.append(self.rootfs_combo.get_active_text())
for child in self.check_hbox:
if isinstance(child, gtk.CheckButton) and child.get_active():
package_format.append(child.get_label())
self.configuration.curr_package_format = " ".join(package_format)
distro = self.distro_combo.get_active_text()
if distro == "Default":
distro = "defaultsetup"
self.configuration.curr_distro = distro
self.configuration.image_rootfs_size = self.rootfs_size_spinner.get_value_as_int() * 1024
self.configuration.image_extra_size = self.extra_size_spinner.get_value_as_int() * 1024
self.configuration.image_fstypes = ""
for image_type in self.image_types:
if self.image_types_checkbuttons[image_type].get_active():
self.configuration.image_fstypes += (" " + image_type)
self.configuration.image_fstypes.strip()
if self.gplv3_checkbox.get_active():
if "GPLv3" not in self.configuration.incompat_license.split():
self.configuration.incompat_license += " GPLv3"
else:
if "GPLv3" in self.configuration.incompat_license.split():
self.configuration.incompat_license = self.configuration.incompat_license.split().remove("GPLv3")
self.configuration.incompat_license = " ".join(self.configuration.incompat_license or [])
self.configuration.incompat_license = self.configuration.incompat_license.strip()
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)

View File

@@ -1,44 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class CrumbsDialog(gtk.Dialog):
"""
A GNOME HIG compliant dialog widget.
Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
"""
def __init__(self, title="", parent=None, flags=0, buttons=None):
super(CrumbsDialog, self).__init__(title, parent, flags, buttons)
self.set_property("has-separator", False) # note: deprecated in 2.22
self.set_border_width(6)
self.vbox.set_property("spacing", 12)
self.action_area.set_property("spacing", 12)
self.action_area.set_property("border-width", 6)

View File

@@ -1,95 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 glib
import gtk
from bb.ui.crumbs.hobwidget import HobIconChecker
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class CrumbsMessageDialog(CrumbsDialog):
"""
A GNOME HIG compliant dialog widget.
Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
"""
def __init__(self, parent=None, label="", icon=gtk.STOCK_INFO, msg=""):
super(CrumbsMessageDialog, self).__init__("", parent, gtk.DIALOG_MODAL)
self.set_border_width(6)
self.vbox.set_property("spacing", 12)
self.action_area.set_property("spacing", 12)
self.action_area.set_property("border-width", 6)
first_column = gtk.HBox(spacing=12)
first_column.set_property("border-width", 6)
first_column.show()
self.vbox.add(first_column)
self.icon = gtk.Image()
# We have our own Info icon which should be used in preference of the stock icon
self.icon_chk = HobIconChecker()
self.icon.set_from_stock(self.icon_chk.check_stock_icon(icon), gtk.ICON_SIZE_DIALOG)
self.icon.set_property("yalign", 0.00)
self.icon.show()
first_column.pack_start(self.icon, expand=False, fill=True, padding=0)
if 0 <= len(msg) < 200:
lbl = label + "%s" % glib.markup_escape_text(msg)
self.label_short = gtk.Label()
self.label_short.set_use_markup(True)
self.label_short.set_line_wrap(True)
self.label_short.set_markup(lbl)
self.label_short.set_property("yalign", 0.00)
self.label_short.show()
first_column.add(self.label_short)
else:
second_row = gtk.VBox(spacing=12)
second_row.set_property("border-width", 6)
self.label_long = gtk.Label()
self.label_long.set_use_markup(True)
self.label_long.set_line_wrap(True)
self.label_long.set_markup(label)
self.label_long.set_alignment(0.0, 0.0)
second_row.pack_start(self.label_long, expand=False, fill=False, padding=0)
self.label_long.show()
self.textWindow = gtk.ScrolledWindow()
self.textWindow.set_shadow_type(gtk.SHADOW_IN)
self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.msgView = gtk.TextView()
self.msgView.set_editable(False)
self.msgView.set_wrap_mode(gtk.WRAP_WORD)
self.msgView.set_cursor_visible(False)
self.msgView.set_size_request(300, 300)
self.buf = gtk.TextBuffer()
self.buf.set_text(msg)
self.msgView.set_buffer(self.buf)
self.textWindow.add(self.msgView)
self.msgView.show()
second_row.add(self.textWindow)
self.textWindow.show()
first_column.add(second_row)
second_row.show()

View File

@@ -1,215 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 glob
import gtk
import gobject
import os
import re
import shlex
import subprocess
import tempfile
from bb.ui.crumbs.hobwidget import hic, HobButton
from bb.ui.crumbs.progressbar import HobProgressBar
import bb.ui.crumbs.utils
import bb.process
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class DeployImageDialog (CrumbsDialog):
__dummy_usb__ = "--select a usb drive--"
def __init__(self, title, image_path, parent, flags, buttons=None, standalone=False):
super(DeployImageDialog, self).__init__(title, parent, flags, buttons)
self.image_path = image_path
self.standalone = standalone
self.create_visual_elements()
self.connect("response", self.response_cb)
def create_visual_elements(self):
self.set_size_request(600, 400)
label = gtk.Label()
label.set_alignment(0.0, 0.5)
markup = "<span font_desc='12'>The image to be written into usb drive:</span>"
label.set_markup(markup)
self.vbox.pack_start(label, expand=False, fill=False, padding=2)
table = gtk.Table(2, 10, False)
table.set_col_spacings(5)
table.set_row_spacings(5)
self.vbox.pack_start(table, expand=True, fill=True)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
scroll.set_shadow_type(gtk.SHADOW_IN)
tv = gtk.TextView()
tv.set_editable(False)
tv.set_wrap_mode(gtk.WRAP_WORD)
tv.set_cursor_visible(False)
self.buf = gtk.TextBuffer()
self.buf.set_text(self.image_path)
tv.set_buffer(self.buf)
scroll.add(tv)
table.attach(scroll, 0, 10, 0, 1)
# There are 2 ways to use DeployImageDialog
# One way is that called by HOB when the 'Deploy Image' button is clicked
# The other way is that called by a standalone script.
# Following block of codes handles the latter way. It adds a 'Select Image' button and
# emit a signal when the button is clicked.
if self.standalone:
gobject.signal_new("select_image_clicked", self, gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ())
icon = gtk.Image()
pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_IMAGES_DISPLAY_FILE)
icon.set_from_pixbuf(pix_buffer)
button = gtk.Button("Select Image")
button.set_image(icon)
#button.set_size_request(140, 50)
table.attach(button, 9, 10, 1, 2, gtk.FILL, 0, 0, 0)
button.connect("clicked", self.select_image_button_clicked_cb)
separator = gtk.HSeparator()
self.vbox.pack_start(separator, expand=False, fill=False, padding=10)
self.usb_desc = gtk.Label()
self.usb_desc.set_alignment(0.0, 0.5)
markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
self.usb_desc.set_markup(markup)
self.usb_combo = gtk.combo_box_new_text()
self.usb_combo.connect("changed", self.usb_combo_changed_cb)
model = self.usb_combo.get_model()
model.clear()
self.usb_combo.append_text(self.__dummy_usb__)
for usb in self.find_all_usb_devices():
self.usb_combo.append_text("/dev/" + usb)
self.usb_combo.set_active(0)
self.vbox.pack_start(self.usb_combo, expand=False, fill=False)
self.vbox.pack_start(self.usb_desc, expand=False, fill=False, padding=2)
self.progress_bar = HobProgressBar()
self.vbox.pack_start(self.progress_bar, expand=False, fill=False)
separator = gtk.HSeparator()
self.vbox.pack_start(separator, expand=False, fill=True, padding=10)
self.vbox.show_all()
self.progress_bar.hide()
def set_image_text_buffer(self, image_path):
self.buf.set_text(image_path)
def set_image_path(self, image_path):
self.image_path = image_path
def popen_read(self, cmd):
tmpout, errors = bb.process.run("%s" % cmd)
return tmpout.strip()
def find_all_usb_devices(self):
usb_devs = [ os.readlink(u)
for u in glob.glob('/dev/disk/by-id/usb*')
if not re.search(r'part\d+', u) ]
return [ '%s' % u[u.rfind('/')+1:] for u in usb_devs ]
def get_usb_info(self, dev):
return "%s %s" % \
(self.popen_read('cat /sys/class/block/%s/device/vendor' % dev),
self.popen_read('cat /sys/class/block/%s/device/model' % dev))
def select_image_button_clicked_cb(self, button):
self.emit('select_image_clicked')
def usb_combo_changed_cb(self, usb_combo):
combo_item = self.usb_combo.get_active_text()
if not combo_item or combo_item == self.__dummy_usb__:
markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
self.usb_desc.set_markup(markup)
else:
markup = "<span font_desc='12'>" + self.get_usb_info(combo_item.lstrip("/dev/")) + "</span>"
self.usb_desc.set_markup(markup)
def response_cb(self, dialog, response_id):
if response_id == gtk.RESPONSE_YES:
lbl = ''
combo_item = self.usb_combo.get_active_text()
if combo_item and combo_item != self.__dummy_usb__ and self.image_path:
cmdline = bb.ui.crumbs.utils.which_terminal()
if cmdline:
tmpfile = tempfile.NamedTemporaryFile()
cmdline += "\"sudo dd if=" + self.image_path + \
" of=" + combo_item + "; echo $? > " + tmpfile.name + "\""
subprocess.call(shlex.split(cmdline))
if int(tmpfile.readline().strip()) == 0:
lbl = "<b>Deploy image successfully.</b>"
else:
lbl = "<b>Failed to deploy image.</b>\nPlease check image <b>%s</b> exists and USB device <b>%s</b> is writable." % (self.image_path, combo_item)
tmpfile.close()
else:
if not self.image_path:
lbl = "<b>No selection made.</b>\nYou have not selected an image to deploy."
else:
lbl = "<b>No selection made.</b>\nYou have not selected a USB device."
if len(lbl):
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
crumbs_dialog.run()
crumbs_dialog.destroy()
def update_progress_bar(self, title, fraction, status=None):
self.progress_bar.update(fraction)
self.progress_bar.set_title(title)
self.progress_bar.set_rcstyle(status)
def write_file(self, ifile, ofile):
self.progress_bar.reset()
self.progress_bar.show()
f_from = os.open(ifile, os.O_RDONLY)
f_to = os.open(ofile, os.O_WRONLY)
total_size = os.stat(ifile).st_size
written_size = 0
while True:
buf = os.read(f_from, 1024*1024)
if not buf:
break
os.write(f_to, buf)
written_size += 1024*1024
self.update_progress_bar("Writing to usb:", written_size * 1.0/total_size)
self.update_progress_bar("Writing completed:", 1.0)
os.close(f_from)
os.close(f_to)
self.progress_bar.hide()

View File

@@ -1,172 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
import gobject
import os
from bb.ui.crumbs.hobwidget import HobViewTable, HobInfoButton, HobButton, HobAltButton
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
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 ImageSelectionDialog (CrumbsDialog):
__columns__ = [{
'col_name' : 'Image name',
'col_id' : 0,
'col_style': 'text',
'col_min' : 400,
'col_max' : 400
}, {
'col_name' : 'Select',
'col_id' : 1,
'col_style': 'radio toggle',
'col_min' : 160,
'col_max' : 160
}]
def __init__(self, image_folder, image_types, title, parent, flags, buttons=None, image_extension = {}):
super(ImageSelectionDialog, self).__init__(title, parent, flags, buttons)
self.connect("response", self.response_cb)
self.image_folder = image_folder
self.image_types = image_types
self.image_list = []
self.image_names = []
self.image_extension = image_extension
# create visual elements on the dialog
self.create_visual_elements()
self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
self.fill_image_store()
def create_visual_elements(self):
hbox = gtk.HBox(False, 6)
self.vbox.pack_start(hbox, expand=False, fill=False)
entry = gtk.Entry()
entry.set_text(self.image_folder)
table = gtk.Table(1, 10, True)
table.set_size_request(560, -1)
hbox.pack_start(table, expand=False, fill=False)
table.attach(entry, 0, 9, 0, 1)
image = gtk.Image()
image.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON)
open_button = gtk.Button()
open_button.set_image(image)
open_button.connect("clicked", self.select_path_cb, self, entry)
table.attach(open_button, 9, 10, 0, 1)
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)
self.image_table.connect("row-activated", self.row_actived_cb)
self.vbox.pack_start(self.image_table, expand=True, fill=True)
self.show_all()
def change_image_cb(self, model, path, columnid):
if not model:
return
iter = model.get_iter_first()
while iter:
rowpath = model.get_path(iter)
model[rowpath][columnid] = False
iter = model.iter_next(iter)
model[path][columnid] = True
def toggled_cb(self, table, cell, path, columnid, tree):
model = tree.get_model()
self.change_image_cb(model, path, columnid)
def table_selected_cb(self, selection):
model, paths = selection.get_selected_rows()
if paths:
self.change_image_cb(model, paths[0], 1)
def row_actived_cb(self, tab, model, path):
self.change_image_cb(model, path, 1)
self.emit('response', gtk.RESPONSE_YES)
def select_path_cb(self, action, parent, entry):
dialog = gtk.FileChooserDialog("", parent,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
text = entry.get_text()
dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
HobAltButton.style_button(button)
button = dialog.add_button("Open", gtk.RESPONSE_YES)
HobButton.style_button(button)
response = dialog.run()
if response == gtk.RESPONSE_YES:
path = dialog.get_filename()
entry.set_text(path)
self.image_folder = path
self.fill_image_store()
dialog.destroy()
def fill_image_store(self):
self.image_list = []
self.image_store.clear()
imageset = set()
for root, dirs, files in os.walk(self.image_folder):
# ignore the sub directories
dirs[:] = []
for f in files:
for image_type in self.image_types:
if image_type in self.image_extension:
real_types = self.image_extension[image_type]
else:
real_types = [image_type]
for real_image_type in real_types:
if f.endswith('.' + real_image_type):
imageset.add(f.rsplit('.' + real_image_type)[0].rsplit('.rootfs')[0])
self.image_list.append(f)
for image in imageset:
self.image_store.set(self.image_store.append(), 0, image, 1, False)
self.image_table.set_model(self.image_store)
def response_cb(self, dialog, response_id):
self.image_names = []
if response_id == gtk.RESPONSE_YES:
iter = self.image_store.get_iter_first()
while iter:
path = self.image_store.get_path(iter)
if self.image_store[path][1]:
for f in self.image_list:
if f.startswith(self.image_store[path][0] + '.'):
self.image_names.append(f)
break
iter = self.image_store.iter_next(iter)

View File

@@ -1,296 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
import gobject
import os
import tempfile
from bb.ui.crumbs.hobwidget import hic, HobButton, HobAltButton
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class CellRendererPixbufActivatable(gtk.CellRendererPixbuf):
"""
A custom CellRenderer implementation which is activatable
so that we can handle user clicks
"""
__gsignals__ = { 'clicked' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING,)), }
def __init__(self):
gtk.CellRendererPixbuf.__init__(self)
self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
self.set_property('follow-state', True)
"""
Respond to a user click on a cell
"""
def do_activate(self, even, widget, path, background_area, cell_area, flags):
self.emit('clicked', path)
#
# LayerSelectionDialog
#
class LayerSelectionDialog (CrumbsDialog):
TARGETS = [
("MY_TREE_MODEL_ROW", gtk.TARGET_SAME_WIDGET, 0),
("text/plain", 0, 1),
("TEXT", 0, 2),
("STRING", 0, 3),
]
def gen_label_widget(self, content):
label = gtk.Label()
label.set_alignment(0, 0)
label.set_markup(content)
label.show()
return label
def layer_widget_toggled_cb(self, cell, path, layer_store):
name = layer_store[path][0]
toggle = not layer_store[path][1]
layer_store[path][1] = toggle
def layer_widget_add_clicked_cb(self, action, layer_store, parent):
dialog = gtk.FileChooserDialog("Add new layer", parent,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
HobAltButton.style_button(button)
button = dialog.add_button("Open", gtk.RESPONSE_YES)
HobButton.style_button(button)
label = gtk.Label("Select the layer you wish to add")
label.show()
dialog.set_extra_widget(label)
response = dialog.run()
path = dialog.get_filename()
dialog.destroy()
lbl = "<b>Error</b>\nUnable to load layer <i>%s</i> because " % path
if response == gtk.RESPONSE_YES:
import os
import os.path
layers = []
it = layer_store.get_iter_first()
while it:
layers.append(layer_store.get_value(it, 0))
it = layer_store.iter_next(it)
if not path:
lbl += "it is an invalid path."
elif not os.path.exists(path+"/conf/layer.conf"):
lbl += "there is no layer.conf inside the directory."
elif path in layers:
lbl += "it is already in loaded layers."
else:
layer_store.append([path])
return
dialog = CrumbsMessageDialog(parent, lbl)
dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
response = dialog.run()
dialog.destroy()
def layer_widget_del_clicked_cb(self, action, tree_selection, layer_store):
model, iter = tree_selection.get_selected()
if iter:
layer_store.remove(iter)
def gen_layer_widget(self, layers, layers_avail, window, tooltip=""):
hbox = gtk.HBox(False, 6)
layer_tv = gtk.TreeView()
layer_tv.set_rules_hint(True)
layer_tv.set_headers_visible(False)
tree_selection = layer_tv.get_selection()
tree_selection.set_mode(gtk.SELECTION_SINGLE)
# Allow enable drag and drop of rows including row move
layer_tv.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
self.TARGETS,
gtk.gdk.ACTION_DEFAULT|
gtk.gdk.ACTION_MOVE)
layer_tv.enable_model_drag_dest(self.TARGETS,
gtk.gdk.ACTION_DEFAULT)
layer_tv.connect("drag_data_get", self.drag_data_get_cb)
layer_tv.connect("drag_data_received", self.drag_data_received_cb)
col0= gtk.TreeViewColumn('Path')
cell0 = gtk.CellRendererText()
cell0.set_padding(5,2)
col0.pack_start(cell0, True)
col0.set_cell_data_func(cell0, self.draw_layer_path_cb)
layer_tv.append_column(col0)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
scroll.set_shadow_type(gtk.SHADOW_IN)
scroll.add(layer_tv)
table_layer = gtk.Table(2, 10, False)
hbox.pack_start(table_layer, expand=True, fill=True)
table_layer.attach(scroll, 0, 10, 0, 1)
layer_store = gtk.ListStore(gobject.TYPE_STRING)
for layer in layers:
layer_store.append([layer])
col1 = gtk.TreeViewColumn('Enabled')
layer_tv.append_column(col1)
cell1 = CellRendererPixbufActivatable()
cell1.set_fixed_size(-1,35)
cell1.connect("clicked", self.del_cell_clicked_cb, layer_store)
col1.pack_start(cell1, True)
col1.set_cell_data_func(cell1, self.draw_delete_button_cb, layer_tv)
add_button = gtk.Button()
add_button.set_relief(gtk.RELIEF_NONE)
box = gtk.HBox(False, 6)
box.show()
add_button.add(box)
add_button.connect("enter-notify-event", self.add_hover_cb)
add_button.connect("leave-notify-event", self.add_leave_cb)
self.im = gtk.Image()
self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
self.im.show()
box.pack_start(self.im, expand=False, fill=False, padding=6)
lbl = gtk.Label("Add layer")
lbl.set_alignment(0.0, 0.5)
lbl.show()
box.pack_start(lbl, expand=True, fill=True, padding=6)
add_button.connect("clicked", self.layer_widget_add_clicked_cb, layer_store, window)
table_layer.attach(add_button, 0, 10, 1, 2, gtk.EXPAND | gtk.FILL, 0, 0, 6)
layer_tv.set_model(layer_store)
hbox.show_all()
return hbox, layer_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_value(iter, 0)
selection.set(selection.target, 8, data)
def drag_data_received_cb(self, treeview, context, x, y, selection, info, etime):
model = treeview.get_model()
data = selection.data
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 add_hover_cb(self, button, event):
self.im.set_from_file(hic.ICON_INDI_ADD_HOVER_FILE)
def add_leave_cb(self, button, event):
self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
def __init__(self, title, layers, layers_non_removable, all_layers, parent, flags, buttons=None):
super(LayerSelectionDialog, self).__init__(title, parent, flags, buttons)
# class members from other objects
self.layers = layers
self.layers_non_removable = layers_non_removable
self.all_layers = all_layers
self.layers_changed = False
# icon for remove button in TreeView
im = gtk.Image()
im.set_from_file(hic.ICON_INDI_REMOVE_FILE)
self.rem_icon = im.get_pixbuf()
# class members for internal use
self.layer_store = None
# create visual elements on the dialog
self.create_visual_elements()
self.connect("response", self.response_cb)
def create_visual_elements(self):
layer_widget, self.layer_store = self.gen_layer_widget(self.layers, self.all_layers, self, None)
layer_widget.set_size_request(450, 250)
self.vbox.pack_start(layer_widget, expand=True, fill=True)
self.show_all()
def response_cb(self, dialog, response_id):
model = self.layer_store
it = model.get_iter_first()
layers = []
while it:
layers.append(model.get_value(it, 0))
it = model.iter_next(it)
self.layers_changed = (self.layers != layers)
self.layers = layers
"""
A custom cell_data_func to draw a delete 'button' in the TreeView for layers
other than the meta layer. The deletion of which is prevented so that the
user can't shoot themselves in the foot too badly.
"""
def draw_delete_button_cb(self, col, cell, model, it, tv):
path = model.get_value(it, 0)
if path in self.layers_non_removable:
cell.set_sensitive(False)
cell.set_property('pixbuf', None)
cell.set_property('mode', gtk.CELL_RENDERER_MODE_INERT)
else:
cell.set_property('pixbuf', self.rem_icon)
cell.set_sensitive(True)
cell.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
return True
"""
A custom cell_data_func to write an extra message into the layer path cell
for the meta layer. We should inform the user that they can't remove it for
their own safety.
"""
def draw_layer_path_cb(self, col, cell, model, it):
path = model.get_value(it, 0)
if path in self.layers_non_removable:
cell.set_property('markup', "<b>It cannot be removed</b>\n%s" % path)
else:
cell.set_property('text', path)
def del_cell_clicked_cb(self, cell, path, model):
it = model.get_iter_from_string(path)
model.remove(it)

View File

@@ -1,161 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 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 gobject
from bb.ui.crumbs.hobwidget import HobAltButton
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
#
# ParsingWarningsDialog
#
class ParsingWarningsDialog (CrumbsDialog):
def __init__(self, title, warnings, parent, flags, buttons=None):
super(ParsingWarningsDialog, self).__init__(title, parent, flags, buttons)
self.warnings = warnings
self.warning_on = 0
self.warn_nb = len(warnings)
# create visual elements on the dialog
self.create_visual_elements()
def cancel_button_cb(self, button):
self.destroy()
def previous_button_cb(self, button):
self.warning_on = self.warning_on - 1
self.refresh_components()
def next_button_cb(self, button):
self.warning_on = self.warning_on + 1
self.refresh_components()
def refresh_components(self):
lbl = self.warnings[self.warning_on]
#when the warning text has more than 400 chars, it uses a scroll bar
if 0<= len(lbl) < 400:
self.warning_label.set_size_request(320, 230)
self.warning_label.set_use_markup(True)
self.warning_label.set_line_wrap(True)
self.warning_label.set_markup(lbl)
self.warning_label.set_property("yalign", 0.00)
else:
self.textWindow.set_shadow_type(gtk.SHADOW_IN)
self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.msgView = gtk.TextView()
self.msgView.set_editable(False)
self.msgView.set_wrap_mode(gtk.WRAP_WORD)
self.msgView.set_cursor_visible(False)
self.msgView.set_size_request(320, 230)
self.buf = gtk.TextBuffer()
self.buf.set_text(lbl)
self.msgView.set_buffer(self.buf)
self.textWindow.add(self.msgView)
self.msgView.show()
if self.warning_on==0:
self.previous_button.set_sensitive(False)
else:
self.previous_button.set_sensitive(True)
if self.warning_on==self.warn_nb-1:
self.next_button.set_sensitive(False)
else:
self.next_button.set_sensitive(True)
if self.warn_nb>1:
self.heading = "Warning " + str(self.warning_on + 1) + " of " + str(self.warn_nb)
self.heading_label.set_markup('<span weight="bold">%s</span>' % self.heading)
else:
self.heading = "Warning"
self.heading_label.set_markup('<span weight="bold">%s</span>' % self.heading)
self.show_all()
if 0<= len(lbl) < 400:
self.textWindow.hide()
else:
self.warning_label.hide()
def create_visual_elements(self):
self.set_size_request(350, 350)
self.heading_label = gtk.Label()
self.heading_label.set_alignment(0.1, 0)
self.warning_label = gtk.Label()
self.textWindow = gtk.ScrolledWindow()
table = gtk.Table(1, 10, False)
cancel_button = gtk.Button()
cancel_button.set_label("Close")
cancel_button.connect("clicked", self.cancel_button_cb)
cancel_button.set_size_request(110, 30)
self.previous_button = gtk.Button()
image1 = gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
image1.show()
box = gtk.HBox(False, 6)
box.show()
self.previous_button.add(box)
lbl = gtk.Label("Previous")
lbl.show()
box.pack_start(image1, expand=False, fill=False, padding=3)
box.pack_start(lbl, expand=True, fill=True, padding=3)
self.previous_button.connect("clicked", self.previous_button_cb)
self.previous_button.set_size_request(110, 30)
self.next_button = gtk.Button()
image2 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
image2.show()
box = gtk.HBox(False, 6)
box.show()
self.next_button.add(box)
lbl = gtk.Label("Next")
lbl.show()
box.pack_start(lbl, expand=True, fill=True, padding=3)
box.pack_start(image2, expand=False, fill=False, padding=3)
self.next_button.connect("clicked", self.next_button_cb)
self.next_button.set_size_request(110, 30)
#when there more than one warning, we need "previous" and "next" button
if self.warn_nb>1:
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)
table.attach(cancel_button, 6, 7, 0, 1, xoptions=gtk.SHRINK)
table.attach(self.previous_button, 7, 8, 0, 1, xoptions=gtk.SHRINK)
table.attach(self.next_button, 8, 9, 0, 1, xoptions=gtk.SHRINK)
self.vbox.pack_end(table, expand=False, fill=False)
else:
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("Cancel", gtk.RESPONSE_CANCEL)
HobAltButton.style_button(cancel_button)
self.refresh_components()

View File

@@ -1,90 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class ProxyDetailsDialog (CrumbsDialog):
def __init__(self, title, user, passwd, parent, flags, buttons=None):
super(ProxyDetailsDialog, self).__init__(title, parent, flags, buttons)
self.connect("response", self.response_cb)
self.auth = not (user == None or passwd == None or user == "")
self.user = user or ""
self.passwd = passwd or ""
# create visual elements on the dialog
self.create_visual_elements()
def create_visual_elements(self):
self.auth_checkbox = gtk.CheckButton("Use authentication")
self.auth_checkbox.set_tooltip_text("Check this box to set the username and the password")
self.auth_checkbox.set_active(self.auth)
self.auth_checkbox.connect("toggled", self.auth_checkbox_toggled_cb)
self.vbox.pack_start(self.auth_checkbox, expand=False, fill=False)
hbox = gtk.HBox(False, 6)
self.user_label = gtk.Label("Username:")
self.user_text = gtk.Entry()
self.user_text.set_text(self.user)
hbox.pack_start(self.user_label, expand=False, fill=False)
hbox.pack_end(self.user_text, expand=False, fill=False)
self.vbox.pack_start(hbox, expand=False, fill=False)
hbox = gtk.HBox(False, 6)
self.passwd_label = gtk.Label("Password:")
self.passwd_text = gtk.Entry()
self.passwd_text.set_text(self.passwd)
hbox.pack_start(self.passwd_label, expand=False, fill=False)
hbox.pack_end(self.passwd_text, expand=False, fill=False)
self.vbox.pack_start(hbox, expand=False, fill=False)
self.refresh_auth_components()
self.show_all()
def refresh_auth_components(self):
self.user_label.set_sensitive(self.auth)
self.user_text.set_editable(self.auth)
self.user_text.set_sensitive(self.auth)
self.passwd_label.set_sensitive(self.auth)
self.passwd_text.set_editable(self.auth)
self.passwd_text.set_sensitive(self.auth)
def auth_checkbox_toggled_cb(self, button):
self.auth = self.auth_checkbox.get_active()
self.refresh_auth_components()
def response_cb(self, dialog, response_id):
if response_id == gtk.RESPONSE_OK:
if self.auth:
self.user = self.user_text.get_text()
self.passwd = self.passwd_text.get_text()
else:
self.user = None
self.passwd = None

View File

@@ -1,216 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
import os
from bb.ui.crumbs.hobwidget import HobInfoButton, HobButton, HobAltButton
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class SettingsUIHelper():
def gen_label_widget(self, content):
label = gtk.Label()
label.set_alignment(0, 0)
label.set_markup(content)
label.show()
return label
def gen_label_info_widget(self, content, tooltip):
table = gtk.Table(1, 10, False)
label = self.gen_label_widget(content)
info = HobInfoButton(tooltip, self)
table.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL)
table.attach(info, 1, 2, 0, 1, xoptions=gtk.FILL, xpadding=10)
return table
def gen_spinner_widget(self, content, lower, upper, tooltip=""):
hbox = gtk.HBox(False, 12)
adjust = gtk.Adjustment(value=content, lower=lower, upper=upper, step_incr=1)
spinner = gtk.SpinButton(adjustment=adjust, climb_rate=1, digits=0)
spinner.set_value(content)
hbox.pack_start(spinner, expand=False, fill=False)
info = HobInfoButton(tooltip, self)
hbox.pack_start(info, expand=False, fill=False)
hbox.show_all()
return hbox, spinner
def gen_combo_widget(self, curr_item, all_item, tooltip=""):
hbox = gtk.HBox(False, 12)
combo = gtk.combo_box_new_text()
hbox.pack_start(combo, expand=False, fill=False)
index = 0
for item in all_item or []:
combo.append_text(item)
if item == curr_item:
combo.set_active(index)
index += 1
info = HobInfoButton(tooltip, self)
hbox.pack_start(info, expand=False, fill=False)
hbox.show_all()
return hbox, combo
def entry_widget_select_path_cb(self, action, parent, entry):
dialog = gtk.FileChooserDialog("", parent,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
text = entry.get_text()
dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
HobAltButton.style_button(button)
button = dialog.add_button("Open", gtk.RESPONSE_YES)
HobButton.style_button(button)
response = dialog.run()
if response == gtk.RESPONSE_YES:
path = dialog.get_filename()
entry.set_text(path)
dialog.destroy()
def gen_entry_widget(self, content, parent, tooltip="", need_button=True):
hbox = gtk.HBox(False, 12)
entry = gtk.Entry()
entry.set_text(content)
entry.set_size_request(350,30)
if need_button:
table = gtk.Table(1, 10, False)
hbox.pack_start(table, expand=True, fill=True)
table.attach(entry, 0, 9, 0, 1, xoptions=gtk.SHRINK)
image = gtk.Image()
image.set_from_stock(gtk.STOCK_OPEN,gtk.ICON_SIZE_BUTTON)
open_button = gtk.Button()
open_button.set_image(image)
open_button.connect("clicked", self.entry_widget_select_path_cb, parent, entry)
table.attach(open_button, 9, 10, 0, 1, xoptions=gtk.SHRINK)
else:
hbox.pack_start(entry, expand=True, fill=True)
if tooltip != "":
info = HobInfoButton(tooltip, self)
hbox.pack_start(info, expand=False, fill=False)
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()

View File

@@ -1,722 +0,0 @@
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011-2012 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
# 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 gtk
import gobject
import hashlib
from bb.ui.crumbs.hobwidget import hic, HobInfoButton, HobButton, HobAltButton
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
from bb.ui.crumbs.hig.proxydetailsdialog import ProxyDetailsDialog
"""
The following are convenience classes for implementing GNOME HIG compliant
BitBake GUI's
In summary: spacing = 12px, border-width = 6px
"""
class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
(BUILD_ENV_PAGE_ID,
SHARED_STATE_PAGE_ID,
PROXIES_PAGE_ID,
OTHERS_PAGE_ID) = range(4)
(TEST_NETWORK_NONE,
TEST_NETWORK_INITIAL,
TEST_NETWORK_RUNNING,
TEST_NETWORK_PASSED,
TEST_NETWORK_FAILED,
TEST_NETWORK_CANCELED) = range(6)
def __init__(self, title, configuration, all_image_types,
all_package_formats, all_distros, all_sdk_machines,
max_threads, parent, flags, handler, buttons=None):
super(SimpleSettingsDialog, self).__init__(title, parent, flags, buttons)
# class members from other objects
# bitbake settings from Builder.Configuration
self.configuration = configuration
self.image_types = all_image_types
self.all_package_formats = all_package_formats
self.all_distros = all_distros
self.all_sdk_machines = all_sdk_machines
self.max_threads = max_threads
# class members for internal use
self.dldir_text = None
self.sstatedir_text = None
self.sstatemirrors_list = []
self.sstatemirrors_changed = 0
self.bb_spinner = None
self.pmake_spinner = None
self.rootfs_size_spinner = None
self.extra_size_spinner = None
self.gplv3_checkbox = None
self.toolchain_checkbox = None
self.setting_store = None
self.image_types_checkbuttons = {}
self.md5 = self.config_md5()
self.proxy_md5 = self.config_proxy_md5()
self.settings_changed = False
self.proxy_settings_changed = False
self.handler = handler
self.proxy_test_ran = False
# create visual elements on the dialog
self.create_visual_elements()
self.connect("response", self.response_cb)
def _get_sorted_value(self, var):
return " ".join(sorted(str(var).split())) + "\n"
def config_proxy_md5(self):
data = ("ENABLE_PROXY: " + self._get_sorted_value(self.configuration.enable_proxy))
if self.configuration.enable_proxy:
for protocol in self.configuration.proxies.keys():
data += (protocol + ": " + self._get_sorted_value(self.configuration.combine_proxy(protocol)))
return hashlib.md5(data).hexdigest()
def config_md5(self):
data = ""
for key in self.configuration.extra_setting.keys():
data += (key + ": " + self._get_sorted_value(self.configuration.extra_setting[key]))
return hashlib.md5(data).hexdigest()
def gen_proxy_entry_widget(self, protocol, parent, need_button=True, line=0):
label = gtk.Label(protocol.upper() + " proxy")
self.proxy_table.attach(label, 0, 1, line, line+1, xpadding=24)
proxy_entry = gtk.Entry()
proxy_entry.set_size_request(300, -1)
self.proxy_table.attach(proxy_entry, 1, 2, line, line+1, ypadding=4)
self.proxy_table.attach(gtk.Label(":"), 2, 3, line, line+1, xpadding=12, ypadding=4)
port_entry = gtk.Entry()
port_entry.set_size_request(60, -1)
self.proxy_table.attach(port_entry, 3, 4, line, line+1, ypadding=4)
details_button = HobAltButton("Details")
details_button.connect("clicked", self.details_cb, parent, protocol)
self.proxy_table.attach(details_button, 4, 5, line, line+1, xpadding=4, yoptions=gtk.EXPAND)
return proxy_entry, port_entry, details_button
def refresh_proxy_components(self):
self.same_checkbox.set_sensitive(self.configuration.enable_proxy)
self.http_proxy.set_text(self.configuration.combine_host_only("http"))
self.http_proxy.set_editable(self.configuration.enable_proxy)
self.http_proxy.set_sensitive(self.configuration.enable_proxy)
self.http_proxy_port.set_text(self.configuration.combine_port_only("http"))
self.http_proxy_port.set_editable(self.configuration.enable_proxy)
self.http_proxy_port.set_sensitive(self.configuration.enable_proxy)
self.http_proxy_details.set_sensitive(self.configuration.enable_proxy)
self.https_proxy.set_text(self.configuration.combine_host_only("https"))
self.https_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.https_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.https_proxy_port.set_text(self.configuration.combine_port_only("https"))
self.https_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.https_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.https_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.ftp_proxy.set_text(self.configuration.combine_host_only("ftp"))
self.ftp_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.ftp_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.ftp_proxy_port.set_text(self.configuration.combine_port_only("ftp"))
self.ftp_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
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.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))
self.cvs_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.cvs_proxy_port.set_text(self.configuration.combine_port_only("cvs"))
self.cvs_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.cvs_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
self.cvs_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
if self.configuration.same_proxy:
if self.http_proxy.get_text():
[w.set_text(self.http_proxy.get_text()) for w in self.same_proxy_addresses]
if self.http_proxy_port.get_text():
[w.set_text(self.http_proxy_port.get_text()) for w in self.same_proxy_ports]
def proxy_checkbox_toggled_cb(self, button):
self.configuration.enable_proxy = self.proxy_checkbox.get_active()
if not self.configuration.enable_proxy:
self.configuration.same_proxy = False
self.same_checkbox.set_active(self.configuration.same_proxy)
self.save_proxy_data()
self.refresh_proxy_components()
def same_checkbox_toggled_cb(self, button):
self.configuration.same_proxy = self.same_checkbox.get_active()
self.save_proxy_data()
self.refresh_proxy_components()
def save_proxy_data(self):
self.configuration.split_proxy("http", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
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("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("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):
if response_id == gtk.RESPONSE_YES:
# Check that all proxy entries have a corresponding port
for proxy, port in zip(self.all_proxy_addresses, self.all_proxy_ports):
if proxy.get_text() and not port.get_text():
lbl = "<b>Enter all port numbers</b>\n\n"
msg = "Proxy servers require a port number. Please make sure you have entered a port number for each proxy server."
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING, msg)
button = dialog.add_button("Close", gtk.RESPONSE_OK)
HobButton.style_button(button)
response = dialog.run()
dialog.destroy()
self.emit_stop_by_name("response")
return
self.configuration.dldir = self.dldir_text.get_text()
self.configuration.sstatedir = self.sstatedir_text.get_text()
self.configuration.sstatemirror = ""
for mirror in self.sstatemirrors_list:
if mirror[1] != "":
if mirror[1].endswith("\\1"):
smirror = mirror[2] + " " + mirror[1] + " \\n "
else:
smirror = mirror[2] + " " + mirror[1] + "\\1 \\n "
self.configuration.sstatemirror += smirror
self.configuration.bbthread = self.bb_spinner.get_value_as_int()
self.configuration.pmake = self.pmake_spinner.get_value_as_int()
self.save_proxy_data()
self.configuration.extra_setting = {}
it = self.setting_store.get_iter_first()
while it:
key = self.setting_store.get_value(it, 0)
value = self.setting_store.get_value(it, 1)
self.configuration.extra_setting[key] = value
it = self.setting_store.iter_next(it)
md5 = self.config_md5()
self.settings_changed = (self.md5 != md5)
self.proxy_settings_changed = (self.proxy_md5 != self.config_proxy_md5())
def create_build_environment_page(self):
advanced_vbox = gtk.VBox(False, 6)
advanced_vbox.set_border_width(6)
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Parallel threads</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("BitBake parallel threads")
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, tooltip)
sub_vbox.pack_start(label, expand=False, fill=False)
sub_vbox.pack_start(bbthread_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("Make parallel threads")
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, tooltip)
sub_vbox.pack_start(label, expand=False, fill=False)
sub_vbox.pack_start(pmake_widget, expand=False, fill=False)
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Downloaded source code</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("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, tooltip)
sub_vbox.pack_start(label, expand=False, fill=False)
sub_vbox.pack_start(dldir_widget, expand=False, fill=False)
return advanced_vbox
def create_shared_state_page(self):
advanced_vbox = gtk.VBox(False)
advanced_vbox.set_border_width(12)
sub_vbox = gtk.VBox(False)
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, 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=12)
content = "<span weight=\"bold\">Shared state mirrors</span>"
tooltip = "URLs pointing to pre-built mirrors that will speed your build. "
tooltip += "Select the \'Standard\' configuration if the structure of your "
tooltip += "mirror replicates the structure of your local shared state directory. "
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, tooltip)
advanced_vbox.pack_start(table, expand=False, fill=False)
sub_vbox = gtk.VBox(False)
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 = [ 0, "", "file://(.*)"]
self.sstatemirrors_list.append(sm_list)
else:
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 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)
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, 1, False)
table.set_col_spacings(6)
add_mirror_button = HobAltButton("Add another mirror")
add_mirror_button.connect("clicked", self.add_mirror)
add_mirror_button.set_size_request(150,30)
table.attach(add_mirror_button, 1, 2, 0, 1, xoptions=gtk.SHRINK)
advanced_vbox.pack_start(table, expand=False, fill=False, padding=9)
return advanced_vbox
def refresh_shared_state_page(self):
page_num = self.nb.get_current_page()
self.nb.remove_page(page_num);
self.nb.insert_page(self.create_shared_state_page(), gtk.Label("Shared state"),page_num)
self.show_all()
self.nb.set_current_page(page_num)
def test_proxy_ended(self, passed):
self.proxy_test_running = False
self.set_test_proxy_state(self.TEST_NETWORK_PASSED if passed else self.TEST_NETWORK_FAILED)
self.set_sensitive(True)
self.refresh_proxy_components()
def timer_func(self):
self.test_proxy_progress.pulse()
return self.proxy_test_running
def test_network_button_cb(self, b):
self.set_test_proxy_state(self.TEST_NETWORK_RUNNING)
self.set_sensitive(False)
self.save_proxy_data()
if self.configuration.enable_proxy == True:
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_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_git_proxy("", "")
self.handler.set_cvs_proxy("", "")
self.proxy_test_ran = True
self.proxy_test_running = True
gobject.timeout_add(100, self.timer_func)
self.handler.trigger_network_test()
def test_proxy_focus_event(self, w, direction):
if self.test_proxy_state in [self.TEST_NETWORK_PASSED, self.TEST_NETWORK_FAILED]:
self.set_test_proxy_state(self.TEST_NETWORK_INITIAL)
return False
def http_proxy_changed(self, e):
if not self.configuration.same_proxy:
return
if e == self.http_proxy:
[w.set_text(self.http_proxy.get_text()) for w in self.same_proxy_addresses]
else:
[w.set_text(self.http_proxy_port.get_text()) for w in self.same_proxy_ports]
def proxy_address_focus_out_event(self, w, direction):
text = w.get_text()
if not text:
return False
if text.find("//") == -1:
w.set_text("http://" + text)
return False
def set_test_proxy_state(self, state):
if self.test_proxy_state == state:
return
[self.proxy_table.remove(w) for w in self.test_gui_elements]
if state == self.TEST_NETWORK_INITIAL:
self.proxy_table.attach(self.test_network_button, 1, 2, 5, 6)
self.test_network_button.show()
elif state == self.TEST_NETWORK_RUNNING:
self.test_proxy_progress.set_rcstyle("running")
self.test_proxy_progress.set_text("Testing network configuration")
self.proxy_table.attach(self.test_proxy_progress, 0, 5, 5, 6, xpadding=4)
self.test_proxy_progress.show()
else: # passed or failed
self.dummy_progress.update(1.0)
if state == self.TEST_NETWORK_PASSED:
self.dummy_progress.set_text("Your network is properly configured")
self.dummy_progress.set_rcstyle("running")
else:
self.dummy_progress.set_text("Network test failed")
self.dummy_progress.set_rcstyle("fail")
self.proxy_table.attach(self.dummy_progress, 0, 4, 5, 6)
self.proxy_table.attach(self.retest_network_button, 4, 5, 5, 6, xpadding=4)
self.dummy_progress.show()
self.retest_network_button.show()
self.test_proxy_state = state
def create_network_page(self):
advanced_vbox = gtk.VBox(False, 6)
advanced_vbox.set_border_width(6)
self.same_proxy_addresses = []
self.same_proxy_ports = []
self.all_proxy_ports = []
self.all_proxy_addresses = []
sub_vbox = gtk.VBox(False, 6)
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(tooltip, self)
hbox = gtk.HBox(False, 12)
hbox.pack_start(label, expand=True, fill=True)
hbox.pack_start(info, expand=False, fill=False)
sub_vbox.pack_start(hbox, expand=False, fill=False)
proxy_test_focus = []
self.direct_checkbox = gtk.RadioButton(None, "Direct network connection")
proxy_test_focus.append(self.direct_checkbox)
self.direct_checkbox.set_tooltip_text("Check this box to use a direct internet connection with no proxy")
self.direct_checkbox.set_active(not self.configuration.enable_proxy)
sub_vbox.pack_start(self.direct_checkbox, expand=False, fill=False)
self.proxy_checkbox = gtk.RadioButton(self.direct_checkbox, "Manual proxy configuration")
proxy_test_focus.append(self.proxy_checkbox)
self.proxy_checkbox.set_tooltip_text("Check this box to manually set up a specific proxy")
self.proxy_checkbox.set_active(self.configuration.enable_proxy)
sub_vbox.pack_start(self.proxy_checkbox, expand=False, fill=False)
self.same_checkbox = gtk.CheckButton("Use the HTTP proxy for all protocols")
proxy_test_focus.append(self.same_checkbox)
self.same_checkbox.set_tooltip_text("Check this box to use the HTTP proxy for all five proxies")
self.same_checkbox.set_active(self.configuration.same_proxy)
hbox = gtk.HBox(False, 12)
hbox.pack_start(self.same_checkbox, expand=False, fill=False, padding=24)
sub_vbox.pack_start(hbox, expand=False, fill=False)
self.proxy_table = gtk.Table(6, 5, False)
self.http_proxy, self.http_proxy_port, self.http_proxy_details = self.gen_proxy_entry_widget(
"http", self, True, 0)
proxy_test_focus +=[self.http_proxy, self.http_proxy_port]
self.http_proxy.connect("changed", self.http_proxy_changed)
self.http_proxy_port.connect("changed", self.http_proxy_changed)
self.https_proxy, self.https_proxy_port, self.https_proxy_details = self.gen_proxy_entry_widget(
"https", self, True, 1)
proxy_test_focus += [self.https_proxy, self.https_proxy_port]
self.same_proxy_addresses.append(self.https_proxy)
self.same_proxy_ports.append(self.https_proxy_port)
self.ftp_proxy, self.ftp_proxy_port, self.ftp_proxy_details = self.gen_proxy_entry_widget(
"ftp", self, True, 2)
proxy_test_focus += [self.ftp_proxy, self.ftp_proxy_port]
self.same_proxy_addresses.append(self.ftp_proxy)
self.same_proxy_ports.append(self.ftp_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)
proxy_test_focus += [self.cvs_proxy, self.cvs_proxy_port]
self.same_proxy_addresses.append(self.cvs_proxy)
self.same_proxy_ports.append(self.cvs_proxy_port)
self.all_proxy_ports = self.same_proxy_ports + [self.http_proxy_port]
self.all_proxy_addresses = self.same_proxy_addresses + [self.http_proxy]
sub_vbox.pack_start(self.proxy_table, expand=False, fill=False)
self.proxy_table.show_all()
# Create the graphical elements for the network test feature, but don't display them yet
self.test_network_button = HobAltButton("Test network configuration")
self.test_network_button.connect("clicked", self.test_network_button_cb)
self.test_proxy_progress = HobProgressBar()
self.dummy_progress = HobProgressBar()
self.retest_network_button = HobAltButton("Retest")
self.retest_network_button.connect("clicked", self.test_network_button_cb)
self.test_gui_elements = [self.test_network_button, self.test_proxy_progress, self.dummy_progress, self.retest_network_button]
# Initialize the network tester
self.test_proxy_state = self.TEST_NETWORK_NONE
self.set_test_proxy_state(self.TEST_NETWORK_INITIAL)
self.proxy_test_passed_id = self.handler.connect("network-passed", lambda h:self.test_proxy_ended(True))
self.proxy_test_failed_id = self.handler.connect("network-failed", lambda h:self.test_proxy_ended(False))
[w.connect("focus-in-event", self.test_proxy_focus_event) for w in proxy_test_focus]
[w.connect("focus-out-event", self.proxy_address_focus_out_event) for w in self.all_proxy_addresses]
self.direct_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
self.proxy_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
self.same_checkbox.connect("toggled", self.same_checkbox_toggled_cb)
self.refresh_proxy_components()
return advanced_vbox
def switch_to_page(self, page_id):
self.nb.set_current_page(page_id)
def details_cb(self, button, parent, protocol):
self.save_proxy_data()
dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
user = self.configuration.proxies[protocol][1],
passwd = self.configuration.proxies[protocol][2],
parent = parent,
flags = gtk.DIALOG_MODAL
| gtk.DIALOG_DESTROY_WITH_PARENT
| gtk.DIALOG_NO_SEPARATOR)
dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.configuration.proxies[protocol][1] = dialog.user
self.configuration.proxies[protocol][2] = dialog.passwd
self.refresh_proxy_components()
dialog.destroy()
def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
combo_item = self.rootfs_combo.get_active_text()
for child in check_hbox.get_children():
if isinstance(child, gtk.CheckButton):
check_hbox.remove(child)
for format in all_package_format:
if format != combo_item:
check_button = gtk.CheckButton(format)
check_hbox.pack_start(check_button, expand=False, fill=False)
check_hbox.show_all()
def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
pkgfmt_hbox = gtk.HBox(False, 24)
rootfs_vbox = gtk.VBox(False, 6)
pkgfmt_hbox.pack_start(rootfs_vbox, expand=False, fill=False)
label = self.gen_label_widget("Root file system package format")
rootfs_vbox.pack_start(label, expand=False, fill=False)
rootfs_format = ""
if curr_package_format:
rootfs_format = curr_package_format.split()[0]
rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
rootfs_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
extra_vbox = gtk.VBox(False, 6)
pkgfmt_hbox.pack_start(extra_vbox, expand=False, fill=False)
label = self.gen_label_widget("Additional package formats")
extra_vbox.pack_start(label, expand=False, fill=False)
check_hbox = gtk.HBox(False, 12)
extra_vbox.pack_start(check_hbox, expand=False, fill=False)
for format in all_package_format:
if format != rootfs_format:
check_button = gtk.CheckButton(format)
is_active = (format in curr_package_format.split())
check_button.set_active(is_active)
check_hbox.pack_start(check_button, expand=False, fill=False)
info = HobInfoButton(tooltip_extra, self)
check_hbox.pack_end(info, expand=False, fill=False)
rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
pkgfmt_hbox.show_all()
return pkgfmt_hbox, rootfs_combo, check_hbox
def editable_settings_cell_edited(self, cell, path_string, new_text, model):
it = model.get_iter_from_string(path_string)
column = cell.get_data("column")
model.set(it, column, new_text)
def editable_settings_add_item_clicked(self, button, model):
new_item = ["##KEY##", "##VALUE##"]
iter = model.append()
model.set (iter,
0, new_item[0],
1, new_item[1],
)
def editable_settings_remove_item_clicked(self, button, treeview):
selection = treeview.get_selection()
model, iter = selection.get_selected()
if iter:
path = model.get_path(iter)[0]
model.remove(iter)
def gen_editable_settings(self, setting, tooltip=""):
setting_hbox = gtk.HBox(False, 12)
vbox = gtk.VBox(False, 12)
setting_hbox.pack_start(vbox, expand=True, fill=True)
setting_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
for key in setting.keys():
setting_store.set(setting_store.append(), 0, key, 1, setting[key])
setting_tree = gtk.TreeView(setting_store)
setting_tree.set_headers_visible(True)
setting_tree.set_size_request(300, 100)
col = gtk.TreeViewColumn('Key')
col.set_min_width(100)
col.set_max_width(150)
col.set_resizable(True)
col1 = gtk.TreeViewColumn('Value')
col1.set_min_width(100)
col1.set_max_width(150)
col1.set_resizable(True)
setting_tree.append_column(col)
setting_tree.append_column(col1)
cell = gtk.CellRendererText()
cell.set_property('width-chars', 10)
cell.set_property('editable', True)
cell.set_data("column", 0)
cell.connect("edited", self.editable_settings_cell_edited, setting_store)
cell1 = gtk.CellRendererText()
cell1.set_property('width-chars', 10)
cell1.set_property('editable', True)
cell1.set_data("column", 1)
cell1.connect("edited", self.editable_settings_cell_edited, setting_store)
col.pack_start(cell, True)
col1.pack_end(cell1, True)
col.set_attributes(cell, text=0)
col1.set_attributes(cell1, text=1)
scroll = gtk.ScrolledWindow()
scroll.set_shadow_type(gtk.SHADOW_IN)
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.add(setting_tree)
vbox.pack_start(scroll, expand=True, fill=True)
# some buttons
hbox = gtk.HBox(True, 6)
vbox.pack_start(hbox, False, False)
button = gtk.Button(stock=gtk.STOCK_ADD)
button.connect("clicked", self.editable_settings_add_item_clicked, setting_store)
hbox.pack_start(button)
button = gtk.Button(stock=gtk.STOCK_REMOVE)
button.connect("clicked", self.editable_settings_remove_item_clicked, setting_tree)
hbox.pack_start(button)
info = HobInfoButton(tooltip, self)
setting_hbox.pack_start(info, expand=False, fill=False)
return setting_hbox, setting_store
def create_others_page(self):
advanced_vbox = gtk.VBox(False, 6)
advanced_vbox.set_border_width(6)
sub_vbox = gtk.VBox(False, 6)
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, tooltip)
sub_vbox.pack_start(label, expand=False, fill=False)
sub_vbox.pack_start(setting_widget, expand=True, fill=True)
return advanced_vbox
def create_visual_elements(self):
self.nb = gtk.Notebook()
self.nb.set_show_tabs(True)
self.nb.append_page(self.create_build_environment_page(), gtk.Label("Build environment"))
self.nb.append_page(self.create_shared_state_page(), gtk.Label("Shared state"))
self.nb.append_page(self.create_network_page(), gtk.Label("Network"))
self.nb.append_page(self.create_others_page(), gtk.Label("Others"))
self.nb.set_current_page(0)
self.vbox.pack_start(self.nb, expand=True, fill=True)
self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
self.show_all()
def destroy(self):
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)

View File

@@ -30,7 +30,6 @@ class HobColors:
BLACK = "#000000"
PALE_BLUE = "#53b8ff"
DEEP_RED = "#aa3e3e"
KHAKI = "#fff68f"
OK = WHITE
RUNNING = PALE_GREEN

View File

@@ -22,6 +22,7 @@
import gobject
import logging
from bb.ui.crumbs.runningbuild import RunningBuild
from bb.ui.crumbs.hobwidget import hcc
class HobHandler(gobject.GObject):
@@ -41,12 +42,6 @@ class HobHandler(gobject.GObject):
"command-failed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
"parsing-warning" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
"sanity-failed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING, gobject.TYPE_INT)),
"generating-data" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
@@ -62,23 +57,10 @@ class HobHandler(gobject.GObject):
"parsing-completed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_PYOBJECT,)),
"recipe-populated" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
"package-populated" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
"network-passed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
"network-failed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
}
(GENERATE_CONFIGURATION, GENERATE_RECIPES, GENERATE_PACKAGES, GENERATE_IMAGE, POPULATE_PACKAGEINFO, SANITY_CHECK, NETWORK_TEST) = range(7)
(SUB_PATH_LAYERS, SUB_FILES_DISTRO, SUB_FILES_MACH, SUB_FILES_SDKMACH, SUB_MATCH_CLASS, SUB_PARSE_CONFIG, SUB_SANITY_CHECK,
SUB_GNERATE_TGTS, SUB_GENERATE_PKGINFO, SUB_BUILD_RECIPES, SUB_BUILD_IMAGE, SUB_NETWORK_TEST) = range(12)
(GENERATE_CONFIGURATION, GENERATE_RECIPES, GENERATE_PACKAGES, GENERATE_IMAGE, POPULATE_PACKAGEINFO, SANITY_CHECK) = range(6)
(SUB_PATH_LAYERS, SUB_FILES_DISTRO, SUB_FILES_MACH, SUB_FILES_SDKMACH, SUB_MATCH_CLASS, SUB_PARSE_CONFIG, SUB_SANITY_CHECK, SUB_GNERATE_TGTS, SUB_GENERATE_PKGINFO, SUB_BUILD_RECIPES, SUB_BUILD_IMAGE) = range(11)
def __init__(self, server, recipe_model, package_model):
super(HobHandler, self).__init__()
@@ -98,7 +80,6 @@ class HobHandler(gobject.GObject):
self.server = server
self.error_msg = ""
self.initcmd = None
self.parsing = False
def set_busy(self):
if not self.generating:
@@ -112,10 +93,7 @@ class HobHandler(gobject.GObject):
def runCommand(self, commandline):
try:
result, error = self.server.runCommand(commandline)
if error:
raise Exception("Error running command '%s': %s" % (commandline, error))
return result
return self.server.runCommand(commandline)
except Exception as e:
self.commands_async = []
self.clear_busy()
@@ -153,8 +131,6 @@ class HobHandler(gobject.GObject):
self.runCommand(["triggerEvent", "bb.event.RequestPackageInfo()"])
elif next_command == self.SUB_SANITY_CHECK:
self.runCommand(["triggerEvent", "bb.event.SanityCheck()"])
elif next_command == self.SUB_NETWORK_TEST:
self.runCommand(["triggerEvent", "bb.event.NetworkTest()"])
elif next_command == self.SUB_BUILD_RECIPES:
self.clear_busy()
self.building = True
@@ -170,58 +146,31 @@ class HobHandler(gobject.GObject):
if self.toolchain_packages:
self.runCommand(["setVariable", "TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages)])
targets.append(self.toolchain)
if targets[0] == "hob-image":
hobImage = self.runCommand(["matchFile", "hob-image.bb"])
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])
targets[0] += version
self.recipe_model.set_custom_image_version(version)
self.runCommand(["buildTargets", targets, self.default_task])
def display_error(self):
self.clear_busy()
self.emit("command-failed", self.error_msg)
self.error_msg = ""
if self.building:
self.building = False
def handle_event(self, event):
if not event:
return
if self.building:
self.current_phase = "building"
self.build.handle_event(event)
if isinstance(event, bb.event.PackageInfo):
self.package_model.populate(event._pkginfolist)
self.emit("package-populated")
self.run_next_command()
elif isinstance(event, bb.event.SanityCheckPassed):
self.run_next_command()
elif isinstance(event, bb.event.SanityCheckFailed):
self.emit("sanity-failed", event._msg, event._network_error)
elif isinstance(event, logging.LogRecord):
if not self.building:
if event.levelno >= logging.ERROR:
formatter = bb.msg.BBLogFormatter()
msg = formatter.format(event)
self.error_msg += msg + '\n'
elif event.levelno >= logging.WARNING and self.parsing == True:
formatter = bb.msg.BBLogFormatter()
msg = formatter.format(event)
warn_msg = msg + '\n'
self.emit("parsing-warning", warn_msg)
if event.levelno >= logging.ERROR:
self.error_msg += event.msg + '\n'
elif isinstance(event, bb.event.TargetsTreeGenerated):
self.current_phase = "data generation"
if event._model:
self.recipe_model.populate(event._model)
self.emit("recipe-populated")
elif isinstance(event, bb.event.ConfigFilesFound):
self.current_phase = "configuration lookup"
var = event._variable
@@ -247,7 +196,9 @@ class HobHandler(gobject.GObject):
self.run_next_command()
elif isinstance(event, bb.command.CommandFailed):
self.commands_async = []
self.display_error()
self.clear_busy()
self.emit("command-failed", self.error_msg)
self.error_msg = ""
elif isinstance(event, (bb.event.ParseStarted,
bb.event.CacheLoadStarted,
bb.event.TreeDataPreparationStarted,
@@ -256,10 +207,8 @@ class HobHandler(gobject.GObject):
message["eventname"] = bb.event.getName(event)
message["current"] = 0
message["total"] = None
message["title"] = "Parsing recipes"
message["title"] = "Parsing recipes: "
self.emit("parsing-started", message)
if isinstance(event, bb.event.ParseStarted):
self.parsing = True
elif isinstance(event, (bb.event.ParseProgress,
bb.event.CacheLoadProgress,
bb.event.TreeDataPreparationProgress)):
@@ -267,7 +216,7 @@ class HobHandler(gobject.GObject):
message["eventname"] = bb.event.getName(event)
message["current"] = event.current
message["total"] = event.total
message["title"] = "Parsing recipes"
message["title"] = "Parsing recipes: "
self.emit("parsing", message)
elif isinstance(event, (bb.event.ParseCompleted,
bb.event.CacheLoadCompleted,
@@ -276,19 +225,8 @@ class HobHandler(gobject.GObject):
message["eventname"] = bb.event.getName(event)
message["current"] = event.total
message["total"] = event.total
message["title"] = "Parsing recipes"
message["title"] = "Parsing recipes: "
self.emit("parsing-completed", message)
if isinstance(event, bb.event.ParseCompleted):
self.parsing = False
elif isinstance(event, bb.event.NetworkTestFailed):
self.emit("network-failed")
self.run_next_command()
elif isinstance(event, bb.event.NetworkTestPassed):
self.emit("network-passed")
self.run_next_command()
if self.error_msg and not self.commands_async:
self.display_error()
return
@@ -335,8 +273,8 @@ class HobHandler(gobject.GObject):
def set_sstate_dir(self, directory):
self.runCommand(["setVariable", "SSTATE_DIR_HOB", directory])
def set_sstate_mirrors(self, url):
self.runCommand(["setVariable", "SSTATE_MIRRORS_HOB", url])
def set_sstate_mirror(self, url):
self.runCommand(["setVariable", "SSTATE_MIRROR_HOB", url])
def set_extra_size(self, image_extra_size):
self.runCommand(["setVariable", "IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size)])
@@ -364,6 +302,9 @@ class HobHandler(gobject.GObject):
def set_ftp_proxy(self, ftp_proxy):
self.runCommand(["setVariable", "ftp_proxy", ftp_proxy])
def set_all_proxy(self, all_proxy):
self.runCommand(["setVariable", "all_proxy", all_proxy])
def set_git_proxy(self, host, port):
self.runCommand(["setVariable", "GIT_PROXY_HOST", host])
self.runCommand(["setVariable", "GIT_PROXY_PORT", port])
@@ -380,10 +321,6 @@ class HobHandler(gobject.GObject):
self.commands_async.append(self.SUB_SANITY_CHECK)
self.run_next_command(self.SANITY_CHECK)
def trigger_network_test(self):
self.commands_async.append(self.SUB_NETWORK_TEST)
self.run_next_command(self.NETWORK_TEST)
def generate_configuration(self):
self.commands_async.append(self.SUB_PARSE_CONFIG)
self.commands_async.append(self.SUB_PATH_LAYERS)
@@ -397,7 +334,7 @@ class HobHandler(gobject.GObject):
self.commands_async.append(self.SUB_PARSE_CONFIG)
self.commands_async.append(self.SUB_GNERATE_TGTS)
self.run_next_command(self.GENERATE_RECIPES)
def generate_packages(self, tgts, default_task="build"):
targets = []
targets.extend(tgts)
@@ -407,9 +344,8 @@ class HobHandler(gobject.GObject):
self.commands_async.append(self.SUB_BUILD_RECIPES)
self.run_next_command(self.GENERATE_PACKAGES)
def generate_image(self, image, base_image, toolchain, image_packages=[], toolchain_packages=[], default_task="build"):
def generate_image(self, image, toolchain, image_packages=[], toolchain_packages=[], default_task="build"):
self.image = image
self.base_image = base_image
self.toolchain = toolchain
self.package_queue = image_packages
self.toolchain_packages = toolchain_packages
@@ -441,9 +377,6 @@ class HobHandler(gobject.GObject):
def reset_build(self):
self.build.reset()
def get_logfile(self):
return self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
def _remove_redundant(self, string):
ret = []
for i in string.split():
@@ -457,17 +390,14 @@ class HobHandler(gobject.GObject):
params["core_base"] = self.runCommand(["getVariable", "COREBASE"]) or ""
hob_layer = params["core_base"] + "/meta-hob"
params["layer"] = self.runCommand(["getVariable", "BBLAYERS"]) or ""
params["layers_non_removable"] = self.runCommand(["getVariable", "BBLAYERS_NON_REMOVABLE"]) or ""
if hob_layer not in params["layer"].split():
params["layer"] += (" " + hob_layer)
if hob_layer not in params["layers_non_removable"].split():
params["layers_non_removable"] += (" " + hob_layer)
params["dldir"] = self.runCommand(["getVariable", "DL_DIR"]) or ""
params["machine"] = self.runCommand(["getVariable", "MACHINE"]) or ""
params["distro"] = self.runCommand(["getVariable", "DISTRO"]) or "defaultsetup"
params["pclass"] = self.runCommand(["getVariable", "PACKAGE_CLASSES"]) or ""
params["sstatedir"] = self.runCommand(["getVariable", "SSTATE_DIR"]) or ""
params["sstatemirror"] = self.runCommand(["getVariable", "SSTATE_MIRRORS"]) or ""
params["sstatemirror"] = self.runCommand(["getVariable", "SSTATE_MIRROR"]) or ""
num_threads = self.runCommand(["getCpuCount"])
if not num_threads:
@@ -549,7 +479,6 @@ class HobHandler(gobject.GObject):
params["runnable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_IMAGE_TYPES"]) or "")
params["runnable_machine_patterns"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_MACHINE_PATTERNS"]) or "")
params["deployable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "DEPLOYABLE_IMAGE_TYPES"]) or "")
params["kernel_image_type"] = self.runCommand(["getVariable", "KERNEL_IMAGETYPE"]) or ""
params["tmpdir"] = self.runCommand(["getVariable", "TMPDIR"]) or ""
params["distro_version"] = self.runCommand(["getVariable", "DISTRO_VERSION"]) or ""
params["target_os"] = self.runCommand(["getVariable", "TARGET_OS"]) or ""
@@ -565,10 +494,9 @@ class HobHandler(gobject.GObject):
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 ""
params["all_proxy"] = self.runCommand(["getVariable", "all_proxy"]) or ""
params["cvs_proxy_host"] = self.runCommand(["getVariable", "CVS_PROXY_HOST"]) or ""
params["cvs_proxy_port"] = self.runCommand(["getVariable", "CVS_PROXY_PORT"]) or ""
params["image_white_pattern"] = self.runCommand(["getVariable", "BBUI_IMAGE_WHITE_PATTERN"]) or ""
params["image_black_pattern"] = self.runCommand(["getVariable", "BBUI_IMAGE_BLACK_PATTERN"]) or ""
return params

View File

@@ -34,7 +34,7 @@ class PackageListModel(gtk.TreeStore):
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_BINB, COL_INC, COL_FADE_INC, COL_FONT) = range(13)
(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) = range(12)
__gsignals__ = {
"package-selection-changed" : (gobject.SIGNAL_RUN_LAST,
@@ -42,7 +42,7 @@ class PackageListModel(gtk.TreeStore):
()),
}
__toolchain_required_packages__ = ["packagegroup-core-standalone-sdk-target", "packagegroup-core-standalone-sdk-target-dbg"]
__toolchain_required_packages__ = ["task-core-standalone-sdk-target", "task-core-standalone-sdk-target-dbg"]
def __init__(self):
@@ -65,8 +65,7 @@ class PackageListModel(gtk.TreeStore):
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_BOOLEAN,
gobject.TYPE_BOOLEAN,
gobject.TYPE_STRING)
gobject.TYPE_BOOLEAN)
"""
@@ -145,12 +144,6 @@ class PackageListModel(gtk.TreeStore):
self.pkg_path = {}
self.rprov_pkg = {}
def getpkgvalue(pkgdict, key, pkgname, defaultval = None):
value = pkgdict.get('%s_%s' % (key, pkgname), None)
if not value:
value = pkgdict.get(key, defaultval)
return value
for pkginfo in pkginfolist:
pn = pkginfo['PN']
pv = pkginfo['PV']
@@ -163,24 +156,25 @@ class PackageListModel(gtk.TreeStore):
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)
# PKGSIZE is artificial, will always be overridden with the package name if present
pkgsize = pkginfo.get('PKGSIZE_%s' % pkg, "0")
# PKG_%s is the renamed version
pkg_rename = pkginfo.get('PKG_%s' % pkg, "")
# The rest may be overridden or not
section = getpkgvalue(pkginfo, 'SECTION', pkg, "")
summary = getpkgvalue(pkginfo, 'SUMMARY', pkg, "")
rdep = getpkgvalue(pkginfo, 'RDEPENDS', pkg, "")
rrec = getpkgvalue(pkginfo, 'RRECOMMENDS', pkg, "")
rprov = getpkgvalue(pkginfo, 'RPROVIDES', pkg, "")
pkgv = pkginfo['PKGV']
pkgr = pkginfo['PKGR']
pkgsize = pkginfo['PKGSIZE_%s' % pkg] if 'PKGSIZE_%s' % pkg in pkginfo.keys() else "0"
pkg_rename = pkginfo['PKG_%s' % pkg] if 'PKG_%s' % pkg in pkginfo.keys() else ""
section = pkginfo['SECTION_%s' % pkg] if 'SECTION_%s' % pkg in pkginfo.keys() else ""
summary = pkginfo['SUMMARY_%s' % pkg] if 'SUMMARY_%s' % pkg in pkginfo.keys() else ""
rdep = pkginfo['RDEPENDS_%s' % pkg] if 'RDEPENDS_%s' % pkg in pkginfo.keys() else ""
rrec = pkginfo['RRECOMMENDS_%s' % pkg] if 'RRECOMMENDS_%s' % pkg in pkginfo.keys() else ""
rprov = pkginfo['RPROVIDES_%s' % pkg] if 'RPROVIDES_%s' % pkg in pkginfo.keys() else ""
for i in rprov.split():
self.rprov_pkg[i] = pkg
allow_empty = getpkgvalue(pkginfo, 'ALLOW_EMPTY', pkg, "")
if 'ALLOW_EMPTY_%s' % pkg in pkginfo.keys():
allow_empty = pkginfo['ALLOW_EMPTY_%s' % pkg]
elif 'ALLOW_EMPTY' in pkginfo.keys():
allow_empty = pkginfo['ALLOW_EMPTY']
else:
allow_empty = ""
if pkgsize == "0" and not allow_empty:
continue
@@ -195,7 +189,7 @@ class PackageListModel(gtk.TreeStore):
self.COL_SEC, section, self.COL_SUM, summary,
self.COL_RDEP, rdep + ' ' + rrec,
self.COL_RPROV, rprov, self.COL_SIZE, size,
self.COL_BINB, "", self.COL_INC, False, self.COL_FONT, '10')
self.COL_BINB, "", self.COL_INC, False)
"""
Check whether the item at item_path is included or not
@@ -337,13 +331,13 @@ class PackageListModel(gtk.TreeStore):
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):
def set_selected_packages(self, packagelist):
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)
self.include_item(item_path=path,
binb="User Selected")
else:
left.append(pn)
@@ -359,7 +353,7 @@ class PackageListModel(gtk.TreeStore):
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":
if not binb or binb == "User Selected":
name = self.get_value(child_it, self.COL_NAME)
packagelist.append(name)
child_it = self.iter_next(child_it)
@@ -461,7 +455,7 @@ class RecipeListModel(gtk.ListStore):
"""
(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__ = "Create your own image"
__dummy_image__ = "Create your own image"
__gsignals__ = {
"recipe-selection-changed" : (gobject.SIGNAL_RUN_LAST,
@@ -526,24 +520,17 @@ class RecipeListModel(gtk.ListStore):
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
return ((val1 == True) and (val2 == False))
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 which are items specified by filter
"""
def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=True):
def tree_model(self, filter, excluded_items_ahead=False):
model = self.filter_new()
model.set_visible_func(self.tree_model_filter, filter)
sort = gtk.TreeModelSort(model)
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)
@@ -577,13 +564,14 @@ class RecipeListModel(gtk.ListStore):
self.clear()
# dummy image for prompt
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.set(self.append(), self.COL_NAME, self.__dummy_image__,
self.COL_DESC, "Use the 'View recipes' and 'View packages' " \
"options to select what you want to include " \
"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__)
self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, self.__dummy_image__)
for item in event_model["pn"]:
name = item
@@ -595,8 +583,8 @@ class RecipeListModel(gtk.ListStore):
depends = event_model["depends"].get(item, []) + event_model["rdepends-pn"].get(item, [])
if ('packagegroup.bbclass' in " ".join(inherits)):
atype = 'packagegroup'
if ('task-' in name):
atype = 'task'
elif ('image.bbclass' in " ".join(inherits)):
if name != "hob-image":
atype = 'image'
@@ -672,10 +660,6 @@ class RecipeListModel(gtk.ListStore):
self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
elif not dep_included:
self.include_item(dep_path, binb=item_name, image_contents=image_contents)
dep_bin = self[item_path][self.COL_BINB].split(', ')
if self[item_path][self.COL_NAME] in dep_bin:
dep_bin.remove(self[item_path][self.COL_NAME])
self[item_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
def exclude_item(self, item_path):
if not self.path_included(item_path):
@@ -766,9 +750,3 @@ class RecipeListModel(gtk.ListStore):
binb="User Selected",
image_contents=True)
self.selection_change_notification()
def set_custom_image_version(self, version):
self.custom_image_version = version
def get_custom_image_version(self):
return self.custom_image_version

View File

@@ -38,7 +38,6 @@ class HobPage (gtk.VBox):
self.title = "Hob -- Image Creator"
else:
self.title = title
self.title_label = gtk.Label()
self.box_group_area = gtk.VBox(False, 12)
self.box_group_area.set_size_request(self.builder_width - 73 - 73, self.builder_height - 88 - 15 - 15)
@@ -47,9 +46,6 @@ class HobPage (gtk.VBox):
self.group_align.add(self.box_group_area)
self.box_group_area.set_homogeneous(False)
def set_title(self, title):
self.title = title
self.title_label.set_markup("<span size='x-large'>%s</span>" % self.title)
def add_onto_top_bar(self, widget = None, padding = 0):
# the top button occupies 1/7 of the page height
@@ -62,9 +58,9 @@ class HobPage (gtk.VBox):
hbox = gtk.HBox()
self.title_label = gtk.Label()
self.title_label.set_markup("<span size='x-large'>%s</span>" % self.title)
hbox.pack_start(self.title_label, expand=False, fill=False, padding=20)
label = gtk.Label()
label.set_markup("<span size='x-large'>%s</span>" % self.title)
hbox.pack_start(label, expand=False, fill=False, padding=20)
if widget:
# add the widget in the event box

View File

@@ -23,7 +23,6 @@ import os
import os.path
import sys
import pango, pangocairo
import cairo
import math
from bb.ui.crumbs.hobcolor import HobColors
@@ -63,6 +62,34 @@ class hic:
ICON_INDI_TICK_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/tick.png'))
ICON_INDI_INFO_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/info.png'))
class hcc:
SUPPORTED_IMAGE_TYPES = {
"jffs2" : ["jffs2"],
"sum.jffs2" : ["sum.jffs2"],
"cramfs" : ["cramfs"],
"ext2" : ["ext2"],
"ext2.gz" : ["ext2.gz"],
"ext2.bz2" : ["ext2.bz2"],
"ext3" : ["ext3"],
"ext3.gz" : ["ext3.gz"],
"ext2.lzma" : ["ext2.lzma"],
"btrfs" : ["btrfs"],
"live" : ["hddimg", "iso"],
"squashfs" : ["squashfs"],
"squashfs-lzma" : ["squashfs-lzma"],
"ubi" : ["ubi"],
"tar" : ["tar"],
"tar.gz" : ["tar.gz"],
"tar.bz2" : ["tar.bz2"],
"tar.xz" : ["tar.xz"],
"cpio" : ["cpio"],
"cpio.gz" : ["cpio.gz"],
"cpio.xz" : ["cpio.xz"],
"vmdk" : ["vmdk"],
"cpio.lzma" : ["cpio.lzma"],
}
class HobViewTable (gtk.VBox):
"""
A VBox to contain the table for different recipe views and package view
@@ -92,7 +119,6 @@ class HobViewTable (gtk.VBox):
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)
@@ -114,8 +140,6 @@ class HobViewTable (gtk.VBox):
cell = gtk.CellRendererText()
col.pack_start(cell, True)
col.set_attributes(cell, text=column['col_id'])
if 'col_t_id' in column.keys():
col.add_attribute(cell, 'font', column['col_t_id'])
elif column['col_style'] == 'check toggle':
cell = HobCellRendererToggle()
cell.set_property('activatable', True)
@@ -125,8 +149,6 @@ class HobViewTable (gtk.VBox):
col.pack_end(cell, True)
col.set_attributes(cell, active=column['col_id'])
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':
cell = gtk.CellRendererToggle()
cell.set_property('activatable', True)
@@ -140,8 +162,6 @@ class HobViewTable (gtk.VBox):
cell = gtk.CellRendererText()
col.pack_start(cell, True)
col.set_cell_data_func(cell, self.display_binb_cb, column['col_id'])
if 'col_t_id' in column.keys():
col.add_attribute(cell, 'font', column['col_t_id'])
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
@@ -153,18 +173,7 @@ class HobViewTable (gtk.VBox):
# Just display the first item
if binb:
bin = binb.split(', ')
total_no = len(bin)
if total_no > 1 and bin[0] == "User Selected":
if total_no > 2:
present_binb = bin[1] + ' (+' + str(total_no - 1) + ')'
else:
present_binb = bin[1]
else:
if total_no > 1:
present_binb = bin[0] + ' (+' + str(total_no - 1) + ')'
else:
present_binb = bin[0]
cell.set_property('text', present_binb)
cell.set_property('text', bin[0])
else:
cell.set_property('text', "")
return True
@@ -195,15 +204,6 @@ class HobViewTable (gtk.VBox):
def stop_cell_fadeinout_cb(self, ctrl, cell, tree):
self.emit("cell-fadeinout-stopped", ctrl, cell, tree)
def set_group_number_cb(self, col, cell, model, iter):
if model and (model.iter_parent(iter) == None):
cell.cell_attr["number_of_children"] = model.iter_n_children(iter)
else:
cell.cell_attr["number_of_children"] = 0
def connect_group_selection(self, cb_func):
self.table_tree.get_selection().connect("changed", cb_func)
"""
A method to calculate a softened value for the colour of widget when in the
provided state.
@@ -225,7 +225,7 @@ def soften_color(widget, state=gtk.STATE_NORMAL):
color.blue = color.blue * blend + style.base[state].blue * (1.0 - blend)
return color.to_string()
class BaseHobButton(gtk.Button):
class HobButton(gtk.Button):
"""
A gtk.Button subclass which follows the visual design of Hob for primary
action buttons
@@ -239,33 +239,24 @@ class BaseHobButton(gtk.Button):
@staticmethod
def style_button(button):
style = button.get_style()
style = gtk.rc_get_style_by_paths(gtk.settings_get_default(), 'gtk-button', 'gtk-button', gobject.TYPE_NONE)
button_color = gtk.gdk.Color(HobColors.ORANGE)
button.modify_bg(gtk.STATE_NORMAL, button_color)
button.modify_bg(gtk.STATE_PRELIGHT, button_color)
button.modify_bg(gtk.STATE_SELECTED, button_color)
button.set_flags(gtk.CAN_DEFAULT)
button.grab_default()
# label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
label = button.get_label()
label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
button.set_label(label)
button.child.set_use_markup(True)
class HobButton(BaseHobButton):
"""
A gtk.Button subclass which follows the visual design of Hob for primary
action buttons
label: the text to display as the button's label
"""
def __init__(self, label):
BaseHobButton.__init__(self, label)
HobButton.style_button(self)
class HobAltButton(BaseHobButton):
class HobAltButton(gtk.Button):
"""
A gtk.Button subclass which has no relief, and so is more discrete
"""
def __init__(self, label):
BaseHobButton.__init__(self, label)
gtk.Button.__init__(self, label)
HobAltButton.style_button(self)
"""
@@ -291,6 +282,14 @@ class HobAltButton(BaseHobButton):
button.set_label("<span size='large' color='%s'><b>%s</b></span>" % (colour, gobject.markup_escape_text(button.text)))
button.child.set_use_markup(True)
@staticmethod
def style_button(button):
button.text = button.get_label()
button.connect("state-changed", HobAltButton.desensitise_on_state_change_cb)
HobAltButton.set_text(button)
button.child.set_use_markup(True)
button.set_relief(gtk.RELIEF_NONE)
class HobImageButton(gtk.Button):
"""
A gtk.Button with an icon and two rows of text, the second of which is
@@ -343,8 +342,7 @@ class HobInfoButton(gtk.EventBox):
def __init__(self, tip_markup, parent=None):
gtk.EventBox.__init__(self)
self.image = gtk.Image()
self.image.set_from_file(
hic.ICON_INFO_DISPLAY_FILE)
self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
self.image.show()
self.add(self.image)
@@ -382,95 +380,363 @@ class HobInfoButton(gtk.EventBox):
def mouse_out_cb(self, widget, event):
self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
class HobIndicator(gtk.DrawingArea):
def __init__(self, count):
gtk.DrawingArea.__init__(self)
# Set no window for transparent background
self.set_has_window(False)
self.set_size_request(38,38)
# We need to pass through button clicks
self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
class HobTabBar(gtk.DrawingArea):
__gsignals__ = {
"blank-area-changed" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_INT,
gobject.TYPE_INT,
gobject.TYPE_INT,
gobject.TYPE_INT,)),
self.connect('expose-event', self.expose)
"tab-switched" : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_INT,)),
}
self.count = count
self.color = HobColors.GRAY
def expose(self, widget, event):
if self.count and self.count > 0:
ctx = widget.window.cairo_create()
x, y, w, h = self.allocation
ctx.set_operator(cairo.OPERATOR_OVER)
ctx.set_source_color(gtk.gdk.color_parse(self.color))
ctx.translate(w/2, h/2)
ctx.arc(x, y, min(w,h)/2 - 2, 0, 2*math.pi)
ctx.fill_preserve()
layout = self.create_pango_layout(str(self.count))
textw, texth = layout.get_pixel_size()
x = (w/2)-(textw/2) + x
y = (h/2) - (texth/2) + y
ctx.move_to(x, y)
self.window.draw_layout(self.style.light_gc[gtk.STATE_NORMAL], int(x), int(y), layout)
def set_count(self, count):
self.count = count
def set_active(self, active):
if active:
self.color = HobColors.DEEP_RED
else:
self.color = HobColors.GRAY
class HobTabLabel(gtk.HBox):
def __init__(self, text, count=0):
gtk.HBox.__init__(self, False, 0)
self.indicator = HobIndicator(count)
self.indicator.show()
self.pack_end(self.indicator, False, False)
self.lbl = gtk.Label(text)
self.lbl.set_alignment(0.0, 0.5)
self.lbl.show()
self.pack_end(self.lbl, True, True, 6)
def set_count(self, count):
self.indicator.set_count(count)
def set_active(self, active=True):
self.indicator.set_active(active)
class HobNotebook(gtk.Notebook):
def __init__(self):
gtk.Notebook.__init__(self)
self.set_property('homogeneous', True)
gtk.DrawingArea.__init__(self)
self.children = []
self.pages = []
self.tab_width = 140
self.tab_height = 52
self.tab_x = 10
self.tab_y = 0
self.width = 500
self.height = 53
self.tab_w_ratio = 140 * 1.0/500
self.tab_h_ratio = 52 * 1.0/53
self.set_size_request(self.width, self.height)
self.current_child = None
self.font = self.get_style().font_desc
self.font.set_size(pango.SCALE * 13)
self.update_children_text_layout_and_bg_color()
self.blank_rectangle = None
self.tab_pressed = False
self.set_property('can-focus', True)
self.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK)
self.connect("expose-event", self.on_draw)
self.connect("button-press-event", self.button_pressed_cb)
self.connect("button-release-event", self.button_released_cb)
self.connect("query-tooltip", self.query_tooltip_cb)
self.show_all()
def button_released_cb(self, widget, event):
self.tab_pressed = False
self.queue_draw()
def button_pressed_cb(self, widget, event):
if event.type == gtk.gdk._2BUTTON_PRESS:
return
result = False
if self.is_focus() or event.type == gtk.gdk.BUTTON_PRESS:
x, y = event.get_coords()
# check which tab be clicked
for child in self.children:
if (child["x"] < x) and (x < child["x"] + self.tab_width) \
and (child["y"] < y) and (y < child["y"] + self.tab_height):
self.current_child = child
result = True
self.grab_focus()
break
# check the blank area is focus in or not
if (self.blank_rectangle) and (self.blank_rectangle.x > 0) and (self.blank_rectangle.y > 0):
if (self.blank_rectangle.x < x) and (x < self.blank_rectangle.x + self.blank_rectangle.width) \
and (self.blank_rectangle.y < y) and (y < self.blank_rectangle.y + self.blank_rectangle.height):
self.grab_focus()
if result == True:
page = self.current_child["toggled_page"]
self.emit("tab-switched", page)
self.tab_pressed = True
self.queue_draw()
def update_children_size(self):
# calculate the size of tabs
self.tab_width = int(self.width * self.tab_w_ratio)
self.tab_height = int(self.height * self.tab_h_ratio)
for i, child in enumerate(self.children):
child["x"] = self.tab_x + i * self.tab_width
child["y"] = self.tab_y
if self.blank_rectangle:
self.resize_blank_rectangle()
def resize_blank_rectangle(self):
width = self.width - self.tab_width * len(self.children) - self.tab_x
x = self.tab_x + self.tab_width * len(self.children)
hpadding = vpadding = 5
self.blank_rectangle = self.set_blank_size(x + hpadding, self.tab_y + vpadding,
width - 2 * hpadding, self.tab_height - 2 * vpadding)
def update_children_text_layout_and_bg_color(self):
style = self.get_style().copy()
color = style.base[gtk.STATE_NORMAL]
for child in self.children:
pangolayout = self.create_pango_layout(child["title"])
pangolayout.set_font_description(self.font)
child["title_layout"] = pangolayout
child["r"] = color.red
child["g"] = color.green
child["b"] = color.blue
def append_tab_child(self, title, page, tooltip=""):
num = len(self.children) + 1
self.tab_width = self.tab_width * len(self.children) / num
i = 0
for i, child in enumerate(self.children):
child["x"] = self.tab_x + i * self.tab_width
i += 1
x = self.tab_x + i * self.tab_width
y = self.tab_y
pangolayout = self.create_pango_layout(title)
pangolayout.set_font_description(self.font)
color = self.style.base[gtk.STATE_NORMAL]
new_one = {
"x" : x,
"y" : y,
"r" : color.red,
"g" : color.green,
"b" : color.blue,
"title_layout" : pangolayout,
"toggled_page" : page,
"title" : title,
"indicator_show" : False,
"indicator_number" : 0,
"tooltip_markup" : tooltip,
}
self.children.append(new_one)
if tooltip and (not self.props.has_tooltip):
self.props.has_tooltip = True
# set the default current child
if not self.current_child:
self.current_child = new_one
def on_draw(self, widget, event):
cr = widget.window.cairo_create()
self.width = self.allocation.width
self.height = self.allocation.height
self.update_children_size()
self.draw_background(cr)
self.draw_toggled_tab(cr)
for child in self.children:
if child["indicator_show"] == True:
self.draw_indicator(cr, child)
self.draw_tab_text(cr)
def draw_background(self, cr):
style = self.get_style()
if self.is_focus():
cr.set_source_color(style.base[gtk.STATE_SELECTED])
else:
cr.set_source_color(style.base[gtk.STATE_NORMAL])
y = 6
h = self.height - 6 - 1
gap = 1
w = self.children[0]["x"]
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
cr.rectangle(0, y, w - gap, h) # start rectangle
cr.fill()
cr.set_source_color(style.base[gtk.STATE_NORMAL])
cr.rectangle(w - gap, y, w, h) #first gap
cr.fill()
w = self.tab_width
for child in self.children:
x = child["x"]
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
cr.rectangle(x, y, w - gap, h) # tab rectangle
cr.fill()
cr.set_source_color(style.base[gtk.STATE_NORMAL])
cr.rectangle(x + w - gap, y, w, h) # gap
cr.fill()
cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
cr.rectangle(x + w, y, self.width - x - w, h) # last rectangle
cr.fill()
def draw_tab_text(self, cr):
style = self.get_style()
for child in self.children:
pangolayout = child["title_layout"]
if pangolayout:
fontw, fonth = pangolayout.get_pixel_size()
# center pos
off_x = (self.tab_width - fontw) / 2
off_y = (self.tab_height - fonth) / 2
x = child["x"] + off_x
y = child["y"] + off_y
if not child == self.current_child:
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), pangolayout, gtk.gdk.Color(HobColors.WHITE))
else:
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), pangolayout)
def draw_toggled_tab(self, cr):
if not self.current_child:
return
x = self.current_child["x"]
y = self.current_child["y"]
width = self.tab_width
height = self.tab_height
style = self.get_style()
color = style.base[gtk.STATE_NORMAL]
r = height / 10
if self.tab_pressed == True:
for xoff, yoff, c1, c2 in [(1, 0, HobColors.SLIGHT_DARK, HobColors.DARK), (2, 0, HobColors.GRAY, HobColors.LIGHT_GRAY)]:
cr.set_source_color(gtk.gdk.color_parse(c1))
cr.move_to(x + xoff, y + height + yoff)
cr.line_to(x + xoff, r + yoff)
cr.arc(x + r + xoff, y + r + yoff, r, math.pi, 1.5*math.pi)
cr.move_to(x + r + xoff, y + yoff)
cr.line_to(x + width - r + xoff, y + yoff)
cr.arc(x + width - r + xoff, y + r + yoff, r, 1.5*math.pi, 2*math.pi)
cr.stroke()
cr.set_source_color(gtk.gdk.color_parse(c2))
cr.move_to(x + width + xoff, r + yoff)
cr.line_to(x + width + xoff, y + height + yoff)
cr.line_to(x + xoff, y + height + yoff)
cr.stroke()
x = x + 2
y = y + 2
cr.set_source_rgba(color.red, color.green, color.blue, 1)
cr.move_to(x + r, y)
cr.line_to(x + width - r , y)
cr.arc(x + width - r, y + r, r, 1.5*math.pi, 2*math.pi)
cr.move_to(x + width, r)
cr.line_to(x + width, y + height)
cr.line_to(x, y + height)
cr.line_to(x, r)
cr.arc(x + r, y + r, r, math.pi, 1.5*math.pi)
cr.fill()
def draw_indicator(self, cr, child):
text = ("%d" % child["indicator_number"])
layout = self.create_pango_layout(text)
layout.set_font_description(self.font)
textw, texth = layout.get_pixel_size()
# draw the back round area
tab_x = child["x"]
tab_y = child["y"]
dest_w = int(32 * self.tab_w_ratio)
dest_h = int(32 * self.tab_h_ratio)
if dest_h < self.tab_height:
dest_w = dest_h
# x position is offset(tab_width*3/4 - icon_width/2) + start_pos(tab_x)
x = tab_x + self.tab_width * 3/4 - dest_w/2
y = tab_y + self.tab_height/2 - dest_h/2
r = min(dest_w, dest_h)/2
if not child == self.current_child:
color = cr.set_source_color(gtk.gdk.color_parse(HobColors.DEEP_RED))
else:
color = cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
# check round back area can contain the text or not
back_round_can_contain_width = float(2 * r * 0.707)
if float(textw) > back_round_can_contain_width:
xoff = (textw - int(back_round_can_contain_width)) / 2
cr.move_to(x + r - xoff, y + r + r)
cr.arc((x + r - xoff), (y + r), r, 0.5*math.pi, 1.5*math.pi)
cr.fill() # left half round
cr.rectangle((x + r - xoff), y, 2 * xoff, 2 * r)
cr.fill() # center rectangle
cr.arc((x + r + xoff), (y + r), r, 1.5*math.pi, 0.5*math.pi)
cr.fill() # right half round
else:
cr.arc((x + r), (y + r), r, 0, 2*math.pi)
cr.fill()
# draw the number text
x = x + (dest_w/2)-(textw/2)
y = y + (dest_h/2) - (texth/2)
cr.move_to(x, y)
self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), layout, gtk.gdk.Color(HobColors.WHITE))
def show_indicator_icon(self, child, number):
child["indicator_show"] = True
child["indicator_number"] = number
self.queue_draw()
def hide_indicator_icon(self, child):
child["indicator_show"] = False
self.queue_draw()
def set_blank_size(self, x, y, w, h):
if not self.blank_rectangle or self.blank_rectangle.x != x or self.blank_rectangle.width != w:
self.emit("blank-area-changed", x, y, w, h)
return gtk.gdk.Rectangle(x, y, w, h)
def query_tooltip_cb(self, widget, x, y, keyboardtip, tooltip):
if keyboardtip or (not tooltip):
return False
# check which tab be clicked
for child in self.children:
if (child["x"] < x) and (x < child["x"] + self.tab_width) \
and (child["y"] < y) and (y < child["y"] + self.tab_height):
tooltip.set_markup(child["tooltip_markup"])
return True
return False
class HobNotebook(gtk.VBox):
def __init__(self):
gtk.VBox.__init__(self, False, 0)
self.notebook = gtk.Notebook()
self.notebook.set_property('homogeneous', True)
self.notebook.set_property('show-tabs', False)
self.tabbar = HobTabBar()
self.tabbar.connect("tab-switched", self.tab_switched_cb)
self.notebook.connect("page-added", self.page_added_cb)
self.notebook.connect("page-removed", self.page_removed_cb)
self.search = None
self.search_name = ""
self.connect("switch-page", self.page_changed_cb)
self.tb = gtk.Table(1, 100, False)
self.hbox= gtk.HBox(False, 0)
self.hbox.pack_start(self.tabbar, True, True)
self.tb.attach(self.hbox, 0, 100, 0, 1)
self.pack_start(self.tb, False, False)
self.pack_start(self.notebook)
self.show_all()
def page_changed_cb(self, nb, page, page_num):
for p, lbl in enumerate(self.pages):
if p == page_num:
lbl.set_active()
else:
lbl.set_active(False)
def append_page(self, child, tab_label, tab_tooltip=None):
label = HobTabLabel(tab_label)
if tab_tooltip:
label.set_tooltip_text(tab_tooltip)
label.set_active(False)
self.pages.append(label)
gtk.Notebook.append_page(self, child, label)
def append_page(self, child, tab_label):
self.notebook.set_current_page(self.notebook.append_page(child, tab_label))
def set_entry(self, name="Search:"):
for child in self.tb.get_children():
if child:
self.tb.remove(child)
hbox_entry = gtk.HBox(False, 0)
hbox_entry.show()
self.search = gtk.Entry()
self.search_name = name
style = self.search.get_style()
@@ -481,20 +747,59 @@ class HobNotebook(gtk.Notebook):
self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR)
self.search.connect("icon-release", self.set_search_entry_clear_cb)
self.search.show()
self.align = gtk.Alignment(xalign=1.0, yalign=0.7)
self.align.add(self.search)
self.align.show()
hbox_entry.pack_end(self.align, False, False)
self.tabbar.resize_blank_rectangle()
self.tb.attach(hbox_entry, 75, 100, 0, 1, xpadding=5)
self.tb.attach(self.hbox, 0, 100, 0, 1)
self.tabbar.connect("blank-area-changed", self.blank_area_resize_cb)
self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
self.search.connect("focus-out-event", self.set_search_entry_reset_cb)
self.set_action_widget(self.search, gtk.PACK_END)
self.tb.show()
def show_indicator_icon(self, title, number):
for child in self.pages:
if child.lbl.get_label() == title:
child.set_count(number)
for child in self.tabbar.children:
if child["toggled_page"] == -1:
continue
if child["title"] == title:
self.tabbar.show_indicator_icon(child, number)
def hide_indicator_icon(self, title):
for child in self.pages:
if child.lbl.get_label() == title:
child.set_count(0)
for child in self.tabbar.children:
if child["toggled_page"] == -1:
continue
if child["title"] == title:
self.tabbar.hide_indicator_icon(child)
def tab_switched_cb(self, widget, page):
self.notebook.set_current_page(page)
def page_added_cb(self, notebook, notebook_child, page):
if not notebook:
return
title = notebook.get_tab_label_text(notebook_child)
label = notebook.get_tab_label(notebook_child)
tooltip_markup = label.get_tooltip_markup()
if not title:
return
for child in self.tabbar.children:
if child["title"] == title:
child["toggled_page"] = page
return
self.tabbar.append_tab_child(title, page, tooltip_markup)
def page_removed_cb(self, notebook, notebook_child, page, title=""):
for child in self.tabbar.children:
if child["title"] == title:
child["toggled_page"] = -1
def blank_area_resize_cb(self, widget, request_x, request_y, request_width, request_height):
self.search.set_size_request(request_width, request_height)
def set_search_entry_editable_cb(self, search, event):
search.set_editable(True)
@@ -514,15 +819,7 @@ class HobNotebook(gtk.Notebook):
self.reset_entry(search)
def set_search_entry_clear_cb(self, search, icon_pos, event):
if search.get_editable() == True:
search.set_text("")
def set_page(self, title):
for child in self.pages:
if child.lbl.get_label() == title:
child.grab_focus()
self.set_current_page(self.pages.index(child))
return
self.reset_entry(search)
class HobWarpCellRendererText(gtk.CellRendererText):
def __init__(self, col_number):
@@ -760,7 +1057,7 @@ class HobCellRendererPixbuf(gtk.CellRendererPixbuf):
if self.control.is_active():
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, True)
else:
self.control.start_run(200, 0, 0, 1000, 150, tree)
self.control.start_run(200, 0, 0, 1000, 200, tree)
else:
self.control.remove_running_cell_area(cell_area)
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, False)
@@ -787,17 +1084,11 @@ class HobCellRendererToggle(gtk.CellRendererToggle):
gtk.CellRendererToggle.__init__(self)
self.ctrl = HobCellRendererController(is_draw_row=True)
self.ctrl.running_mode = self.ctrl.MODE_ONE_SHORT
self.cell_attr = {"fadeout": False, "number_of_children": 0}
self.cell_attr = {"fadeout": False}
def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
if (not self.ctrl) or (not widget):
return
if flags & gtk.CELL_RENDERER_SELECTED:
state = gtk.STATE_SELECTED
else:
state = gtk.STATE_NORMAL
if self.ctrl.is_active():
path = widget.get_path_at_pos(cell_area.x + cell_area.width/2, cell_area.y + cell_area.height/2)
# sometimes the parameters of cell_area will be a negative number,such as pull up down the scroll bar
@@ -806,23 +1097,14 @@ class HobCellRendererToggle(gtk.CellRendererToggle):
path = path[0]
if path in self.ctrl.running_cell_areas:
cr = window.cairo_create()
color = widget.get_style().base[state]
color = gtk.gdk.Color(HobColors.WHITE)
row_x, _, row_width, _ = widget.get_visible_rect()
border_y = self.get_property("ypad")
self.ctrl.on_draw_fadeinout_cb(cr, color, row_x, cell_area.y - border_y, row_width, \
cell_area.height + border_y * 2, self.cell_attr["fadeout"])
# draw number of a group
if self.cell_attr["number_of_children"]:
text = "%d pkg" % self.cell_attr["number_of_children"]
pangolayout = widget.create_pango_layout(text)
textw, texth = pangolayout.get_pixel_size()
x = cell_area.x + (cell_area.width/2) - (textw/2)
y = cell_area.y + (cell_area.height/2) - (texth/2)
widget.style.paint_layout(window, state, True, cell_area, widget, "checkbox", x, y, pangolayout)
else:
return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
'''delay: normally delay time is 1000ms
cell_list: whilch cells need to be render

View File

@@ -22,7 +22,6 @@
import gtk
import glib
import re
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.hobcolor import HobColors
from bb.ui.crumbs.hobwidget import hic, HobImageButton, HobInfoButton, HobAltButton, HobButton
@@ -34,9 +33,6 @@ from bb.ui.crumbs.hobpages import HobPage
#
class ImageConfigurationPage (HobPage):
__dummy_machine__ = "--select a machine--"
__dummy_image__ = "--select a base image--"
def __init__(self, builder):
super(ImageConfigurationPage, self).__init__(builder, "Image configuration")
@@ -45,8 +41,6 @@ class ImageConfigurationPage (HobPage):
# or by manual. If by manual, all user's recipe selection and package selection are
# cleared.
self.machine_combo_changed_by_manual = True
self.stopping = False
self.warning_shift = 0
self.create_visual_elements()
def create_visual_elements(self):
@@ -116,10 +110,9 @@ class ImageConfigurationPage (HobPage):
self.show_all()
def update_progress_bar(self, title, fraction, status=None):
if self.stopping == False:
self.progress_bar.update(fraction)
self.progress_bar.set_text(title)
self.progress_bar.set_rcstyle(status)
self.progress_bar.update(fraction)
self.progress_bar.set_title(title)
self.progress_bar.set_rcstyle(status)
def show_info_populating(self):
self._pack_components(pack_config_build_button = False)
@@ -138,40 +131,8 @@ class ImageConfigurationPage (HobPage):
self._pack_components(pack_config_build_button = True)
self.set_config_machine_layout(show_progress_bar = False)
self.set_config_baseimg_layout()
self.set_rcppkg_layout()
self.show_all()
if self.builder.recipe_model.get_selected_image() == self.builder.recipe_model.__custom_image__:
self.just_bake_button.hide()
def add_warnings_bar(self):
#create the warnings bar shown when recipes parsing generates warnings
color = HobColors.KHAKI
warnings_bar = gtk.EventBox()
warnings_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
warnings_bar.set_flags(gtk.CAN_DEFAULT)
warnings_bar.grab_default()
build_stop_tab = gtk.Table(10, 20, True)
warnings_bar.add(build_stop_tab)
icon = gtk.Image()
icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INDI_ALERT_FILE)
icon.set_from_pixbuf(icon_pix_buffer)
build_stop_tab.attach(icon, 0, 2, 0, 10)
label = gtk.Label()
label.set_alignment(0.0, 0.5)
warnings_nb = len(self.builder.parsing_warnings)
if warnings_nb == 1:
label.set_markup("<span size='x-large'><b>1 recipe parsing warning</b></span>")
else:
label.set_markup("<span size='x-large'><b>%s recipe parsing warnings</b></span>" % warnings_nb)
build_stop_tab.attach(label, 2, 12, 0, 10)
view_warnings_button = HobButton("View warnings")
view_warnings_button.connect('clicked', self.view_warnings_button_clicked_cb)
build_stop_tab.attach(view_warnings_button, 15, 19, 1, 9)
return warnings_bar
def create_config_machine(self):
self.machine_title = gtk.Label()
@@ -186,6 +147,7 @@ class ImageConfigurationPage (HobPage):
self.machine_title_desc.set_markup(mark)
self.machine_combo = gtk.combo_box_new_text()
self.machine_combo.set_wrap_width(1)
self.machine_combo.connect("changed", self.machine_combo_changed_cb)
icon_file = hic.ICON_LAYERS_DISPLAY_FILE
@@ -201,12 +163,13 @@ class ImageConfigurationPage (HobPage):
markup += "dev-manual.html#understanding-and-using-layers\">reference manual</a>."
self.layer_info_icon = HobInfoButton(markup, self.get_parent())
# self.progress_box = gtk.HBox(False, 6)
self.progress_box = gtk.HBox(False, 6)
self.progress_bar = HobProgressBar()
# self.progress_box.pack_start(self.progress_bar, expand=True, fill=True)
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.progress_box.pack_end(self.stop_button, expand=False, fill=False)
self.machine_separator = gtk.HSeparator()
def set_config_machine_layout(self, show_progress_bar = False):
@@ -216,15 +179,7 @@ class ImageConfigurationPage (HobPage):
self.gtable.attach(self.layer_button, 14, 36, 7, 12)
self.gtable.attach(self.layer_info_icon, 36, 40, 7, 11)
if show_progress_bar:
#self.gtable.attach(self.progress_box, 0, 40, 15, 18)
self.gtable.attach(self.progress_bar, 0, 37, 15, 18)
self.gtable.attach(self.stop_button, 37, 40, 15, 18, 0, 0)
if self.builder.parsing_warnings:
self.warnings_bar = self.add_warnings_bar()
self.gtable.attach(self.warnings_bar, 0, 40, 14, 18)
self.warning_shift = 4
else:
self.warning_shift = 0
self.gtable.attach(self.progress_box, 0, 40, 15, 19)
self.gtable.attach(self.machine_separator, 0, 40, 13, 14)
def create_config_baseimg(self):
@@ -241,73 +196,72 @@ class ImageConfigurationPage (HobPage):
self.image_title_desc.set_markup(mark)
self.image_combo = gtk.combo_box_new_text()
self.image_combo.set_wrap_width(1)
self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
self.image_desc = gtk.Label()
self.image_desc.set_alignment(0.0, 0.5)
self.image_desc.set_size_request(256, -1)
self.image_desc.set_justify(gtk.JUSTIFY_LEFT)
self.image_desc.set_line_wrap(True)
# button to view recipes
icon_file = hic.ICON_RCIPE_DISPLAY_FILE
hover_file = hic.ICON_RCIPE_HOVER_FILE
self.view_adv_configuration_button = HobImageButton("Advanced configuration",
"Select image types, package formats, etc",
icon_file, hover_file)
self.view_adv_configuration_button.connect("clicked", self.view_adv_configuration_button_clicked_cb)
self.view_recipes_button = HobImageButton("View recipes",
"Add/remove recipes and tasks",
icon_file, hover_file)
self.view_recipes_button.connect("clicked", self.view_recipes_button_clicked_cb)
# button to view packages
icon_file = hic.ICON_PACKAGES_DISPLAY_FILE
hover_file = hic.ICON_PACKAGES_HOVER_FILE
self.view_packages_button = HobImageButton("View packages",
"Add/remove previously built packages",
icon_file, hover_file)
self.view_packages_button.connect("clicked", self.view_packages_button_clicked_cb)
self.image_separator = gtk.HSeparator()
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)
self.gtable.attach(self.image_combo, 0, 12, 23+self.warning_shift, 26+self.warning_shift)
self.gtable.attach(self.image_desc, 0, 12, 27+self.warning_shift, 33+self.warning_shift)
self.gtable.attach(self.view_adv_configuration_button, 14, 36, 23+self.warning_shift, 28+self.warning_shift)
self.gtable.attach(self.image_separator, 0, 40, 35+self.warning_shift, 36+self.warning_shift)
self.gtable.attach(self.image_title, 0, 40, 15, 17)
self.gtable.attach(self.image_title_desc, 0, 40, 18, 22)
self.gtable.attach(self.image_combo, 0, 12, 23, 26)
self.gtable.attach(self.image_desc, 13, 38, 23, 28)
self.gtable.attach(self.image_separator, 0, 40, 35, 36)
def set_rcppkg_layout(self):
self.gtable.attach(self.view_recipes_button, 0, 20, 28, 33)
self.gtable.attach(self.view_packages_button, 20, 40, 28, 33)
def create_config_build_button(self):
# Create the "Build packages" and "Build image" buttons at the bottom
button_box = gtk.HBox(False, 6)
# create button "Build image"
self.just_bake_button = HobButton("Build image")
#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)
just_bake_button = HobButton("Build image")
just_bake_button.set_size_request(205, 49)
just_bake_button.set_tooltip_text("Build target image")
just_bake_button.connect("clicked", self.just_bake_button_clicked_cb)
button_box.pack_end(just_bake_button, expand=False, fill=False)
# 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)
label = gtk.Label(" or ")
button_box.pack_end(label, expand=False, fill=False)
# create button "Build Packages"
build_packages_button = HobAltButton("Build packages")
build_packages_button.connect("clicked", self.build_packages_button_clicked_cb)
build_packages_button.set_tooltip_text("Build recipes into packages")
button_box.pack_end(build_packages_button, expand=False, fill=False)
return button_box
def stop_button_clicked_cb(self, button):
self.stopping = True
self.progress_bar.set_text("Stopping recipe parsing")
self.progress_bar.set_rcstyle("stop")
self.builder.cancel_parse_sync()
def view_warnings_button_clicked_cb(self, button):
self.builder.show_warning_dialog()
def machine_combo_changed_cb(self, machine_combo):
self.stopping = False
combo_item = machine_combo.get_active_text()
if not combo_item or combo_item == self.__dummy_machine__:
if not combo_item:
return
# remove __dummy_machine__ item from the store list after first user selection
# because it is no longer valid
combo_store = machine_combo.get_model()
if len(combo_store) and (combo_store[0][0] == self.__dummy_machine__):
machine_combo.remove_text(0)
self.builder.configuration.curr_mach = combo_item
if self.machine_combo_changed_by_manual:
self.builder.configuration.clear_selection()
@@ -318,13 +272,13 @@ class ImageConfigurationPage (HobPage):
self.builder.populate_recipe_package_info_async()
def update_machine_combo(self):
all_machines = [self.__dummy_machine__] + self.builder.parameters.all_machines
all_machines = self.builder.parameters.all_machines
model = self.machine_combo.get_model()
model.clear()
for machine in all_machines:
self.machine_combo.append_text(machine)
self.machine_combo.set_active(0)
self.machine_combo.set_active(-1)
def switch_machine_combo(self):
self.machine_combo_changed_by_manual = False
@@ -335,15 +289,10 @@ class ImageConfigurationPage (HobPage):
self.machine_combo.set_active(active)
return
active += 1
self.machine_combo.set_active(-1)
if model[0][0] != self.__dummy_machine__:
self.machine_combo.insert_text(0, self.__dummy_machine__)
self.machine_combo.set_active(0)
def update_image_desc(self):
def update_image_desc(self, selected_image):
desc = ""
selected_image = self.image_combo.get_active_text()
if selected_image and selected_image in self.builder.recipe_model.pn_path.keys():
image_path = self.builder.recipe_model.pn_path[selected_image]
image_iter = self.builder.recipe_model.get_iter(image_path)
@@ -360,15 +309,9 @@ class ImageConfigurationPage (HobPage):
def image_combo_changed_cb(self, combo):
self.builder.window_sensitive(False)
selected_image = self.image_combo.get_active_text()
if not selected_image or (selected_image == self.__dummy_image__):
if not selected_image:
return
# remove __dummy_image__ item from the store list after first user selection
# because it is no longer valid
combo_store = combo.get_model()
if len(combo_store) and (combo_store[0][0] == self.__dummy_image__):
combo.remove_text(0)
self.builder.customized = False
selected_recipes = []
@@ -376,16 +319,13 @@ class ImageConfigurationPage (HobPage):
image_path = self.builder.recipe_model.pn_path[selected_image]
image_iter = self.builder.recipe_model.get_iter(image_path)
selected_packages = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_INSTALL).split()
self.update_image_desc()
self.update_image_desc(selected_image)
self.builder.recipe_model.reset()
self.builder.package_model.reset()
self.show_baseimg_selected()
if selected_image == self.builder.recipe_model.__custom_image__:
self.just_bake_button.hide()
glib.idle_add(self.image_combo_changed_idle_cb, selected_image, selected_recipes, selected_packages)
def _image_combo_connect_signal(self):
@@ -402,66 +342,32 @@ class ImageConfigurationPage (HobPage):
# populate image combo
filter = {RecipeListModel.COL_TYPE : ['image']}
image_model = recipe_model.tree_model(filter)
image_model.set_sort_column_id(recipe_model.COL_NAME, gtk.SORT_ASCENDING)
active = 0
active = -1
cnt = 0
white_pattern = []
if self.builder.parameters.image_white_pattern:
for i in self.builder.parameters.image_white_pattern.split():
white_pattern.append(re.compile(i))
black_pattern = []
if self.builder.parameters.image_black_pattern:
for i in self.builder.parameters.image_black_pattern.split():
black_pattern.append(re.compile(i))
black_pattern.append(re.compile("hob-image"))
it = image_model.get_iter_first()
self._image_combo_disconnect_signal()
model = self.image_combo.get_model()
model.clear()
# Set a indicator text to combo store when first open
if not selected_image:
self.image_combo.append_text(self.__dummy_image__)
cnt = cnt + 1
# append and set active
while it:
path = image_model.get_path(it)
it = image_model.iter_next(it)
image_name = image_model[path][recipe_model.COL_NAME]
if image_name == self.builder.recipe_model.__custom_image__:
if image_name == self.builder.recipe_model.__dummy_image__:
continue
if black_pattern:
allow = True
for pattern in black_pattern:
if pattern.search(image_name):
allow = False
break
elif white_pattern:
allow = False
for pattern in white_pattern:
if pattern.search(image_name):
allow = True
break
else:
allow = True
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__)
if selected_image == self.builder.recipe_model.__custom_image__:
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.__dummy_image__)
if selected_image == self.builder.recipe_model.__dummy_image__:
active = cnt
self.image_combo.set_active(-1)
self.image_combo.set_active(active)
if active != 0:
if active != -1:
self.show_baseimg_selected()
self._image_combo_connect_signal()
@@ -469,22 +375,18 @@ 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.reparse_post_adv_settings()
def view_recipes_button_clicked_cb(self, button):
self.builder.show_recipes()
def view_packages_button_clicked_cb(self, button):
self.builder.show_packages()
def just_bake_button_clicked_cb(self, button):
self.builder.parsing_warnings = []
self.builder.just_bake()
def edit_image_button_clicked_cb(self, button):
self.builder.configuration.initial_selected_image = self.builder.configuration.selected_image
self.builder.show_recipes()
def build_packages_button_clicked_cb(self, button):
self.builder.build_packages()
def template_button_clicked_cb(self, button):
response, path = self.builder.show_load_template_dialog()
@@ -498,7 +400,7 @@ class ImageConfigurationPage (HobPage):
def settings_button_clicked_cb(self, button):
# Create an advanced settings dialog
response, settings_changed = self.builder.show_simple_settings_dialog()
response, settings_changed = self.builder.show_adv_settings_dialog()
if not response:
return
if settings_changed:

View File

@@ -25,97 +25,34 @@ import gtk
from bb.ui.crumbs.hobcolor import HobColors
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
#
# ImageDetailsPage
#
class ImageDetailsPage (HobPage):
__columns__ = [{
'col_name' : 'Image name',
'col_id' : 0,
'col_style': 'text',
'col_min' : 500,
'col_max' : 500
}, {
'col_name' : 'Image size',
'col_id' : 1,
'col_style': 'text',
'col_min' : 100,
'col_max' : 100
}, {
'col_name' : 'Select',
'col_id' : 2,
'col_style': 'radio toggle',
'col_min' : 100,
'col_max' : 100
}]
class DetailBox (gtk.EventBox):
def __init__(self, widget = None, varlist = None, vallist = None, icon = None, button = None, button2=None, color = HobColors.LIGHT_GRAY):
gtk.EventBox.__init__(self)
# set color
style = self.get_style().copy()
style.bg[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(color, False, False)
self.set_style(style)
self.row = gtk.Table(1, 2, False)
self.row.set_border_width(10)
self.add(self.row)
total_rows = 0
if widget:
total_rows = 10
if varlist and vallist:
# pack the icon and the text on the left
total_rows += len(varlist)
self.table = gtk.Table(total_rows, 20, True)
self.table.set_row_spacings(6)
self.table.set_size_request(100, -1)
self.row.attach(self.table, 0, 1, 0, 1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL)
colid = 0
rowid = 0
self.line_widgets = {}
if icon:
self.table.attach(icon, colid, colid + 2, 0, 1)
colid = colid + 2
if widget:
self.table.attach(widget, colid, 20, 0, 10)
rowid = 10
if varlist and vallist:
for row in range(rowid, total_rows):
index = row - rowid
self.line_widgets[varlist[index]] = self.text2label(varlist[index], vallist[index])
self.table.attach(self.line_widgets[varlist[index]], colid, 20, row, row + 1)
# pack the button on the right
if button:
self.bbox = gtk.VBox()
self.bbox.pack_start(button, expand=True, fill=False)
if button2:
self.bbox.pack_start(button2, expand=True, fill=False)
self.bbox.set_size_request(150,-1)
self.row.attach(self.bbox, 1, 2, 0, 1, xoptions=gtk.FILL, yoptions=gtk.EXPAND)
def update_line_widgets(self, variable, value):
if len(self.line_widgets) == 0:
return
if not isinstance(self.line_widgets[variable], gtk.Label):
return
self.line_widgets[variable].set_markup(self.format_line(variable, value))
def wrap_line(self, inputs):
# wrap the long text of inputs
wrap_width_chars = 75
outputs = ""
tmps = inputs
less_chars = len(inputs)
while (less_chars - wrap_width_chars) > 0:
less_chars -= wrap_width_chars
outputs += tmps[:wrap_width_chars] + "\n "
tmps = inputs[less_chars:]
outputs += tmps
return outputs
def format_line(self, variable, value):
wraped_value = self.wrap_line(value)
markup = "<span weight=\'bold\'>%s</span>" % variable
markup += "<span weight=\'normal\' foreground=\'#1c1c1c\' font_desc=\'14px\'>%s</span>" % wraped_value
return markup
def text2label(self, variable, value):
# append the name:value to the left box
# such as "Name: hob-core-minimal-variant-2011-12-15-beagleboard"
label = gtk.Label()
label.set_alignment(0.0, 0.5)
label.set_markup(self.format_line(variable, value))
return label
class BuildDetailBox (gtk.EventBox):
def __init__(self, varlist = None, vallist = None, icon = None, color = HobColors.LIGHT_GRAY):
def __init__(self, widget = None, varlist = None, vallist = None, icon = None, button = None, color = HobColors.LIGHT_GRAY):
gtk.EventBox.__init__(self)
# set color
@@ -124,30 +61,34 @@ class ImageDetailsPage (HobPage):
self.set_style(style)
self.hbox = gtk.HBox()
self.hbox.set_border_width(10)
self.hbox.set_border_width(15)
self.add(self.hbox)
total_rows = 0
if varlist and vallist:
if widget:
row = 1
elif varlist and vallist:
# pack the icon and the text on the left
total_rows += len(varlist)
self.table = gtk.Table(total_rows, 20, True)
self.table.set_row_spacings(6)
row = len(varlist)
self.table = gtk.Table(row, 20, True)
self.table.set_size_request(100, -1)
self.hbox.pack_start(self.table, expand=True, fill=True, padding=15)
colid = 0
rowid = 0
self.line_widgets = {}
if icon:
self.table.attach(icon, colid, colid + 2, 0, 1)
colid = colid + 2
if varlist and vallist:
for row in range(rowid, total_rows):
index = row - rowid
self.line_widgets[varlist[index]] = self.text2label(varlist[index], vallist[index])
self.table.attach(self.line_widgets[varlist[index]], colid, 20, row, row + 1)
if widget:
self.table.attach(widget, colid, 20, 0, 1)
elif varlist and vallist:
for line in range(0, row):
self.line_widgets[varlist[line]] = self.text2label(varlist[line], vallist[line])
self.table.attach(self.line_widgets[varlist[line]], colid, 20, line, line + 1)
# pack the button on the right
if button:
self.hbox.pack_end(button, expand=False, fill=False)
def update_line_widgets(self, variable, value):
if len(self.line_widgets) == 0:
return
@@ -155,23 +96,9 @@ class ImageDetailsPage (HobPage):
return
self.line_widgets[variable].set_markup(self.format_line(variable, value))
def wrap_line(self, inputs):
# wrap the long text of inputs
wrap_width_chars = 75
outputs = ""
tmps = inputs
less_chars = len(inputs)
while (less_chars - wrap_width_chars) > 0:
less_chars -= wrap_width_chars
outputs += tmps[:wrap_width_chars] + "\n "
tmps = inputs[less_chars:]
outputs += tmps
return outputs
def format_line(self, variable, value):
wraped_value = self.wrap_line(value)
markup = "<span weight=\'bold\'>%s</span>" % variable
markup += "<span weight=\'normal\' foreground=\'#1c1c1c\' font_desc=\'14px\'>%s</span>" % wraped_value
markup += "<span weight=\'normal\' foreground=\'#1c1c1c\' font_desc=\'14px\'>%s</span>" % value
return markup
def text2label(self, variable, value):
@@ -185,7 +112,7 @@ class ImageDetailsPage (HobPage):
def __init__(self, builder):
super(ImageDetailsPage, self).__init__(builder, "Image details")
self.image_store = []
self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
self.button_ids = {}
self.details_bottom_buttons = gtk.HBox(False, 6)
self.create_visual_elements()
@@ -230,30 +157,27 @@ class ImageDetailsPage (HobPage):
self.details_bottom_buttons.remove(child)
def show_page(self, step):
self.build_succeeded = (step == self.builder.IMAGE_GENERATED)
build_succeeded = (step == self.builder.IMAGE_GENERATED)
image_addr = self.builder.parameters.image_addr
image_names = self.builder.parameters.image_names
if self.build_succeeded:
if build_succeeded:
machine = self.builder.configuration.curr_mach
base_image = self.builder.recipe_model.get_selected_image()
layers = self.builder.configuration.layers
pkg_num = "%s" % len(self.builder.package_model.get_selected_packages())
log_file = self.builder.current_logfile
else:
pkg_num = "N/A"
log_file = None
# remove
for button_id, button in self.button_ids.items():
button.disconnect(button_id)
self._remove_all_widget()
# repack
self.pack_start(self.details_top_buttons, expand=False, fill=False)
self.pack_start(self.group_align, expand=True, fill=True)
self.build_result = None
if self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING:
if build_succeeded:
# building is the previous step
icon = gtk.Image()
pixmap_path = hic.ICON_INDI_CONFIRM_FILE
@@ -262,93 +186,49 @@ class ImageDetailsPage (HobPage):
icon.set_from_pixbuf(pix_buffer)
varlist = [""]
vallist = ["Your image is ready"]
self.build_result = self.BuildDetailBox(varlist=varlist, vallist=vallist, icon=icon, color=color)
self.build_result = self.DetailBox(varlist=varlist, vallist=vallist, icon=icon, color=color)
self.box_group_area.pack_start(self.build_result, expand=False, fill=False)
# create the buttons at the bottom first because the buttons are used in apply_button_per_image()
if self.build_succeeded:
if 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 = []
self.toggled_image = ""
self.image_store.clear()
default_toggled = False
default_image_size = 0
self.num_toggled = 0
i = 0
for image_name in image_names:
image_size = HobPage._size_to_string(os.stat(os.path.join(image_addr, image_name)).st_size)
image_attr = ("run" if (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)) else \
("deploy" if self.test_deployable(image_name) else ""))
is_toggled = (image_attr != "")
if not self.toggled_image:
if not default_toggled:
default_toggled = (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)) \
or self.test_deployable(image_name)
if i == (len(image_names) - 1):
is_toggled = True
if is_toggled:
default_toggled = True
self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, default_toggled)
if default_toggled:
default_image_size = image_size
self.toggled_image = image_name
split_stuff = image_name.split('.')
if "rootfs" in split_stuff:
image_type = image_name[(len(split_stuff[0]) + len(".rootfs") + 1):]
self.create_bottom_buttons(self.buttonlist, image_name)
else:
image_type = image_name[(len(split_stuff[0]) + 1):]
self.image_store.append({'name': image_name,
'type': image_type,
'size': image_size,
'is_toggled': is_toggled,
'action_attr': image_attr,})
self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, False)
i = i + 1
self.num_toggled += is_toggled
is_runnable = self.create_bottom_buttons(self.buttonlist, self.toggled_image)
# Generated image files info
varlist = ["Name: ", "Files created: ", "Directory: "]
vallist = []
vallist.append(image_name.split('.')[0])
vallist.append(', '.join(fileitem['type'] for fileitem in self.image_store))
vallist.append(image_addr)
image_table = HobViewTable(self.__columns__)
image_table.set_model(self.image_store)
image_table.connect("toggled", self.toggled_cb)
view_files_button = HobAltButton("View files")
view_files_button.connect("clicked", self.view_files_clicked_cb, image_addr)
view_files_button.set_tooltip_text("Open the directory containing the image files")
open_log_button = None
if log_file:
open_log_button = HobAltButton("Open log")
open_log_button.connect("clicked", self.open_log_clicked_cb, log_file)
open_log_button.set_tooltip_text("Open the build's log file")
self.image_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=view_files_button, button2=open_log_button)
self.box_group_area.pack_start(self.image_detail, expand=False, fill=True)
# The default kernel box for the qemu images
self.sel_kernel = ""
self.kernel_detail = None
if 'qemu' in image_name:
self.sel_kernel = self.get_kernel_file_name()
# varlist = ["Kernel: "]
# vallist = []
# vallist.append(self.sel_kernel)
# change_kernel_button = HobAltButton("Change")
# change_kernel_button.connect("clicked", self.change_kernel_cb)
# change_kernel_button.set_tooltip_text("Change qemu kernel file")
# 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)
self.image_detail = self.DetailBox(widget=image_table, button=view_files_button)
self.box_group_area.pack_start(self.image_detail, expand=True, fill=True)
# Machine, Base image and Layers
layer_num_limit = 15
varlist = ["Machine: ", "Base image: ", "Layers: "]
vallist = []
self.setting_detail = None
if self.build_succeeded:
if build_succeeded:
vallist.append(machine)
vallist.append(base_image)
i = 0
@@ -372,41 +252,29 @@ class ImageDetailsPage (HobPage):
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)
self.box_group_area.pack_start(self.setting_detail, expand=False, fill=False)
# Packages included, and Total image size
varlist = ["Packages included: ", "Total image size: "]
vallist = []
vallist.append(pkg_num)
vallist.append(default_image_size)
if self.build_succeeded:
if build_succeeded:
edit_packages_button = HobAltButton("Edit packages")
edit_packages_button.set_tooltip_text("Edit the packages included in your image")
edit_packages_button.connect("clicked", self.edit_packages_button_clicked_cb)
else: # get to this page from "My images"
edit_packages_button = None
self.package_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=edit_packages_button)
self.box_group_area.pack_start(self.package_detail, expand=True, fill=True)
self.box_group_area.pack_start(self.package_detail, expand=False, fill=False)
# pack the buttons at the bottom, at this time they are already created.
if self.build_succeeded:
self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False)
else: # for "My images" page
self.details_separator = gtk.HSeparator()
self.box_group_area.pack_start(self.details_separator, expand=False, fill=False)
self.box_group_area.pack_start(self.details_bottom_buttons, expand=False, fill=False)
self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False)
self.show_all()
if self.kernel_detail and (not is_runnable):
self.kernel_detail.hide()
def view_files_clicked_cb(self, button, image_addr):
subprocess.call("xdg-open /%s" % image_addr, shell=True)
def open_log_clicked_cb(self, button, log_file):
if log_file:
log_file = "file:///" + log_file
gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
os.system("xdg-open /%s" % image_addr)
def refresh_package_detail_box(self, image_size):
self.package_detail.update_line_widgets("Total image size: ", image_size)
@@ -428,8 +296,6 @@ class ImageDetailsPage (HobPage):
return mach_runnable
def test_deployable(self, image_name):
if self.builder.configuration.curr_mach.startswith("qemu"):
return False
deployable = False
for t in self.builder.parameters.deployable_image_types:
if image_name.endswith(t):
@@ -437,121 +303,49 @@ class ImageDetailsPage (HobPage):
break
return deployable
def get_kernel_file_name(self, kernel_addr=""):
kernel_name = ""
if not kernel_addr:
kernel_addr = self.builder.parameters.image_addr
files = [f for f in os.listdir(kernel_addr) if f[0] <> '.']
for check_file in files:
if check_file.endswith(".bin"):
name_splits = check_file.split(".")[0]
if self.builder.parameters.kernel_image_type in name_splits.split("-"):
kernel_name = check_file
break
return kernel_name
def show_builded_images_dialog(self, widget, primary_action=""):
title = primary_action if primary_action else "Your builded images"
dialog = CrumbsDialog(title, self.builder,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
dialog.set_border_width(12)
label = gtk.Label()
label.set_use_markup(True)
label.set_alignment(0.0, 0.5)
label.set_padding(12,0)
if primary_action == "Run image":
label.set_markup("<span font_desc='12'>Select the image file you want to run:</span>")
elif primary_action == "Deploy image":
label.set_markup("<span font_desc='12'>Select the image file you want to deploy:</span>")
else:
label.set_markup("<span font_desc='12'>Select the image file you want to %s</span>" % primary_action)
dialog.vbox.pack_start(label, expand=False, fill=False)
# filter created images as action attribution (deploy or run)
action_attr = ""
action_images = []
for fileitem in self.image_store:
action_attr = fileitem['action_attr']
if (action_attr == 'run' and primary_action == "Run image") \
or (action_attr == 'deploy' and primary_action == "Deploy image"):
action_images.append(fileitem)
# pack the corresponding 'runnable' or 'deploy' radio_buttons, if there has no more than one file.
# assume that there does not both have 'deploy' and 'runnable' files in the same building result
# in possible as design.
curr_row = 0
rows = (len(action_images)) if len(action_images) < 10 else 10
table = gtk.Table(rows, 10, True)
table.set_row_spacings(6)
table.set_col_spacing(0, 12)
table.set_col_spacing(5, 12)
sel_parent_btn = None
for fileitem in action_images:
sel_btn = gtk.RadioButton(sel_parent_btn, fileitem['type'])
sel_parent_btn = sel_btn if not sel_parent_btn else sel_parent_btn
sel_btn.set_active(fileitem['is_toggled'])
sel_btn.connect('toggled', self.table_selected_cb, fileitem)
if curr_row < 10:
table.attach(sel_btn, 0, 4, curr_row, curr_row + 1, xpadding=24)
else:
table.attach(sel_btn, 5, 9, curr_row - 10, curr_row - 9, xpadding=24)
curr_row += 1
dialog.vbox.pack_start(table, expand=False, fill=False, padding=6)
button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
HobAltButton.style_button(button)
if primary_action:
button = dialog.add_button(primary_action, gtk.RESPONSE_YES)
HobButton.style_button(button)
dialog.show_all()
response = dialog.run()
dialog.destroy()
if response != gtk.RESPONSE_YES:
def toggled_cb(self, table, cell, path, columnid, tree):
model = tree.get_model()
if not model:
return
iter = model.get_iter_first()
while iter:
rowpath = model.get_path(iter)
model[rowpath][columnid] = False
iter = model.iter_next(iter)
for fileitem in self.image_store:
if fileitem['is_toggled']:
if fileitem['action_attr'] == 'run':
self.builder.runqemu_image(fileitem['name'], self.sel_kernel)
elif fileitem['action_attr'] == 'deploy':
self.builder.deploy_image(fileitem['name'])
model[path][columnid] = True
self.refresh_package_detail_box(model[path][1])
def table_selected_cb(self, tbutton, image):
image['is_toggled'] = tbutton.get_active()
if image['is_toggled']:
self.toggled_image = image['name']
image_name = model[path][0]
def change_kernel_cb(self, widget):
kernel_path = self.builder.show_load_kernel_dialog()
if kernel_path and self.kernel_detail:
import os.path
self.sel_kernel = os.path.basename(kernel_path)
markup = self.kernel_detail.format_line("Kernel: ", self.sel_kernel)
label = ((self.kernel_detail.get_children()[0]).get_children()[0]).get_children()[0]
label.set_markup(markup)
# remove
for button_id, button in self.button_ids.items():
button.disconnect(button_id)
self._remove_all_widget()
# repack
self.pack_start(self.details_top_buttons, expand=False, fill=False)
self.pack_start(self.group_align, expand=True, fill=True)
if self.build_result:
self.box_group_area.pack_start(self.build_result, expand=False, fill=False)
self.box_group_area.pack_start(self.image_detail, expand=True, fill=True)
if self.setting_detail:
self.box_group_area.pack_start(self.setting_detail, expand=False, fill=False)
self.box_group_area.pack_start(self.package_detail, expand=False, fill=False)
self.create_bottom_buttons(self.buttonlist, image_name)
self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False)
self.show_all()
def create_bottom_buttons(self, buttonlist, image_name):
# Create the buttons at the bottom
created = False
packed = False
self.button_ids = {}
is_runnable = False
# create button "Deploy image"
name = "Deploy image"
if name in buttonlist and self.test_deployable(image_name):
deploy_button = HobButton('Deploy image')
#deploy_button.set_size_request(205, 49)
deploy_button.set_size_request(205, 49)
deploy_button.set_tooltip_text("Burn a live image to a USB drive or flash memory")
deploy_button.set_flags(gtk.CAN_DEFAULT)
button_id = deploy_button.connect("clicked", self.deploy_button_clicked_cb)
@@ -564,15 +358,15 @@ class ImageDetailsPage (HobPage):
if name in buttonlist and self.test_type_runnable(image_name) and self.test_mach_runnable(image_name):
if created == True:
# separator
#label = gtk.Label(" or ")
#self.details_bottom_buttons.pack_end(label, expand=False, fill=False)
label = gtk.Label(" or ")
self.details_bottom_buttons.pack_end(label, expand=False, fill=False)
# create button "Run image"
run_button = HobAltButton("Run image")
else:
# create button "Run image" as the primary button
run_button = HobButton("Run image")
#run_button.set_size_request(205, 49)
run_button.set_size_request(205, 49)
run_button.set_flags(gtk.CAN_DEFAULT)
packed = True
run_button.set_tooltip_text("Start up an image with qemu emulator")
@@ -580,22 +374,25 @@ class ImageDetailsPage (HobPage):
self.button_ids[button_id] = run_button
self.details_bottom_buttons.pack_end(run_button, expand=False, fill=False)
created = True
is_runnable = True
if not packed:
box = gtk.HBox(False, 6)
box.show()
subbox = gtk.HBox(False, 0)
subbox.set_size_request(205, 49)
subbox.show()
box.add(subbox)
self.details_bottom_buttons.pack_end(box, False, False)
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)
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
# create button "Save as template"
save_button = HobAltButton("Save as template")
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
@@ -605,39 +402,34 @@ class ImageDetailsPage (HobPage):
name = "Build new image"
if name in buttonlist:
# create button "Build new image"
if packed:
build_new_button = HobAltButton("Build new image")
else:
build_new_button = HobButton("Build new image")
build_new_button.set_flags(gtk.CAN_DEFAULT)
#build_new_button.set_size_request(205, 49)
self.details_bottom_buttons.pack_end(build_new_button, expand=False, fill=False)
build_new_button = HobAltButton("Build new image")
build_new_button.set_tooltip_text("Create a new image from scratch")
button_id = build_new_button.connect("clicked", self.build_new_button_clicked_cb)
self.button_ids[button_id] = build_new_button
self.details_bottom_buttons.pack_start(build_new_button, expand=False, fill=False)
return is_runnable
def _get_selected_image(self):
image_name = ""
iter = self.image_store.get_iter_first()
while iter:
path = self.image_store.get_path(iter)
if self.image_store[path][2]:
image_name = self.image_store[path][0]
break
iter = self.image_store.iter_next(iter)
return image_name
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:
self.set_sensitive(False)
self.show_builded_images_dialog(None, "Deploy image")
self.set_sensitive(True)
else:
self.builder.deploy_image(self.toggled_image)
image_name = self._get_selected_image()
self.builder.deploy_image(image_name)
def run_button_clicked_cb(self, button):
if self.toggled_image:
if self.num_toggled > 1:
self.set_sensitive(False)
self.show_builded_images_dialog(None, "Run image")
self.set_sensitive(True)
else:
self.builder.runqemu_image(self.toggled_image, self.sel_kernel)
image_name = self._get_selected_image()
self.builder.runqemu_image(image_name)
def build_new_button_clicked_cb(self, button):
self.builder.initiate_new_build_async()
@@ -660,7 +452,7 @@ class ImageDetailsPage (HobPage):
def settings_button_clicked_cb(self, button):
# Create an advanced settings dialog
response, settings_changed = self.builder.show_simple_settings_dialog()
response, settings_changed = self.builder.show_adv_settings_dialog()
if not response:
return
if settings_changed:

View File

@@ -34,8 +34,7 @@ class PackageSelectionPage (HobPage):
pages = [
{
'name' : 'Included packages',
'tooltip' : 'The packages currently included for your image',
'name' : 'Included',
'filter' : { PackageListModel.COL_INC : [True] },
'columns' : [{
'col_name' : 'Package name',
@@ -44,6 +43,13 @@ class PackageSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 300,
'expand' : 'True'
}, {
'col_name' : 'Brought in by',
'col_id' : PackageListModel.COL_BINB,
'col_style': 'binb',
'col_min' : 100,
'col_max' : 350,
'expand' : 'True'
}, {
'col_name' : 'Size',
'col_id' : PackageListModel.COL_SIZE,
@@ -51,13 +57,6 @@ class PackageSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 300,
'expand' : 'True'
}, {
'col_name' : 'Brought in by (+others)',
'col_id' : PackageListModel.COL_BINB,
'col_style': 'binb',
'col_min' : 100,
'col_max' : 350,
'expand' : 'True'
}, {
'col_name' : 'Included',
'col_id' : PackageListModel.COL_INC,
@@ -67,7 +66,6 @@ class PackageSelectionPage (HobPage):
}]
}, {
'name' : 'All packages',
'tooltip' : 'All packages that have been built',
'filter' : {},
'columns' : [{
'col_name' : 'Package name',
@@ -92,12 +90,9 @@ class PackageSelectionPage (HobPage):
}]
}
]
(INCLUDED,
ALL) = range(2)
def __init__(self, builder):
super(PackageSelectionPage, self).__init__(builder, "Edit packages")
super(PackageSelectionPage, self).__init__(builder, "Packages")
# set invisiable members
self.recipe_model = self.builder.recipe_model
@@ -106,16 +101,13 @@ class PackageSelectionPage (HobPage):
# create visual elements
self.create_visual_elements()
def included_clicked_cb(self, button):
self.ins.set_current_page(self.INCLUDED)
def create_visual_elements(self):
self.label = gtk.Label("Packages included: 0\nSelected packages size: 0 MB")
self.eventbox = self.add_onto_top_bar(self.label, 73)
self.pack_start(self.eventbox, expand=False, fill=False)
self.pack_start(self.group_align, expand=True, fill=True)
# set visible members
# set visiable members
self.ins = HobNotebook()
self.tables = [] # we need to modify table when the dialog is shown
# append the tab
@@ -125,37 +117,35 @@ class PackageSelectionPage (HobPage):
filter = page['filter']
tab.set_model(self.package_model.tree_model(filter))
tab.connect("toggled", self.table_toggled_cb, page['name'])
if page['name'] == "Included packages":
if page['name'] == "Included":
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'])
label = gtk.Label(page['name'])
self.ins.append_page(tab, label)
self.tables.append(tab)
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)
self.button_box = gtk.HBox(False, 6)
self.box_group_area.pack_start(self.button_box, expand=False, fill=False)
button_box = gtk.HBox(False, 6)
self.box_group_area.pack_start(button_box, expand=False, fill=False)
self.build_image_button = HobButton('Build image')
#self.build_image_button.set_size_request(205, 49)
self.build_image_button.set_size_request(205, 49)
self.build_image_button.set_tooltip_text("Build target image")
self.build_image_button.set_flags(gtk.CAN_DEFAULT)
self.build_image_button.grab_default()
self.build_image_button.connect("clicked", self.build_image_clicked_cb)
self.button_box.pack_end(self.build_image_button, expand=False, fill=False)
button_box.pack_end(self.build_image_button, expand=False, fill=False)
self.back_button = HobAltButton('Cancel')
self.back_button = HobAltButton("<< Back to image configuration")
self.back_button.connect("clicked", self.back_button_clicked_cb)
self.button_box.pack_end(self.back_button, expand=False, fill=False)
button_box.pack_start(self.back_button, expand=False, fill=False)
def button_click_cb(self, widget, event):
path, col = widget.table_tree.get_cursor()
@@ -165,36 +155,11 @@ class PackageSelectionPage (HobPage):
if binb:
self.builder.show_binb_dialog(binb)
def open_log_clicked_cb(self, button, log_file):
if log_file:
log_file = "file:///" + log_file
gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
def show_page(self, log_file):
children = self.button_box.get_children() or []
for child in children:
self.button_box.remove(child)
# re-packed the buttons as request, add the 'open log' button if build success
self.button_box.pack_end(self.build_image_button, expand=False, fill=False)
if log_file:
open_log_button = HobAltButton("Open log")
open_log_button.connect("clicked", self.open_log_clicked_cb, log_file)
open_log_button.set_tooltip_text("Open the build's log file")
self.button_box.pack_end(open_log_button, expand=False, fill=False)
self.button_box.pack_end(self.back_button, expand=False, fill=False)
self.show_all()
def build_image_clicked_cb(self, button):
self.builder.parsing_warnings = []
self.builder.build_image()
def back_button_clicked_cb(self, button):
if self.builder.previous_step == self.builder.IMAGE_GENERATED:
self.builder.restore_initial_selected_packages()
self.refresh_selection()
self.builder.show_image_details()
else:
self.builder.show_configuration()
self.builder.show_configuration()
def _expand_all(self):
for tab in self.tables:
@@ -210,23 +175,23 @@ class PackageSelectionPage (HobPage):
selected_packages_size_str = HobPage._size_to_string(selected_packages_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
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\nTotal image size: %s" %
self.label.set_text("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)
self.ins.show_indicator_icon("Included", selected_packages_num)
def toggle_item_idle_cb(self, path, view_tree, cell, pagename):
if not self.package_model.path_included(path):
self.package_model.include_item(item_path=path, binb="User Selected")
else:
if pagename == "Included packages":
if pagename == "Included":
self.pre_fadeout_checkout_include(view_tree)
self.package_model.exclude_item(item_path=path)
self.render_fadeout(view_tree, cell)
@@ -236,7 +201,7 @@ class PackageSelectionPage (HobPage):
self.refresh_selection()
if not self.builder.customized:
self.builder.customized = True
self.builder.configuration.selected_image = self.recipe_model.__custom_image__
self.builder.configuration.selected_image = self.recipe_model.__dummy_image__
self.builder.rcppkglist_populated()
self.builder.window_sensitive(True)
@@ -282,7 +247,3 @@ class PackageSelectionPage (HobPage):
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):
self.ins.set_current_page(curr_page)

View File

@@ -125,17 +125,11 @@ class PersistentTooltip(gtk.Window):
style.fg[gtk.STATE_NORMAL] = gtk.gdk.color_parse(val)
self.label.set_style(style)
break # we only care for the tooltip_fg_color
self.label.set_markup(markup)
self.label.show()
bin.add(self.label)
hbox.pack_end(bin, True, True, 6)
# add the original URL display for user reference
if 'a href' in markup:
hbox.set_tooltip_text(self.get_markup_url(markup))
hbox.show()
self.connect("key-press-event", self._catch_esc_cb)
"""
@@ -171,16 +165,3 @@ class PersistentTooltip(gtk.Window):
def hide(self):
self.shown = False
gtk.Window.hide(self)
"""
Called to get the hyperlink URL from markup text.
"""
def get_markup_url(self, markup):
url = "http:"
if markup and type(markup) == str:
s = markup
if 'http:' in s:
import re
url = re.search('(http:[^,\\ "]+)', s).group(0)
return url

View File

@@ -11,9 +11,6 @@ class ProgressBar(gtk.Dialog):
self.vbox.pack_start(self.progress)
self.show_all()
def set_text(self, msg):
self.progress.set_text(msg)
def update(self, x, y):
self.progress.set_fraction(float(x)/float(y))
self.progress.set_text("%2d %%" % (x*100/y))

View File

@@ -43,11 +43,6 @@ class HobProgressBar (gtk.ProgressBar):
text += " %.0f%%" % self.percentage
self.set_text(text)
def set_stop_title(self, text=None):
if not text:
text = ""
self.set_text(text)
def reset(self):
self.set_fraction(0)
self.set_text("")

View File

@@ -33,10 +33,10 @@ from bb.ui.crumbs.hobpages import HobPage
class RecipeSelectionPage (HobPage):
pages = [
{
'name' : 'Included recipes',
'name' : 'Included',
'tooltip' : 'The recipes currently included for your image',
'filter' : { RecipeListModel.COL_INC : [True],
RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] },
RecipeListModel.COL_TYPE : ['recipe', 'task'] },
'columns' : [{
'col_name' : 'Recipe name',
'col_id' : RecipeListModel.COL_NAME,
@@ -44,6 +44,13 @@ class RecipeSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Brought in by',
'col_id' : RecipeListModel.COL_BINB,
'col_style': 'binb',
'col_min' : 100,
'col_max' : 500,
'expand' : 'True'
}, {
'col_name' : 'Group',
'col_id' : RecipeListModel.COL_GROUP,
@@ -51,13 +58,6 @@ class RecipeSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 300,
'expand' : 'True'
}, {
'col_name' : 'Brought in by (+others)',
'col_id' : RecipeListModel.COL_BINB,
'col_style': 'binb',
'col_min' : 100,
'col_max' : 500,
'expand' : 'True'
}, {
'col_name' : 'Included',
'col_id' : RecipeListModel.COL_INC,
@@ -67,7 +67,7 @@ class RecipeSelectionPage (HobPage):
}]
}, {
'name' : 'All recipes',
'tooltip' : 'All recipes in your configured layers',
'tooltip' : 'All recipes available in the Yocto Project',
'filter' : { RecipeListModel.COL_TYPE : ['recipe'] },
'columns' : [{
'col_name' : 'Recipe name',
@@ -76,13 +76,6 @@ class RecipeSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Group',
'col_id' : RecipeListModel.COL_GROUP,
'col_style': 'text',
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'License',
'col_id' : RecipeListModel.COL_LIC,
@@ -90,6 +83,13 @@ class RecipeSelectionPage (HobPage):
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Group',
'col_id' : RecipeListModel.COL_GROUP,
'col_style': 'text',
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Included',
'col_id' : RecipeListModel.COL_INC,
@@ -98,16 +98,23 @@ class RecipeSelectionPage (HobPage):
'col_max' : 100
}]
}, {
'name' : 'Package Groups',
'tooltip' : 'All package groups in your configured layers',
'filter' : { RecipeListModel.COL_TYPE : ['packagegroup'] },
'name' : 'Tasks',
'tooltip' : 'All tasks available in the Yocto Project',
'filter' : { RecipeListModel.COL_TYPE : ['task'] },
'columns' : [{
'col_name' : 'Package group name',
'col_name' : 'Task name',
'col_id' : RecipeListModel.COL_NAME,
'col_style': 'text',
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Description',
'col_id' : RecipeListModel.COL_DESC,
'col_style': 'text',
'col_min' : 100,
'col_max' : 400,
'expand' : 'True'
}, {
'col_name' : 'Included',
'col_id' : RecipeListModel.COL_INC,
@@ -117,29 +124,23 @@ class RecipeSelectionPage (HobPage):
}]
}
]
(INCLUDED,
ALL,
TASKS) = range(3)
def __init__(self, builder = None):
super(RecipeSelectionPage, self).__init__(builder, "Step 1 of 2: Edit recipes")
super(RecipeSelectionPage, self).__init__(builder, "Recipes")
# set invisible members
# set invisiable members
self.recipe_model = self.builder.recipe_model
# create visual elements
self.create_visual_elements()
def included_clicked_cb(self, button):
self.ins.set_current_page(self.INCLUDED)
def create_visual_elements(self):
self.eventbox = self.add_onto_top_bar(None, 73)
self.label = gtk.Label()
self.eventbox = self.add_onto_top_bar(self.label, 73)
self.pack_start(self.eventbox, expand=False, fill=False)
self.pack_start(self.group_align, expand=True, fill=True)
# set visible members
# set visiable members
self.ins = HobNotebook()
self.tables = [] # we need modify table when the dialog is shown
# append the tabs in order
@@ -149,10 +150,13 @@ class RecipeSelectionPage (HobPage):
filter = page['filter']
tab.set_model(self.recipe_model.tree_model(filter))
tab.connect("toggled", self.table_toggled_cb, page['name'])
if page['name'] == "Included recipes":
if page['name'] == "Included":
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'])
label = gtk.Label(page['name'])
label.set_selectable(False)
label.set_tooltip_text(page['tooltip'])
self.ins.append_page(tab, label)
self.tables.append(tab)
self.ins.set_entry("Search recipes:")
@@ -170,16 +174,16 @@ class RecipeSelectionPage (HobPage):
self.box_group_area.pack_end(button_box, expand=False, fill=False)
self.build_packages_button = HobButton('Build packages')
#self.build_packages_button.set_size_request(205, 49)
self.build_packages_button.set_size_request(205, 49)
self.build_packages_button.set_tooltip_text("Build selected recipes into packages")
self.build_packages_button.set_flags(gtk.CAN_DEFAULT)
self.build_packages_button.grab_default()
self.build_packages_button.connect("clicked", self.build_packages_clicked_cb)
button_box.pack_end(self.build_packages_button, expand=False, fill=False)
self.back_button = HobAltButton('Cancel')
self.back_button = HobAltButton("<< Back to image configuration")
self.back_button.connect("clicked", self.back_button_clicked_cb)
button_box.pack_end(self.back_button, expand=False, fill=False)
button_box.pack_start(self.back_button, expand=False, fill=False)
def button_click_cb(self, widget, event):
path, col = widget.table_tree.get_cursor()
@@ -193,21 +197,19 @@ class RecipeSelectionPage (HobPage):
self.builder.build_packages()
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()
def refresh_selection(self):
self.builder.configuration.selected_image = self.recipe_model.get_selected_image()
_, self.builder.configuration.selected_recipes = self.recipe_model.get_selected_recipes()
self.ins.show_indicator_icon("Included recipes", len(self.builder.configuration.selected_recipes))
self.label.set_text("Recipes included: %s" % len(self.builder.configuration.selected_recipes))
self.ins.show_indicator_icon("Included", len(self.builder.configuration.selected_recipes))
def toggle_item_idle_cb(self, path, view_tree, cell, pagename):
if not self.recipe_model.path_included(path):
self.recipe_model.include_item(item_path=path, binb="User Selected", image_contents=False)
else:
if pagename == "Included recipes":
if pagename == "Included":
self.pre_fadeout_checkout_include(view_tree)
self.recipe_model.exclude_item(item_path=path)
self.render_fadeout(view_tree, cell)
@@ -217,7 +219,7 @@ class RecipeSelectionPage (HobPage):
self.refresh_selection()
if not self.builder.customized:
self.builder.customized = True
self.builder.configuration.selected_image = self.recipe_model.__custom_image__
self.builder.configuration.selected_image = self.recipe_model.__dummy_image__
self.builder.rcppkglist_populated()
self.builder.window_sensitive(True)
@@ -239,7 +241,7 @@ class RecipeSelectionPage (HobPage):
# 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],
RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] }
RecipeListModel.COL_TYPE : ['recipe', 'task'] }
new_model = self.recipe_model.tree_model(filter, excluded_items_ahead=True)
tree.set_model(new_model)
@@ -261,6 +263,3 @@ class RecipeSelectionPage (HobPage):
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)

View File

@@ -46,7 +46,7 @@ class RunningBuildModel (gtk.TreeStore):
color = model.get(it, self.COL_COLOR)[0]
if not color:
return False
if color == HobColors.ERROR or color == HobColors.WARNING:
if color == HobColors.ERROR:
return True
return False
@@ -76,27 +76,15 @@ class RunningBuild (gobject.GObject):
'build-complete' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
'build-aborted' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
'task-started' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_PYOBJECT,)),
'log-error' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
'log-warning' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
'disk-full' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
'no-provider' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_PYOBJECT,)),
'log' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)),
}
pids_to_task = {}
tasks_to_iter = {}
@@ -105,7 +93,6 @@ class RunningBuild (gobject.GObject):
gobject.GObject.__init__ (self)
self.model = RunningBuildModel()
self.sequential = sequential
self.buildaborted = False
def reset (self):
self.pids_to_task.clear()
@@ -135,8 +122,6 @@ class RunningBuild (gobject.GObject):
parent = self.tasks_to_iter[(package, task)]
if(isinstance(event, logging.LogRecord)):
if event.taskpid == 0 or event.levelno > logging.INFO:
self.emit("log", "handle", event)
# FIXME: this is a hack! More info in Yocto #1433
# http://bugzilla.pokylinux.org/show_bug.cgi?id=1433, temporarily
# mask the error message as it's not informative for the user.
@@ -154,7 +139,6 @@ class RunningBuild (gobject.GObject):
elif event.levelno >= logging.WARNING:
icon = "dialog-warning"
color = HobColors.WARNING
self.emit("log-warning")
else:
icon = None
color = HobColors.OK
@@ -223,7 +207,6 @@ class RunningBuild (gobject.GObject):
self.tasks_to_iter[(package, task)] = i
elif isinstance(event, bb.build.TaskBase):
self.emit("log", "info", event._message)
current = self.tasks_to_iter[(package, task)]
parent = self.tasks_to_iter[(package, None)]
@@ -291,10 +274,7 @@ class RunningBuild (gobject.GObject):
0))
# Emit the appropriate signal depending on the number of failures
if self.buildaborted:
self.emit ("build-aborted")
self.buildaborted = False
elif (failures >= 1):
if (failures >= 1):
self.emit ("build-failed")
else:
self.emit ("build-succeeded")
@@ -306,12 +286,7 @@ class RunningBuild (gobject.GObject):
if pbar:
pbar.set_text(event.msg)
elif isinstance(event, bb.event.DiskFull):
self.buildaborted = True
self.emit("disk-full")
elif isinstance(event, bb.command.CommandFailed):
self.emit("log", "error", "Command execution failed: %s" % (event.error))
if event.error.startswith("Exited with"):
# If the command fails with an exit code we're done, emit the
# generic signal for the UI to notify the user
@@ -339,24 +314,7 @@ class RunningBuild (gobject.GObject):
elif isinstance(event, bb.event.ParseCompleted) and pbar:
pbar.hide()
#using runqueue events as many as possible to update the progress bar
elif isinstance(event, bb.runqueue.runQueueTaskFailed):
self.emit("log", "error", "Task %s (%s) failed with exit code '%s'" % (event.taskid, event.taskstring, event.exitcode))
elif isinstance(event, bb.runqueue.sceneQueueTaskFailed):
self.emit("log", "warn", "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead" \
% (event.taskid, event.taskstring, event.exitcode))
elif isinstance(event, (bb.runqueue.runQueueTaskStarted, bb.runqueue.sceneQueueTaskStarted)):
if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
self.emit("log", "info", "Running setscene task %d of %d (%s)" % \
(event.stats.completed + event.stats.active + event.stats.failed + 1,
event.stats.total, event.taskstring))
else:
if event.noexec:
tasktype = 'noexec task'
else:
tasktype = 'task'
self.emit("log", "info", "Running %s %s of %s (ID: %s, %s)" % \
(tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1,
event.stats.total, event.taskid, event.taskstring))
message = {}
message["eventname"] = bb.event.getName(event)
num_of_completed = event.stats.completed + event.stats.failed
@@ -365,10 +323,6 @@ class RunningBuild (gobject.GObject):
message["title"] = ""
message["task"] = event.taskstring
self.emit("task-started", message)
elif isinstance(event, bb.event.MultipleProviders):
self.emit("log", "info", "multiple providers are available for %s%s (%s)" \
% (event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)))
self.emit("log", "info", "consider defining a PREFERRED_PROVIDER entry to match %s" % (event._item))
elif isinstance(event, bb.event.NoProvider):
msg = ""
if event._runtime:
@@ -383,34 +337,6 @@ class RunningBuild (gobject.GObject):
for reason in event._reasons:
msg += ("%s\n" % reason)
self.emit("no-provider", msg)
self.emit("log", "error", msg)
elif isinstance(event, bb.event.LogExecTTY):
icon = "dialog-warning"
color = HobColors.WARNING
if self.sequential or not parent:
tree_add = self.model.append
else:
tree_add = self.model.prepend
tree_add(parent,
(None,
package,
task,
event.msg,
icon,
color,
0))
else:
if not isinstance(event, (bb.event.BuildBase,
bb.event.StampUpdate,
bb.event.ConfigParsed,
bb.event.RecipeParsed,
bb.event.RecipePreFinalise,
bb.runqueue.runQueueEvent,
bb.runqueue.runQueueExitWait,
bb.event.OperationStarted,
bb.event.OperationCompleted,
bb.event.OperationProgress)):
self.emit("log", "error", "Unknown event: %s" % (event.error if hasattr(event, 'error') else 'error'))
return

View File

@@ -1,85 +0,0 @@
#!/usr/bin/env python
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2012 Intel Corporation
#
# Authored by Bogdan Marinescu <bogdan.a.marinescu@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, gobject
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.hobwidget import hic
from bb.ui.crumbs.hobpages import HobPage
#
# SanityCheckPage
#
class SanityCheckPage (HobPage):
def __init__(self, builder):
super(SanityCheckPage, self).__init__(builder)
self.running = False
self.create_visual_elements()
self.show_all()
def make_label(self, text, bold=True):
label = gtk.Label()
label.set_alignment(0.0, 0.5)
mark = "<span %s>%s</span>" % (self.span_tag('x-large', 'bold') if bold else self.span_tag('medium'), text)
label.set_markup(mark)
return label
def start(self):
if not self.running:
self.running = True
gobject.timeout_add(100, self.timer_func)
def stop(self):
self.running = False
def is_running(self):
return self.running
def timer_func(self):
self.progress_bar.pulse()
return self.running
def create_visual_elements(self):
# Table'd layout. 'rows' and 'cols' give the table size
rows, cols = 30, 50
self.table = gtk.Table(rows, cols, True)
self.pack_start(self.table, expand=False, fill=False)
sx, sy = 2, 2
# 'info' icon
image = gtk.Image()
image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
self.table.attach(image, sx, sx + 2, sy, sy + 3 )
image.show()
# 'Checking' message
label = self.make_label('Hob is checking for correct build system setup')
self.table.attach(label, sx + 2, cols, sy, sy + 3, xpadding=5 )
label.show()
# 'Shouldn't take long' message.
label = self.make_label("The check shouldn't take long.", False)
self.table.attach(label, sx + 2, cols, sy + 3, sy + 4, xpadding=5)
label.show()
# Progress bar
self.progress_bar = HobProgressBar()
self.table.attach(self.progress_bar, sx + 2, cols - 3, sy + 5, sy + 7, xpadding=5)
self.progress_bar.show()
# All done
self.table.show()

View File

@@ -101,19 +101,7 @@ class HobTemplateFile(ConfigFile):
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()
@@ -137,7 +125,7 @@ class RecipeFile(ConfigFile):
class TemplateMgr(gobject.GObject):
__gLocalVars__ = ["MACHINE", "PACKAGE_CLASSES", "DISTRO", "DL_DIR", "SSTATE_DIR", "SSTATE_MIRRORS", "PARALLEL_MAKE", "BB_NUMBER_THREADS", "CONF_VERSION"]
__gLocalVars__ = ["MACHINE", "PACKAGE_CLASSES", "DISTRO", "DL_DIR", "SSTATE_DIR", "SSTATE_MIRROR", "PARALLEL_MAKE", "BB_NUMBER_THREADS", "CONF_VERSION"]
__gBBLayersVars__ = ["BBLAYERS", "LCONF_VERSION"]
__gRecipeVars__ = ["DEPENDS", "IMAGE_INSTALL"]
@@ -186,9 +174,6 @@ class TemplateMgr(gobject.GObject):
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()

View File

@@ -22,7 +22,6 @@
# bitbake which will allow more flexibility.
import os
import bb
def which_terminal():
term = bb.utils.which(os.environ["PATH"], "xterm")

View File

@@ -24,7 +24,7 @@ import threading
import xmlrpclib
import bb
import bb.event
from bb.ui.crumbs.progressbar import HobProgressBar
from bb.ui.crumbs.progress import ProgressBar
# Package Model
(COL_PKG_NAME) = (0)
@@ -198,23 +198,17 @@ class gtkthread(threading.Thread):
def main(server, eventHandler):
try:
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
elif not cmdline or cmdline[0] != "generateDotGraph":
cmdline = server.runCommand(["getCmdLineAction"])
if cmdline and not cmdline['action']:
print(cmdline['msg'])
return
elif not cmdline or (cmdline['action'] and cmdline['action'][0] != "generateDotGraph"):
print("This UI is only compatible with the -g option")
return 1
ret, error = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
if error:
print("Error running command '%s': %s" % (cmdline, error))
return 1
elif ret != True:
print("Error running command '%s': returned %s" % (cmdline, ret))
return 1
return
ret = server.runCommand(["generateDepTreeEvent", cmdline['action'][1], cmdline['action'][2]])
if ret != True:
print("Couldn't run command! %s" % ret)
return
except xmlrpclib.Fault as x:
print("XMLRPC Fault getting commandline:\n %s" % x)
return
@@ -226,13 +220,8 @@ def main(server, eventHandler):
gtk.gdk.threads_enter()
dep = DepExplorer()
bardialog = gtk.Dialog(parent=dep,
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
bardialog.set_default_size(400, 50)
pbar = HobProgressBar()
bardialog.vbox.pack_start(pbar)
bardialog.show_all()
bardialog.connect("delete-event", gtk.main_quit)
pbar = ProgressBar(dep)
pbar.connect("delete-event", gtk.main_quit)
gtk.gdk.threads_leave()
progress_total = 0
@@ -240,9 +229,7 @@ def main(server, eventHandler):
try:
event = eventHandler.waitEvent(0.25)
if gtkthread.quit.isSet():
_, error = server.runCommand(["stateStop"])
if error:
print('Unable to cleanly stop: %s' % error)
server.runCommand(["stateStop"])
break
if event is None:
@@ -251,20 +238,19 @@ def main(server, eventHandler):
if isinstance(event, bb.event.CacheLoadStarted):
progress_total = event.total
gtk.gdk.threads_enter()
bardialog.set_title("Loading Cache")
pbar.update(0)
pbar.set_title("Loading Cache")
pbar.update(0, progress_total)
gtk.gdk.threads_leave()
if isinstance(event, bb.event.CacheLoadProgress):
x = event.current
gtk.gdk.threads_enter()
pbar.update(x * 1.0 / progress_total)
pbar.set_title('')
pbar.update(x, progress_total)
gtk.gdk.threads_leave()
continue
if isinstance(event, bb.event.CacheLoadCompleted):
bardialog.hide()
pbar.hide()
continue
if isinstance(event, bb.event.ParseStarted):
@@ -272,21 +258,19 @@ def main(server, eventHandler):
if progress_total == 0:
continue
gtk.gdk.threads_enter()
pbar.update(0)
bardialog.set_title("Processing recipes")
pbar.set_title("Processing recipes")
pbar.update(0, progress_total)
gtk.gdk.threads_leave()
if isinstance(event, bb.event.ParseProgress):
x = event.current
gtk.gdk.threads_enter()
pbar.update(x * 1.0 / progress_total)
pbar.set_title('')
pbar.update(x, progress_total)
gtk.gdk.threads_leave()
continue
if isinstance(event, bb.event.ParseCompleted):
bardialog.hide()
pbar.hide()
continue
if isinstance(event, bb.event.DepTreeGenerated):
@@ -318,13 +302,9 @@ def main(server, eventHandler):
break
if shutdown == 1:
print("\nSecond Keyboard Interrupt, stopping...\n")
_, error = server.runCommand(["stateStop"])
if error:
print('Unable to cleanly stop: %s' % error)
server.runCommand(["stateStop"])
if shutdown == 0:
print("\nKeyboard Interrupt, closing down...\n")
_, error = server.runCommand(["stateShutdown"])
if error:
print('Unable to cleanly shutdown: %s' % error)
server.runCommand(["stateShutdown"])
shutdown = shutdown + 1
pass

View File

@@ -80,19 +80,16 @@ def main (server, eventHandler):
running_build.connect ("build-failed", running_build_failed_cb)
try:
cmdline, error = server.runCommand(["getCmdLineAction"])
if err:
print("Error getting bitbake commandline: %s" % error)
return 1
elif not cmdline:
cmdline = 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
ret, error = server.runCommand(cmdline)
if error:
print("Error running command '%s': %s" % (cmdline, error))
elif not cmdline['action']:
print(cmdline['msg'])
return 1
elif ret != True:
print("Error running command '%s': returned %s" % (cmdline, ret))
ret = server.runCommand(cmdline['action'])
if ret != True:
print("Couldn't get default commandline! %s" % ret)
return 1
except xmlrpclib.Fault as x:
print("XMLRPC Fault getting commandline:\n %s" % x)

View File

@@ -22,7 +22,7 @@
import sys
import os
requirements = "FATAL: Hob requires Gtk+ 2.20.0 or higher, PyGtk 2.21.0 or higher"
requirements = "FATAL: Gtk+, PyGtk and PyGobject are required to use Hob"
try:
import gobject
import gtk
@@ -30,7 +30,7 @@ try:
pygtk.require('2.0') # to be certain we don't have gtk+ 1.x !?!
gtkver = gtk.gtk_version
pygtkver = gtk.pygtk_version
if gtkver < (2, 20, 0) or pygtkver < (2, 21, 0):
if gtkver < (2, 18, 0) or pygtkver < (2, 16, 0):
sys.exit("%s,\nYou have Gtk+ %s and PyGtk %s." % (requirements,
".".join(map(str, gtkver)),
".".join(map(str, pygtkver))))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -25,12 +25,7 @@ import sys
import xmlrpclib
import logging
import progressbar
import signal
import bb.msg
import time
import fcntl
import struct
import copy
from bb.ui import uihelper
logger = logging.getLogger("BitBake")
@@ -42,21 +37,8 @@ class BBProgress(progressbar.ProgressBar):
widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ',
progressbar.ETA()]
try:
self._resize_default = signal.getsignal(signal.SIGWINCH)
except:
self._resize_default = None
progressbar.ProgressBar.__init__(self, maxval, [self.msg + ": "] + widgets)
def _handle_resize(self, signum, frame):
progressbar.ProgressBar._handle_resize(self, signum, frame)
if self._resize_default:
self._resize_default(signum, frame)
def finish(self):
progressbar.ProgressBar.finish(self)
if self._resize_default:
signal.signal(signal.SIGWINCH, self._resize_default)
class NonInteractiveProgress(object):
fobj = sys.stdout
@@ -88,188 +70,74 @@ def pluralise(singular, plural, qty):
else:
return plural % qty
class InteractConsoleLogFilter(logging.Filter):
def __init__(self, tf, format):
self.tf = tf
self.format = format
def filter(self, record):
if record.levelno == self.format.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")):
return False
self.tf.clearFooter()
return True
class TerminalFilter(object):
columns = 80
def sigwinch_handle(self, signum, frame):
self.columns = self.getTerminalColumns()
if self._sigwinch_default:
self._sigwinch_default(signum, frame)
def getTerminalColumns(self):
def ioctl_GWINSZ(fd):
try:
cr = struct.unpack('hh', fcntl.ioctl(fd, self.termios.TIOCGWINSZ, '1234'))
except:
return None
return cr
cr = ioctl_GWINSZ(sys.stdout.fileno())
if not cr:
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
cr = ioctl_GWINSZ(fd)
os.close(fd)
except:
pass
if not cr:
try:
cr = (env['LINES'], env['COLUMNS'])
except:
cr = (25, 80)
return cr[1]
def __init__(self, main, helper, console, format):
self.main = main
self.helper = helper
self.cuu = None
self.stdinbackup = None
self.interactive = sys.stdout.isatty()
self.footer_present = False
self.lastpids = []
if not self.interactive:
return
try:
import curses
except ImportError:
sys.exit("FATAL: The knotty ui could not load the required curses python module.")
import termios
self.curses = curses
self.termios = termios
try:
fd = sys.stdin.fileno()
self.stdinbackup = termios.tcgetattr(fd)
new = copy.deepcopy(self.stdinbackup)
new[3] = new[3] & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSADRAIN, new)
curses.setupterm()
if curses.tigetnum("colors") > 2:
format.enable_color()
self.ed = curses.tigetstr("ed")
if self.ed:
self.cuu = curses.tigetstr("cuu")
try:
self._sigwinch_default = signal.getsignal(signal.SIGWINCH)
signal.signal(signal.SIGWINCH, self.sigwinch_handle)
except:
pass
self.columns = self.getTerminalColumns()
except:
self.cuu = None
console.addFilter(InteractConsoleLogFilter(self, format))
def clearFooter(self):
if self.footer_present:
lines = self.footer_present
sys.stdout.write(self.curses.tparm(self.cuu, lines))
sys.stdout.write(self.curses.tparm(self.ed))
self.footer_present = False
return
def updateFooter(self):
if not self.cuu:
if not main.shutdown or not self.helper.needUpdate:
return
activetasks = self.helper.running_tasks
failedtasks = self.helper.failed_tasks
runningpids = self.helper.running_pids
if self.footer_present and (self.lastcount == self.helper.tasknumber_current) and (self.lastpids == runningpids):
return
if self.footer_present:
self.clearFooter()
if (not self.helper.tasknumber_total or self.helper.tasknumber_current == self.helper.tasknumber_total) and not len(activetasks):
if len(runningpids) == 0:
return
self.helper.getTasks()
tasks = []
for t in runningpids:
tasks.append("%s (pid %s)" % (activetasks[t]["title"], t))
if self.main.shutdown:
content = "Waiting for %s running tasks to finish:" % len(activetasks)
elif not len(activetasks):
content = "No currently running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total)
if main.shutdown:
print("Waiting for %s running tasks to finish:" % len(activetasks))
else:
content = "Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total)
print content
lines = 1 + int(len(content) / (self.columns + 1))
print("Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total))
for tasknum, task in enumerate(tasks):
content = "%s: %s" % (tasknum, task)
print content
lines = lines + 1 + int(len(content) / (self.columns + 1))
self.footer_present = lines
self.lastpids = runningpids[:]
self.lastcount = self.helper.tasknumber_current
print("%s: %s" % (tasknum, task))
def finish(self):
if self.stdinbackup:
fd = sys.stdin.fileno()
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
return
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)
return 1
loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
if error:
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % 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)
return 1
if sys.stdin.isatty() and sys.stdout.isatty():
log_exec_tty = True
else:
log_exec_tty = False
includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"])
loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
consolelogfile = server.runCommand(["getVariable", "BB_CONSOLELOG"])
helper = uihelper.BBUIHelper()
console = logging.StreamHandler(sys.stdout)
format_str = "%(levelname)s: %(message)s"
format = bb.msg.BBLogFormatter(format_str)
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
bb.msg.addDefaultlogFilter(console)
console.setFormatter(format)
logger.addHandler(console)
if consolelogfile:
bb.utils.mkdirhier(os.path.dirname(consolelogfile))
conlogformat = bb.msg.BBLogFormatter(format_str)
consolelog = logging.FileHandler(consolelogfile)
bb.msg.addDefaultlogFilter(consolelog)
consolelog.setFormatter(conlogformat)
consolelog.setFormatter(format)
logger.addHandler(consolelog)
try:
cmdline, error = server.runCommand(["getCmdLineAction"])
if error:
logger.error("Unable to get bitbake commandline arguments: %s" % error)
return 1
elif not cmdline:
cmdline = 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
ret, error = server.runCommand(cmdline)
if error:
logger.error("Command '%s' failed: %s" % (cmdline, error))
elif not cmdline['action']:
print(cmdline['msg'])
return 1
elif ret != True:
logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
ret = server.runCommand(cmdline['action'])
if ret != True:
print("Couldn't get default commandline! %s" % ret)
return 1
except xmlrpclib.Fault as x:
logger.error("XMLRPC Fault getting commandline:\n %s" % x)
print("XMLRPC Fault getting commandline:\n %s" % x)
return 1
parseprogress = None
@@ -296,20 +164,6 @@ def main(server, eventHandler, tf = TerminalFilter):
if not main.shutdown:
main.shutdown = 1
if isinstance(event, bb.event.LogExecTTY):
if log_exec_tty:
tries = event.retries
while tries:
print "Trying to run: %s" % event.prog
if os.system(event.prog) == 0:
break
time.sleep(event.sleep_delay)
tries -= 1
if tries:
continue
logger.warn(event.msg)
continue
if isinstance(event, logging.LogRecord):
if event.levelno >= format.ERROR:
errors = errors + 1
@@ -329,7 +183,7 @@ def main(server, eventHandler, tf = TerminalFilter):
logfile = event.logfile
if logfile and os.path.exists(logfile):
termfilter.clearFooter()
bb.error("Logfile of failure stored in: %s" % logfile)
print("ERROR: Logfile of failure stored in: %s" % logfile)
if includelogs and not event.errprinted:
print("Log data follows:")
f = open(logfile, "r")
@@ -467,15 +321,11 @@ def main(server, eventHandler, tf = TerminalFilter):
termfilter.clearFooter()
if main.shutdown == 1:
print("\nSecond Keyboard Interrupt, stopping...\n")
_, error = server.runCommand(["stateStop"])
if error:
logger.error("Unable to cleanly stop: %s" % error)
server.runCommand(["stateStop"])
if main.shutdown == 0:
print("\nKeyboard Interrupt, closing down...\n")
interrupted = True
_, error = server.runCommand(["stateShutdown"])
if error:
logger.error("Unable to cleanly shutdown: %s" % error)
print("\nKeyboard Interrupt, closing down...\n")
server.runCommand(["stateShutdown"])
main.shutdown = main.shutdown + 1
pass

View File

@@ -0,0 +1,109 @@
#
# BitBake (No)TTY UI Implementation (v2)
#
# Handling output to TTYs or files (no TTY)
#
# Copyright (C) 2012 Richard Purdie
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from bb.ui import knotty
import logging
import sys
logger = logging.getLogger("BitBake")
class InteractConsoleLogFilter(logging.Filter):
def __init__(self, tf, format):
self.tf = tf
self.format = format
def filter(self, record):
if record.levelno == self.format.NOTE and (record.msg.startswith("Running") or record.msg.startswith("package ")):
return False
self.tf.clearFooter()
return True
class TerminalFilter2(object):
def __init__(self, main, helper, console, format):
self.main = main
self.helper = helper
self.cuu = None
self.stdinbackup = None
self.interactive = sys.stdout.isatty()
self.footer_present = False
self.lastpids = []
if not self.interactive:
return
import curses
import termios
import copy
self.curses = curses
self.termios = termios
try:
fd = sys.stdin.fileno()
self.stdinbackup = termios.tcgetattr(fd)
new = copy.deepcopy(self.stdinbackup)
new[3] = new[3] & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSADRAIN, new)
curses.setupterm()
self.ed = curses.tigetstr("ed")
if self.ed:
self.cuu = curses.tigetstr("cuu")
except:
self.cuu = None
console.addFilter(InteractConsoleLogFilter(self, format))
def clearFooter(self):
if self.footer_present:
lines = self.footer_present
sys.stdout.write(self.curses.tparm(self.cuu, lines))
sys.stdout.write(self.curses.tparm(self.ed))
self.footer_present = False
def updateFooter(self):
if not self.cuu:
return
activetasks = self.helper.running_tasks
failedtasks = self.helper.failed_tasks
runningpids = self.helper.running_pids
if self.footer_present and (self.lastpids == runningpids):
return
if self.footer_present:
self.clearFooter()
if not activetasks:
return
lines = 1
tasks = []
for t in runningpids:
tasks.append("%s (pid %s)" % (activetasks[t]["title"], t))
if self.main.shutdown:
print("Waiting for %s running tasks to finish:" % len(activetasks))
else:
print("Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total))
for tasknum, task in enumerate(tasks):
print("%s: %s" % (tasknum, task))
lines = lines + 1
self.footer_present = lines
self.lastpids = runningpids[:]
def finish(self):
if self.stdinbackup:
fd = sys.stdin.fileno()
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
def main(server, eventHandler):
bb.ui.knotty.main(server, eventHandler, TerminalFilter2)

View File

@@ -47,13 +47,7 @@
from __future__ import division
import logging
import os, sys, itertools, time, subprocess
try:
import curses
except ImportError:
sys.exit("FATAL: The ncurses ui could not load the required curses python module.")
import os, sys, curses, itertools, time
import bb
import xmlrpclib
from bb import ui
@@ -236,18 +230,15 @@ class NCursesUI:
shutdown = 0
try:
cmdline, error = server.runCommand(["getCmdLineAction"])
cmdline = server.runCommand(["getCmdLineAction"])
if not cmdline:
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
return
elif error:
print("Error getting bitbake commandline: %s" % error)
elif not cmdline['action']:
print(cmdline['msg'])
return
ret, error = server.runCommand(cmdline)
if error:
print("Error running command '%s': %s" % (cmdline, error))
return
elif ret != True:
ret = server.runCommand(cmdline['action'])
if ret != True:
print("Couldn't get default commandlind! %s" % ret)
return
except xmlrpclib.Fault as x:
@@ -295,7 +286,7 @@ class NCursesUI:
# bb.error("log data follows (%s)" % logfile)
# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d)
# if number_of_lines:
# subprocess.call('tail -n%s %s' % (number_of_lines, logfile), shell=True)
# os.system('tail -n%s %s' % (number_of_lines, logfile))
# else:
# f = open(logfile, "r")
# while True:
@@ -321,8 +312,6 @@ class NCursesUI:
if isinstance(event, bb.cooker.CookerExit):
exitflag = True
if isinstance(event, bb.event.LogExecTTY):
mw.appendText('WARN: ' + event.msg + '\n')
if helper.needUpdate:
activetasks, failedtasks = helper.getTasks()
taw.erase()
@@ -348,14 +337,10 @@ class NCursesUI:
exitflag = True
if shutdown == 1:
mw.appendText("Second Keyboard Interrupt, stopping...\n")
_, error = server.runCommand(["stateStop"])
if error:
print("Unable to cleanly stop: %s" % error)
server.runCommand(["stateStop"])
if shutdown == 0:
mw.appendText("Keyboard Interrupt, closing down...\n")
_, error = server.runCommand(["stateShutdown"])
if error:
print("Unable to cleanly shutdown: %s" % error)
server.runCommand(["stateShutdown"])
shutdown = shutdown + 1
pass

View File

@@ -37,17 +37,6 @@ class BBUIEventQueue:
self.BBServer = BBServer
self.clientinfo = clientinfo
server = UIXMLRPCServer(self.clientinfo)
self.host, self.port = server.socket.getsockname()
server.register_function( self.system_quit, "event.quit" )
server.register_function( self.send_event, "event.sendpickle" )
server.socket.settimeout(1)
self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port)
self.server = server
self.t = threading.Thread()
self.t.setDaemon(True)
self.t.run = self.startCallbackHandler
@@ -84,9 +73,19 @@ class BBUIEventQueue:
def startCallbackHandler(self):
while not self.server.quit:
self.server.handle_request()
self.server.server_close()
server = UIXMLRPCServer(self.clientinfo)
self.host, self.port = server.socket.getsockname()
server.register_function( self.system_quit, "event.quit" )
server.register_function( self.send_event, "event.sendpickle" )
server.socket.settimeout(1)
self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port)
self.server = server
while not server.quit:
server.handle_request()
server.server_close()
def system_quit( self ):
"""

View File

@@ -48,10 +48,9 @@ class BBUIHelper:
self.running_pids.remove(event.pid)
self.failed_tasks.append( { 'title' : "%s %s" % (event._package, event._task)})
self.needUpdate = True
if isinstance(event, bb.runqueue.runQueueTaskStarted) or isinstance(event, bb.runqueue.sceneQueueTaskStarted):
if isinstance(event, bb.runqueue.runQueueTaskStarted):
self.tasknumber_current = event.stats.completed + event.stats.active + event.stats.failed + 1
self.tasknumber_total = event.stats.total
self.needUpdate = True
def getTasks(self):
self.needUpdate = False

View File

@@ -26,7 +26,6 @@ import logging
import bb
import bb.msg
import multiprocessing
import fcntl
from commands import getstatusoutput
from contextlib import contextmanager
@@ -109,10 +108,130 @@ def vercmp(ta, tb):
r = vercmp_part(ra, rb)
return r
def vercmp_string(a, b):
ta = split_version(a)
tb = split_version(b)
return vercmp(ta, tb)
_package_weights_ = {"pre":-2, "p":0, "alpha":-4, "beta":-3, "rc":-1} # dicts are unordered
_package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
def relparse(myver):
"""Parses the last elements of a version number into a triplet, that can
later be compared.
"""
number = 0
p1 = 0
p2 = 0
mynewver = myver.split('_')
if len(mynewver) == 2:
# an _package_weights_
number = float(mynewver[0])
match = 0
for x in _package_ends_:
elen = len(x)
if mynewver[1][:elen] == x:
match = 1
p1 = _package_weights_[x]
try:
p2 = float(mynewver[1][elen:])
except:
p2 = 0
break
if not match:
# normal number or number with letter at end
divider = len(myver)-1
if myver[divider:] not in "1234567890":
# letter at end
p1 = ord(myver[divider:])
number = float(myver[0:divider])
else:
number = float(myver)
else:
# normal number or number with letter at end
divider = len(myver)-1
if myver[divider:] not in "1234567890":
#letter at end
p1 = ord(myver[divider:])
number = float(myver[0:divider])
else:
number = float(myver)
return [number, p1, p2]
__vercmp_cache__ = {}
def vercmp_string(val1, val2):
"""This takes two version strings and returns an integer to tell you whether
the versions are the same, val1>val2 or val2>val1.
"""
# quick short-circuit
if val1 == val2:
return 0
valkey = val1 + " " + val2
# cache lookup
try:
return __vercmp_cache__[valkey]
try:
return - __vercmp_cache__[val2 + " " + val1]
except KeyError:
pass
except KeyError:
pass
# consider 1_p2 vc 1.1
# after expansion will become (1_p2,0) vc (1,1)
# then 1_p2 is compared with 1 before 0 is compared with 1
# to solve the bug we need to convert it to (1,0_p2)
# by splitting _prepart part and adding it back _after_expansion
val1_prepart = val2_prepart = ''
if val1.count('_'):
val1, val1_prepart = val1.split('_', 1)
if val2.count('_'):
val2, val2_prepart = val2.split('_', 1)
# replace '-' by '.'
# FIXME: Is it needed? can val1/2 contain '-'?
val1 = val1.split("-")
if len(val1) == 2:
val1[0] = val1[0] + "." + val1[1]
val2 = val2.split("-")
if len(val2) == 2:
val2[0] = val2[0] + "." + val2[1]
val1 = val1[0].split('.')
val2 = val2[0].split('.')
# add back decimal point so that .03 does not become "3" !
for x in xrange(1, len(val1)):
if val1[x][0] == '0' :
val1[x] = '.' + val1[x]
for x in xrange(1, len(val2)):
if val2[x][0] == '0' :
val2[x] = '.' + val2[x]
# extend varion numbers
if len(val2) < len(val1):
val2.extend(["0"]*(len(val1)-len(val2)))
elif len(val1) < len(val2):
val1.extend(["0"]*(len(val2)-len(val1)))
# add back _prepart tails
if val1_prepart:
val1[-1] += '_' + val1_prepart
if val2_prepart:
val2[-1] += '_' + val2_prepart
# The above code will extend version numbers out so they
# have the same number of digits.
for x in xrange(0, len(val1)):
cmp1 = relparse(val1[x])
cmp2 = relparse(val2[x])
for y in xrange(0, 3):
myret = cmp1[y] - cmp2[y]
if myret != 0:
__vercmp_cache__[valkey] = myret
return myret
__vercmp_cache__[valkey] = 0
return 0
def explode_deps(s):
"""
@@ -138,7 +257,7 @@ def explode_deps(s):
#r[-1] += ' ' + ' '.join(j)
return r
def explode_dep_versions2(s):
def explode_dep_versions(s):
"""
Take an RDEPENDS style string of format:
"DEPEND1 (optional version) DEPEND2 (optional version) ..."
@@ -147,70 +266,24 @@ def explode_dep_versions2(s):
r = {}
l = s.replace(",", "").split()
lastdep = None
lastcmp = ""
lastver = ""
incmp = False
inversion = False
for i in l:
if i[0] == '(':
incmp = True
i = i[1:].strip()
if not i:
continue
if incmp:
incmp = False
inversion = True
# This list is based on behavior and supported comparisons from deb, opkg and rpm.
#
# Even though =<, <<, ==, !=, =>, and >> may not be supported,
# we list each possibly valid item.
# The build system is responsible for validation of what it supports.
if i.startswith(('<=', '=<', '<<', '==', '!=', '>=', '=>', '>>')):
lastcmp = i[0:2]
i = i[2:]
elif i.startswith(('<', '>', '=')):
lastcmp = i[0:1]
i = i[1:]
else:
# This is an unsupported case!
lastcmp = (i or "")
i = ""
i.strip()
if not i:
continue
lastver = i[1:] or ""
#j = []
elif inversion and i.endswith(')'):
inversion = False
lastver = lastver + " " + (i[:-1] or "")
r[lastdep] = lastver
elif not inversion:
r[i] = None
lastdep = i
lastver = ""
elif inversion:
lastver = lastver + " " + i
if inversion:
if i.endswith(')'):
i = i[:-1] or ""
inversion = False
if lastver and i:
lastver += " "
if i:
lastver += i
if lastdep not in r:
r[lastdep] = []
r[lastdep].append(lastcmp + " " + lastver)
continue
#if not inversion:
lastdep = i
lastver = ""
lastcmp = ""
if not (i in r and r[i]):
r[lastdep] = []
return r
def explode_dep_versions(s):
r = explode_dep_versions2(s)
for d in r:
if not r[d]:
r[d] = None
continue
if len(r[d]) > 1:
bb.warn("explode_dep_versions(): Item %s appeared in dependency string '%s' multiple times with different values. explode_dep_versions cannot cope with this." % (d, s))
r[d] = r[d][0]
return r
def join_deps(deps, commasep=True):
@@ -220,11 +293,7 @@ def join_deps(deps, commasep=True):
result = []
for dep in deps:
if deps[dep]:
if isinstance(deps[dep], list):
for v in deps[dep]:
result.append(dep + " (" + v + ")")
else:
result.append(dep + " (" + deps[dep] + ")")
result.append(dep + " (" + deps[dep] + ")")
else:
result.append(dep)
if commasep:
@@ -266,23 +335,20 @@ def better_compile(text, file, realfile, mode = "exec"):
for line in body:
logger.error(line)
e = bb.BBHandledException(e)
raise e
raise
def better_exec(code, context, text = None, realfile = "<code>"):
def better_exec(code, context, text, realfile = "<code>"):
"""
Similiar to better_compile, better_exec will
print the lines that are responsible for the
error.
"""
import bb.parse
if not text:
text = code
if not hasattr(code, "co_filename"):
code = better_compile(code, realfile, realfile)
try:
exec(code, _context, context)
except Exception as e:
except Exception:
(t, value, tb) = sys.exc_info()
if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
@@ -307,32 +373,22 @@ def better_exec(code, context, text = None, realfile = "<code>"):
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])
logger.error("(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):
while nexttb is not None:
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
logger.error("(file: '%s', lineno: %s, function: %s)", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
else:
break
nexttb = tb.tb_next
level = level + 1
e = bb.BBHandledException(e)
raise e
raise
def simple_exec(code, context):
exec(code, _context, context)
@@ -486,6 +542,8 @@ def preserved_envvars():
'BB_PRESERVE_ENV',
'BB_ENV_WHITELIST',
'BB_ENV_EXTRAWHITE',
'LANG',
'_',
]
return v + preserved_envvars_exported() + preserved_envvars_exported_interactive()
@@ -783,8 +841,6 @@ def which(path, item, direction = 0):
for p in paths:
next = os.path.join(p, item)
if os.path.exists(next):
if not os.path.isabs(next):
next = os.path.abspath(next)
return next
return ""
@@ -816,7 +872,3 @@ def contains(variable, checkvalues, truevalue, falsevalue, d):
def cpu_count():
return multiprocessing.cpu_count()
def nonblockingfd(fd):
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)

View File

@@ -1,82 +1,71 @@
# This is a single Makefile to handle all generated Yocto Project documents.
# The Makefile needs to live in the documents directory and all figures used
# in any manuals must be .PNG files and live in the individual book's figures
# directory as well as in the figures directory for the mega-manual.
# Note that the figures for the Yocto Project Development Manual
# differ depending on the BRANCH being built.
# directory. Note that the figures for the Yocto Project Development Manual
# differ between the 'master' and 'edison' branches.
#
# The Makefile has these targets:
#
# pdf: generates a PDF version of a manual. Not valid for the Quick Start
# or the mega-manual (single, large HTML file comprised of all
# Yocto Project manuals).
# html: generates an HTML version of a manual.
# tarball: creates a tarball for the doc files.
# validate: validates
# validate: validates
# publish: pushes generated files to the Yocto Project website
# clean: removes files
#
# The Makefile generates an HTML and PDF version of every document except the
# Yocto Project Quick Start and the single, HTML mega-manual, which is comprised
# of all the individual Yocto Project manuals. These two manuals are in HTML
# form only. The variable DOC indicates the folder name for a given manual. The
# variable VER represents the distro version of the Yocto Release for which the
# manuals are being generated. The variable BRANCH is used to indicate the
# branch (edison or denzil) and is used only when DOC=dev-manual or
# DOC=mega-manual. If you do not specify a BRANCH, the default branch used
# will be for the latest Yocto Project release. If you build for either
# edison or denzil, you must use BRANCH. You do not need to use BRANCH for
# any release beyond denzil.
# Yocto Project Quick Start. The Quick Start is in HTML form only. The variable
# DOC is used to indicate the folder name for a given manual. The variable
# VER represents the distro version of the Yocto Release for which the manuals
# are being generated. The variable BRANCH is used to indicate the 'edison'
# branch and is used only when DOC=dev-manual (making the YP Development
# Manual).
#
# To build a manual, you must invoke Makefile with the DOC argument. If you
# are going to publish the manual, then you must invoke Makefile with both the
# DOC and the VER argument. Furthermore, if you are building or publishing
# the edison or denzil versions of the Yocto Poject Development Manual or
# the mega-manual, you must also use the BRANCH argument.
# To build the HTML and PDF versions of the manual you must invoke the Makefile
# with the DOC argument. If you are going to publish the manual then you
# you must invoke the Makefile with both the DOC and the VER argument.
# If you are building the 'edison' version of the YP DEvelopment Manual then
# you must use the DOC and BRANCH arguments.
#
# Examples:
#
# make DOC=bsp-guide
# make DOC=yocto-project-qs
# make pdf DOC=ref-manual
# make pdf DOC=poky-ref-manual
# make DOC=dev-manual BRANCH=edison
# make DOC=mega-manual BRANCH=denzil
#
# The first example generates the HTML and PDF versions of the BSP Guide.
# The second example generates the HTML version only of the Quick Start. Note that
# the Quick Start only has an HTML version available. The third example generates
# both the PDF and HTML versions of the Yocto Project Reference Manual. The
# fourth example generates both the PDF and HTML 'edison' versions of the YP
# Development Manual. The last exmample generates the HTML version of the
# mega-manual and uses the 'denzil' branch when choosing figures for the
# tarball of figures. Any example that does not use the BRANCH argument
# builds the current version of the manual set.
# The second example generates the HTML version only of the Quick Start. Note that
# the Quick Start only has an HTML version available. The third example generates
# both the PDF and HTML versions of the Yocto Project Reference Manual. The
# last example generates both the PDF and HTML 'edison' versions of the YP
# Development Manual.
#
# Use the publish target to push the generated manuals to the Yocto Project
# website. All files needed for the manual's HTML form are pushed as well as the
# PDF version (if applicable).
# Use the publish target to push the generated manuals to the Yocto Project
# website. All files needed for the manual's HTML form are pushed as well as the
# PDF version (if applicable).
# Examples:
#
# make publish DOC=bsp-guide VER=1.3
# make publish DOC=adt-manual VER=1.3
# make publish DOC=bsp-guide VER=1.2
# make publish DOC=adt-manual VER=1.2
# make publish DOC=dev-manual VER=1.1.1 BRANCH=edison
# make publish DOC=dev-manual VER=1.2 BRANCH=denzil
# make publish DOC=dev-manual VER=1.2
#
# The first example publishes the 1.3 version of both the PDF and HTML versions of
# the BSP Guide. The second example publishes the 1.3 version of both the PDF and
# The first example publishes the 1.2 version of both the PDF and HTML versions of
# the BSP Guide. The second example publishes the 1.2 version of both the PDF and
# HTML versions of the ADT Manual. The third example publishes the PDF and HTML
# 'edison' versions of the YP Development Manual. The fourth example publishes
# the PDF and HTML 'denzil' versions of the YP Development Manual.
# 'edison' versions of the YP Development Manual. Finally, the last example publishes
# the PDF and HTML 'master' versions of the YP Development Manual.
#
ifeq ($(DOC),bsp-guide)
XSLTOPTS = --stringparam html.stylesheet bsp-style.css \
XSLTOPTS = --stringparam html.stylesheet 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
TARFILES = style.css bsp-guide.html bsp-guide.pdf figures/bsp-title.png
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
FIGURES = figures
STYLESHEET = $(DOC)/*.css
@@ -84,7 +73,7 @@ STYLESHEET = $(DOC)/*.css
endif
ifeq ($(DOC),dev-manual)
XSLTOPTS = --stringparam html.stylesheet dev-style.css \
XSLTOPTS = --stringparam html.stylesheet style.css \
--stringparam chapter.autolabel 1 \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
@@ -92,13 +81,12 @@ XSLTOPTS = --stringparam html.stylesheet dev-style.css \
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
# list of files is all-inclusive for all branches. Note, if you don't provide a BRANCH
# option, it defaults to the latest stuff. This would be appropriate for "master" branch.
# message for .PNG files that are not present when building a particular branch. The
# list of files is all-inclusive for all branches.
#
ifeq ($(BRANCH),edison)
TARFILES = dev-style.css dev-manual.html dev-manual.pdf \
TARFILES = 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-example-repos-edison.png \
@@ -106,21 +94,15 @@ TARFILES = dev-style.css dev-manual.html dev-manual.pdf \
figures/kernel-overview-3-edison.png \
figures/source-repos.png figures/yp-download.png \
figures/wip.png
else ifeq ($(BRANCH),denzil)
TARFILES = dev-style.css dev-manual.html dev-manual.pdf \
else
TARFILES = 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-example-repos-denzil.png \
figures/kernel-example-repos.png \
figures/kernel-overview-1.png figures/kernel-overview-2.png \
figures/kernel-overview-3-denzil.png \
figures/kernel-overview-3.png \
figures/source-repos.png figures/yp-download.png \
figures/wip.png
else
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
endif
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
@@ -130,77 +112,24 @@ STYLESHEET = $(DOC)/*.css
endif
ifeq ($(DOC),yocto-project-qs)
XSLTOPTS = --stringparam html.stylesheet qs-style.css \
XSLTOPTS = --stringparam html.stylesheet 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
TARFILES = yocto-project-qs.html 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
ifeq ($(DOC),mega-manual)
XSLTOPTS = --stringparam html.stylesheet mega-style.css \
--stringparam chapter.autolabel 1 \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--xinclude
ALLPREQ = html tarball
ifeq ($(BRANCH),edison)
TARFILES = mega-manual.html mega-style.css figures/yocto-environment.png figures/building-an-image.png \
figures/using-a-pre-built-image.png \
figures/poky-title.png \
figures/adt-title.png figures/bsp-title.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-example-repos-edison.png \
figures/kernel-overview-1.png figures/kernel-overview-2.png \
figures/kernel-overview-3-edison.png \
figures/source-repos.png figures/yp-download.png \
figures/wip.png
else ifeq ($(BRANCH),denzil)
TARFILES = mega-manual.html mega-style.css figures/yocto-environment.png figures/building-an-image.png \
figures/using-a-pre-built-image.png \
figures/poky-title.png \
figures/adt-title.png figures/bsp-title.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-example-repos-denzil.png \
figures/kernel-overview-1.png figures/kernel-overview-2.png \
figures/kernel-overview-3-denzil.png \
figures/source-repos.png figures/yp-download.png \
figures/wip.png
else
TARFILES = mega-manual.html mega-style.css figures/yocto-environment.png figures/building-an-image.png \
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-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/kernel-dev-title.png
endif
MANUALS = $(DOC)/$(DOC).html
FIGURES = figures
STYLESHEET = $(DOC)/*.css
endif
ifeq ($(DOC),ref-manual)
XSLTOPTS = --stringparam html.stylesheet ref-style.css \
ifeq ($(DOC),poky-ref-manual)
XSLTOPTS = --stringparam html.stylesheet style.css \
--stringparam chapter.autolabel 1 \
--stringparam appendix.autolabel A \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--xinclude
ALLPREQ = html pdf tarball
TARFILES = ref-manual.html ref-style.css figures/poky-title.png \
figures/buildhistory.png figures/buildhistory-web.png
TARFILES = poky-ref-manual.html style.css figures/poky-title.png
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
FIGURES = figures
STYLESHEET = $(DOC)/*.css
@@ -208,42 +137,28 @@ endif
ifeq ($(DOC),adt-manual)
XSLTOPTS = --stringparam html.stylesheet adt-style.css \
XSLTOPTS = --stringparam html.stylesheet style.css \
--stringparam chapter.autolabel 1 \
--stringparam appendix.autolabel A \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--xinclude
ALLPREQ = html pdf tarball
TARFILES = adt-manual.html adt-manual.pdf adt-style.css figures/adt-title.png
TARFILES = adt-manual.html adt-manual.pdf style.css figures/adt-title.png
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
FIGURES = figures
STYLESHEET = $(DOC)/*.css
endif
ifeq ($(DOC),kernel-manual)
XSLTOPTS = --stringparam html.stylesheet kernel-style.css \
XSLTOPTS = --stringparam html.stylesheet style.css \
--stringparam chapter.autolabel 1 \
--stringparam appendix.autolabel A \
--stringparam section.autolabel 1 \
--stringparam section.label.includes.component.label 1 \
--xinclude
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 = --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
TARFILES = kernel-manual.html kernel-manual.pdf style.css figures/kernel-title.png figures/kernel-architecture-overview.png
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf
FIGURES = figures
STYLESHEET = $(DOC)/*.css
@@ -261,47 +176,17 @@ all: $(ALLPREQ)
pdf:
ifeq ($(DOC),yocto-project-qs)
@echo " "
@echo "ERROR: You cannot generate a yocto-project-qs PDF file."
@echo "ERROR: You cannot generate a PDF file for the Yocto Project Quick Start"
@echo " "
else ifeq ($(DOC),mega-manual)
@echo " "
@echo "ERROR: You cannot generate a mega-manual PDF file."
@echo " "
else
cd $(DOC); ../tools/poky-docbook-to-pdf $(DOC).xml ../template; cd ..
endif
html:
ifeq ($(DOC),mega-manual)
# See http://www.sagehill.net/docbookxsl/HtmlOutput.html
@echo " "
@echo "******** Building "$(DOC)
@echo " "
# See http://www.sagehill.net/docbookxsl/HtmlOutput.html
cd $(DOC); xsltproc $(XSLTOPTS) -o $(DOC).html $(DOC)-customization.xsl $(DOC).xml; cd ..
@echo " "
@echo "******** Using mega-manual.sed to process external links"
@echo " "
cd $(DOC); sed -f ../tools/mega-manual.sed < mega-manual.html > mega-output.html; cd ..
@echo " "
@echo "******** Cleaning up transient file mega-output.html"
@echo " "
cd $(DOC); rm mega-manual.html; mv mega-output.html mega-manual.html; cd ..
else
# See http://www.sagehill.net/docbookxsl/HtmlOutput.html
@echo " "
@echo "******** Building "$(DOC)
@echo " "
cd $(DOC); xsltproc $(XSLTOPTS) -o $(DOC).html $(DOC)-customization.xsl $(DOC).xml; cd ..
endif
tarball: html
@echo " "
@echo "******** Creating Tarball of document files"
@echo " "
cd $(DOC); tar -cvzf $(DOC).tgz $(TARFILES); cd ..
validate:
@@ -309,18 +194,8 @@ validate:
publish:
@if test -f $(DOC)/$(DOC).html; \
then \
echo " "; \
echo "******** Publishing "$(DOC)".html"; \
echo " "; \
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."; \
echo " "; \
fi
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)/figures
clean:
rm -rf $(MANUALS); rm $(DOC)/$(DOC).tgz;
rm -f $(MANUALS)

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