mirror of
https://git.yoctoproject.org/poky
synced 2026-09-19 00:49:33 +02:00
Compare commits
362 Commits
yocto-2.2.
...
jethro-14.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adb34b8ddc | ||
|
|
a20868079c | ||
|
|
1ff7aee3da | ||
|
|
2e4a7df41c | ||
|
|
a778a2b6db | ||
|
|
7b85e8c29c | ||
|
|
ba4fbd376d | ||
|
|
70799fb931 | ||
|
|
6976f01adc | ||
|
|
867babeb6f | ||
|
|
96c1644d0d | ||
|
|
9e1ca0ba84 | ||
|
|
a37112a3bc | ||
|
|
d11c5d8944 | ||
|
|
1f8eb08791 | ||
|
|
b9c389404f | ||
|
|
820b835e3c | ||
|
|
6ffa151404 | ||
|
|
c1ba8e1174 | ||
|
|
c3f5e64b58 | ||
|
|
84997c7f21 | ||
|
|
e26f842287 | ||
|
|
2b8ab746ba | ||
|
|
b4362e0955 | ||
|
|
0ad02a1f1a | ||
|
|
6ec3aa9972 | ||
|
|
ac81181091 | ||
|
|
90dc28b0b6 | ||
|
|
8df8e70f96 | ||
|
|
d23b450ea3 | ||
|
|
91353b6936 | ||
|
|
942832888b | ||
|
|
dc61ec5f0c | ||
|
|
766c5ced75 | ||
|
|
2ff9d30dac | ||
|
|
2804850ea7 | ||
|
|
6998a3c1e6 | ||
|
|
6057d0aa47 | ||
|
|
48048dcaa2 | ||
|
|
931a6e6d5e | ||
|
|
98e7d8a9a0 | ||
|
|
ffa3a07ac1 | ||
|
|
661aff850e | ||
|
|
8f62c3dc44 | ||
|
|
2622059ca0 | ||
|
|
ddb1db9ef7 | ||
|
|
fc1ba0b67f | ||
|
|
9657825ef3 | ||
|
|
9f1dc20619 | ||
|
|
c95d42a7d1 | ||
|
|
7d403a2ecd | ||
|
|
75e6b3b57b | ||
|
|
cb5dd8d314 | ||
|
|
1fedf13e63 | ||
|
|
e1b940b4d1 | ||
|
|
b2e2a7426c | ||
|
|
524417d587 | ||
|
|
0a9e04cade | ||
|
|
37eb21b2b1 | ||
|
|
72ea3c272c | ||
|
|
0e0c04343d | ||
|
|
4037644690 | ||
|
|
64b9c83b0c | ||
|
|
96456b15ea | ||
|
|
d8b15a0384 | ||
|
|
9149baa38d | ||
|
|
a01d3234f6 | ||
|
|
3b2c540986 | ||
|
|
ad7cab35ff | ||
|
|
4e260c96f4 | ||
|
|
1ecd2f56aa | ||
|
|
1081306623 | ||
|
|
f96cfb009d | ||
|
|
94d9c374e9 | ||
|
|
0e8aae7bc8 | ||
|
|
3e93d609c0 | ||
|
|
970a077b83 | ||
|
|
4cdca0571a | ||
|
|
17480a956d | ||
|
|
b3c799c831 | ||
|
|
f01272c3a5 | ||
|
|
f2688ed200 | ||
|
|
c9e0efd1f7 | ||
|
|
2596de9179 | ||
|
|
118380bc5d | ||
|
|
7fde327c85 | ||
|
|
3863499572 | ||
|
|
c7947af728 | ||
|
|
a79b7d685b | ||
|
|
4f2dfdcd39 | ||
|
|
ddbc13155f | ||
|
|
32728d0946 | ||
|
|
dade0e68c6 | ||
|
|
a325db9bc8 | ||
|
|
c940dd928f | ||
|
|
65306b0bfc | ||
|
|
f117786f24 | ||
|
|
6f8a7089b3 | ||
|
|
1945133a22 | ||
|
|
e3bf77e381 | ||
|
|
44585dd62a | ||
|
|
a4ee99f27a | ||
|
|
16d64def97 | ||
|
|
a31931e290 | ||
|
|
da75750122 | ||
|
|
ae691815c8 | ||
|
|
ba15486e27 | ||
|
|
2ef5feeb3d | ||
|
|
a201edefae | ||
|
|
00b016b010 | ||
|
|
877a6b3ef4 | ||
|
|
ed3fc1ab85 | ||
|
|
dafc9d7755 | ||
|
|
49ce0e7d4a | ||
|
|
6b2102cd59 | ||
|
|
504e742a5e | ||
|
|
6b9d2edd7d | ||
|
|
ed3115be57 | ||
|
|
c6864efbc0 | ||
|
|
328bd419be | ||
|
|
6dba9abd43 | ||
|
|
be42a1d4fd | ||
|
|
6d06b104ce | ||
|
|
5f5e9c4629 | ||
|
|
bb5e264604 | ||
|
|
2d452b19d6 | ||
|
|
f87869c6d6 | ||
|
|
a820a2073b | ||
|
|
bdd03ee432 | ||
|
|
87f7f062df | ||
|
|
16cb70663f | ||
|
|
3f54d40e23 | ||
|
|
909cf62394 | ||
|
|
e86f3240e6 | ||
|
|
2aeac77235 | ||
|
|
9e5370d2e6 | ||
|
|
07682c1bfb | ||
|
|
09e3b84ea5 | ||
|
|
4fe84e836d | ||
|
|
a9e1361611 | ||
|
|
3b223f75ee | ||
|
|
42ce9b8751 | ||
|
|
45a2977b83 | ||
|
|
c8e5c38b8a | ||
|
|
4823395a7d | ||
|
|
4a4fde53bd | ||
|
|
f8dd7e105a | ||
|
|
269c2bd717 | ||
|
|
1a51bb69b7 | ||
|
|
c484129d7b | ||
|
|
5f27b611cd | ||
|
|
6e32be7c7b | ||
|
|
d6fed74776 | ||
|
|
6e8edf0e0f | ||
|
|
87ba508688 | ||
|
|
9991263ffe | ||
|
|
b15baaee6f | ||
|
|
1a52eceaa5 | ||
|
|
6d601592e1 | ||
|
|
75c088f2e2 | ||
|
|
41265570c6 | ||
|
|
5ca77b2fc6 | ||
|
|
a4398c7ff7 | ||
|
|
05c31507da | ||
|
|
6945a4fdde | ||
|
|
b1f23d1254 | ||
|
|
7fe17a2942 | ||
|
|
50700a7da6 | ||
|
|
1f0e615bec | ||
|
|
dc5f155e15 | ||
|
|
ef135112fd | ||
|
|
ae57ea03c6 | ||
|
|
eb9666a3e2 | ||
|
|
5b12268f6e | ||
|
|
a3a374a639 | ||
|
|
f4341a9b6f | ||
|
|
35f4306ed4 | ||
|
|
3e8a07b901 | ||
|
|
5ffc3267e7 | ||
|
|
5cc5f99bba | ||
|
|
21a816c73a | ||
|
|
6a0fbfaeb5 | ||
|
|
d57aaf7a39 | ||
|
|
941874ae29 | ||
|
|
d74a3cb765 | ||
|
|
12fae23964 | ||
|
|
67ac9d6254 | ||
|
|
5812fc9e20 | ||
|
|
3de249206e | ||
|
|
79de8cf5fa | ||
|
|
a23d2625e2 | ||
|
|
b6def81ff5 | ||
|
|
db0f8ac8b3 | ||
|
|
c8122a088f | ||
|
|
cdeb2415dd | ||
|
|
52cd219877 | ||
|
|
a88d6cb170 | ||
|
|
4e74b36458 | ||
|
|
66807731c7 | ||
|
|
4c075e7114 | ||
|
|
ee52ac6e85 | ||
|
|
e9f95df962 | ||
|
|
e63bab1a09 | ||
|
|
64a492097f | ||
|
|
5b043dafa3 | ||
|
|
655c8a5c9d | ||
|
|
96fda8c8f6 | ||
|
|
ae037d974e | ||
|
|
26eb877e18 | ||
|
|
e632cdb031 | ||
|
|
e8c96131d9 | ||
|
|
108ea6d05f | ||
|
|
9a88c1d255 | ||
|
|
443b09a61d | ||
|
|
6c910685ec | ||
|
|
9fd2349842 | ||
|
|
5a40d9fb69 | ||
|
|
1bbf18385b | ||
|
|
2ec6d1dcbc | ||
|
|
55aafb547d | ||
|
|
029948bc8e | ||
|
|
2bcf141c77 | ||
|
|
cc3a391bd9 | ||
|
|
049be17b53 | ||
|
|
5e94ac7ba9 | ||
|
|
7ee1828d30 | ||
|
|
ca6ec2e392 | ||
|
|
b55a677699 | ||
|
|
4922f470dd | ||
|
|
3ec0e95fed | ||
|
|
942ce53beb | ||
|
|
ce8ae1c164 | ||
|
|
080e027d14 | ||
|
|
c6ae9c1fae | ||
|
|
049b7db30c | ||
|
|
d632a923dc | ||
|
|
5b993ed429 | ||
|
|
27ee5b4f0e | ||
|
|
a4134af78b | ||
|
|
e10ec6f3be | ||
|
|
a5a965d409 | ||
|
|
2fb7ee2628 | ||
|
|
b9ad87b18f | ||
|
|
0a1c63ad6b | ||
|
|
d4b400e1c7 | ||
|
|
8c8c4ede3f | ||
|
|
b83220257a | ||
|
|
dd0ba9ea4a | ||
|
|
325d205769 | ||
|
|
53b114b55f | ||
|
|
02be35d1ad | ||
|
|
f5551f85aa | ||
|
|
aa179aeede | ||
|
|
20007c87b2 | ||
|
|
2dd7f469f5 | ||
|
|
81cc737056 | ||
|
|
2b1dce5a3c | ||
|
|
a6f52930a6 | ||
|
|
e79a538a54 | ||
|
|
b171076f46 | ||
|
|
255115f6e4 | ||
|
|
3f691055c5 | ||
|
|
5ba94af1e6 | ||
|
|
d03d145410 | ||
|
|
00ff950d3c | ||
|
|
22446c6f44 | ||
|
|
8597a616f3 | ||
|
|
95cc641ec3 | ||
|
|
84d48acb01 | ||
|
|
4369329b76 | ||
|
|
4c3191f9ab | ||
|
|
caca77eb17 | ||
|
|
383159ef64 | ||
|
|
be40baa5a0 | ||
|
|
a897bfdbdc | ||
|
|
9c4b61e919 | ||
|
|
4c07dd2172 | ||
|
|
83b72d8d1f | ||
|
|
44639bd817 | ||
|
|
a0be9bd862 | ||
|
|
53f0290658 | ||
|
|
f4b0c49145 | ||
|
|
fb409c9d17 | ||
|
|
55d097a106 | ||
|
|
8e6b2d6823 | ||
|
|
332eb1dcce | ||
|
|
cbc4e832d1 | ||
|
|
c4b71e1a6a | ||
|
|
fdea03df12 | ||
|
|
6fc1109f5d | ||
|
|
9eb4ce0a81 | ||
|
|
6880f826c3 | ||
|
|
7dcaa840ff | ||
|
|
435139b2a9 | ||
|
|
5f49c0a248 | ||
|
|
a29ec8108e | ||
|
|
b1e980f33b | ||
|
|
ed8269010c | ||
|
|
9a620dada4 | ||
|
|
f8280717e4 | ||
|
|
49858bdc02 | ||
|
|
d2134528a6 | ||
|
|
fec97f6fa2 | ||
|
|
3b4f65968e | ||
|
|
61a7de097a | ||
|
|
82c0072033 | ||
|
|
f181e72cb8 | ||
|
|
4b5d4ca1c9 | ||
|
|
d2ea8f1041 | ||
|
|
02ef437608 | ||
|
|
3653b17aea | ||
|
|
8879571d11 | ||
|
|
eeda3c66a2 | ||
|
|
dee9fbe044 | ||
|
|
d001d46d17 | ||
|
|
777451ca43 | ||
|
|
cb0ca7264d | ||
|
|
8970ad60f5 | ||
|
|
4f7fdd0a59 | ||
|
|
3b7df55075 | ||
|
|
f3d7c3f385 | ||
|
|
96b1b5c127 | ||
|
|
7aecb577e0 | ||
|
|
75ca2c8682 | ||
|
|
02c7b3f271 | ||
|
|
003c94f7d9 | ||
|
|
ad6db0121f | ||
|
|
f0d87fea69 | ||
|
|
35f34a61b3 | ||
|
|
3b77e205c0 | ||
|
|
e2d441275d | ||
|
|
ef69078072 | ||
|
|
4700e404f3 | ||
|
|
743ee049b8 | ||
|
|
8a3deca4a4 | ||
|
|
0073b234d7 | ||
|
|
71dbbcd0c8 | ||
|
|
41f1026849 | ||
|
|
38e3c6e6dd | ||
|
|
b99ec284c4 | ||
|
|
c670dc77fe | ||
|
|
b968190e84 | ||
|
|
1278753c37 | ||
|
|
7b25b70884 | ||
|
|
d9423fbd54 | ||
|
|
347347ad78 | ||
|
|
5935783f21 | ||
|
|
7fdad70111 | ||
|
|
0cb2fa5f73 | ||
|
|
5b52e9b086 | ||
|
|
59bdde4327 | ||
|
|
8ad2bcca49 | ||
|
|
c3087bd977 | ||
|
|
d2bf9fb2ca | ||
|
|
0556c58bff | ||
|
|
e683dac7ab | ||
|
|
092757ec5b | ||
|
|
dab55553b2 | ||
|
|
1753d4a5da | ||
|
|
9dd3422bc6 | ||
|
|
1401976a02 | ||
|
|
a54a0dba10 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,7 +1,7 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
/*.patch
|
/*.patch
|
||||||
/build*/
|
build*/
|
||||||
pyshtables.py
|
pyshtables.py
|
||||||
pstage/
|
pstage/
|
||||||
scripts/oe-git-proxy-socks
|
scripts/oe-git-proxy-socks
|
||||||
@@ -14,7 +14,6 @@ hob-image-*.bb
|
|||||||
*.orig
|
*.orig
|
||||||
*.rej
|
*.rej
|
||||||
*~
|
*~
|
||||||
!meta-poky
|
|
||||||
!meta-yocto
|
!meta-yocto
|
||||||
!meta-yocto-bsp
|
!meta-yocto-bsp
|
||||||
!meta-yocto-imported
|
!meta-yocto-imported
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
# Template settings
|
# Template settings
|
||||||
TEMPLATECONF=${TEMPLATECONF:-meta-poky/conf}
|
TEMPLATECONF=${TEMPLATECONF:-meta-yocto/conf}
|
||||||
|
|||||||
2
README
2
README
@@ -42,7 +42,7 @@ documentation:
|
|||||||
Git repository: http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/
|
Git repository: http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/
|
||||||
Mailing list: yocto@yoctoproject.org
|
Mailing list: yocto@yoctoproject.org
|
||||||
|
|
||||||
meta-poky, meta-yocto-bsp:
|
meta-yocto(-bsp):
|
||||||
Git repository: http://git.yoctoproject.org/cgit/cgit.cgi/meta-yocto(-bsp)
|
Git repository: http://git.yoctoproject.org/cgit/cgit.cgi/meta-yocto(-bsp)
|
||||||
Mailing list: poky@yoctoproject.org
|
Mailing list: poky@yoctoproject.org
|
||||||
|
|
||||||
|
|||||||
198
README.hardware
198
README.hardware
@@ -77,22 +77,31 @@ variable value corresponding to the device is given in brackets.
|
|||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
|
||||||
Intel x86 based PCs and devices (genericx86*)
|
Intel x86 based PCs and devices (genericx86)
|
||||||
=============================================
|
==========================================
|
||||||
|
|
||||||
The genericx86 and genericx86-64 MACHINE are tested on the following platforms:
|
The genericx86 MACHINE is tested on the following platforms:
|
||||||
|
|
||||||
Intel Xeon/Core i-Series:
|
Intel Xeon/Core i-Series:
|
||||||
+ Intel NUC5 Series - ix-52xx Series SOC (Broadwell)
|
+ Intel Romley Server: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Canoe Pass CRB)
|
||||||
+ Intel NUC6 Series - ix-62xx Series SOC (Skylake)
|
+ Intel Romley Server: Ivy Bridge Xeon processor, C600 PCH (Patsburg), (Intel SDP S2R3)
|
||||||
+ Intel Shumway Xeon Server
|
+ Intel Crystal Forest Server: Sandy Bridge Xeon processor, DH89xx PCH (Cave Creek), (Stargo CRB)
|
||||||
|
+ Intel Chief River Mobile: Ivy Bridge Mobile processor, QM77 PCH (Panther Point-M), (Emerald Lake II CRB, Sabino Canyon CRB)
|
||||||
|
+ Intel Huron River Mobile: Sandy Bridge processor, QM67 PCH (Cougar Point), (Emerald Lake CRB, EVOC EC7-1817LNAR board)
|
||||||
|
+ Intel Calpella Platform: Core i7 processor, QM57 PCH (Ibex Peak-M), (Red Fort CRB, Emerson MATXM CORE-411-B)
|
||||||
|
+ Intel Nehalem/Westmere-EP Server: Xeon 56xx/55xx processors, 5520 chipset, ICH10R IOH (82801), (Hanlan Creek CRB)
|
||||||
|
+ Intel Nehalem Workstation: Xeon 56xx/55xx processors, System SC5650SCWS (Greencity CRB)
|
||||||
|
+ Intel Picket Post Server: Xeon 56xx/55xx processors (Jasper Forest), 3420 chipset (Ibex Peak), (Osage CRB)
|
||||||
|
+ Intel Storage Platform: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Oak Creek Canyon CRB)
|
||||||
|
+ Intel Shark Bay Client Platform: Haswell processor, LynxPoint PCH, (Walnut Canyon CRB, Lava Canyon CRB, Basking Ridge CRB, Flathead Creek CRB)
|
||||||
|
+ Intel Shark Bay Ultrabook Platform: Haswell ULT processor, Lynx Point-LP PCH, (WhiteTip Mountain 1 CRB)
|
||||||
|
|
||||||
Intel Atom platforms:
|
Intel Atom platforms:
|
||||||
+ MinnowBoard MAX - E3825 SOC (Bay Trail)
|
+ Intel embedded Menlow: Intel Atom Z510/530 CPU, System Controller Hub US15W (Portwell NANO-8044)
|
||||||
+ MinnowBoard MAX - Turbot (ADI Engineering) - E3826 SOC (Bay Trail)
|
+ Intel Luna Pier: Intel Atom N4xx/D5xx series CPU (aka: Pineview-D & -M), 82801HM I/O Hub (ICH8M), (Advantech AIMB-212, Moon Creek CRB)
|
||||||
- These boards can be either 32bot or 64bit modes depending on firmware
|
+ Intel Queens Bay platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Emerson NITX-315, Crown Bay CRB, Minnow Board)
|
||||||
- See minnowboard.org for details
|
+ Intel Fish River Island platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Kontron KM2M806)
|
||||||
+ Intel Braswell SOC
|
+ Intel Cedar Trail platform: Intel Atom N2000 & D2000 series CPU (aka: Cedarview), NM10 Express Chipset (Norco kit BIS-6630, Cedar Rock CRB)
|
||||||
|
|
||||||
and is likely to work on many unlisted Atom/Core/Xeon based devices. The MACHINE
|
and is likely to work on many unlisted Atom/Core/Xeon based devices. The MACHINE
|
||||||
type supports ethernet, wifi, sound, and Intel/vesa graphics by default in
|
type supports ethernet, wifi, sound, and Intel/vesa graphics by default in
|
||||||
@@ -127,14 +136,53 @@ USB Device:
|
|||||||
device, but the idea is to force BIOS to read the Cylinder/Head/Sector
|
device, but the idea is to force BIOS to read the Cylinder/Head/Sector
|
||||||
geometry from the device.
|
geometry from the device.
|
||||||
|
|
||||||
2. Use a ".wic" image with an EFI partition
|
2. Without such an option, the BIOS generally boots the device in USB-ZIP
|
||||||
|
mode. To write an image to a USB device that will be bootable in
|
||||||
|
USB-ZIP mode, carry out the following actions:
|
||||||
|
|
||||||
a) With a default grub-efi bootloader:
|
a. Determine the geometry of your USB device using fdisk:
|
||||||
# dd if=core-image-minimal-genericx86-64.wic of=/dev/sdb
|
|
||||||
|
|
||||||
b) Use systemd-boot instead
|
# fdisk /dev/sdb
|
||||||
- Build an image with EFI_PROVIDER="systemd-boot" then use the above
|
Command (m for help): p
|
||||||
dd command to write the image to a USB stick.
|
|
||||||
|
Disk /dev/sdb: 4011 MB, 4011491328 bytes
|
||||||
|
124 heads, 62 sectors/track, 1019 cylinders, total 7834944 sectors
|
||||||
|
...
|
||||||
|
|
||||||
|
Command (m for help): q
|
||||||
|
|
||||||
|
b. Configure the USB device for USB-ZIP mode:
|
||||||
|
|
||||||
|
# mkdiskimage -4 /dev/sdb 1019 124 62
|
||||||
|
|
||||||
|
Where 1019, 124 and 62 are the cylinder, head and sectors/track counts
|
||||||
|
as reported by fdisk (substitute the values reported for your device).
|
||||||
|
When the operation has finished and the access LED (if any) on the
|
||||||
|
device stops flashing, remove and reinsert the device to allow the
|
||||||
|
kernel to detect the new partition layout.
|
||||||
|
|
||||||
|
c. Copy the contents of the image to the USB-ZIP mode device:
|
||||||
|
|
||||||
|
# mkdir /tmp/image
|
||||||
|
# mkdir /tmp/usbkey
|
||||||
|
# mount -o loop core-image-minimal-genericx86.hddimg /tmp/image
|
||||||
|
# mount /dev/sdb4 /tmp/usbkey
|
||||||
|
# cp -rf /tmp/image/* /tmp/usbkey
|
||||||
|
|
||||||
|
d. Install the syslinux boot loader:
|
||||||
|
|
||||||
|
# syslinux /dev/sdb4
|
||||||
|
|
||||||
|
e. Unmount everything:
|
||||||
|
|
||||||
|
# umount /tmp/image
|
||||||
|
# umount /tmp/usbkey
|
||||||
|
|
||||||
|
Install the boot device in the target board and configure the BIOS to boot
|
||||||
|
from it.
|
||||||
|
|
||||||
|
For more details on the USB-ZIP scenario, see the syslinux documentation:
|
||||||
|
http://git.kernel.org/?p=boot/syslinux/syslinux.git;a=blob_plain;f=doc/usbkey.txt;hb=HEAD
|
||||||
|
|
||||||
|
|
||||||
Texas Instruments Beaglebone (beaglebone)
|
Texas Instruments Beaglebone (beaglebone)
|
||||||
@@ -160,17 +208,59 @@ this, issue the following commands from the u-boot prompt:
|
|||||||
To further tailor these instructions for your board, please refer to the
|
To further tailor these instructions for your board, please refer to the
|
||||||
documentation at http://www.beagleboard.org/bone and http://www.beagleboard.org/black
|
documentation at http://www.beagleboard.org/bone and http://www.beagleboard.org/black
|
||||||
|
|
||||||
From a Linux system with access to the image files perform the following steps:
|
From a Linux system with access to the image files perform the following steps
|
||||||
|
as root, replacing mmcblk0* with the SD card device on your machine (such as sdc
|
||||||
|
if used via a usb card reader):
|
||||||
|
|
||||||
1. Build an image. For example:
|
1. Partition and format an SD card:
|
||||||
|
# fdisk -lu /dev/mmcblk0
|
||||||
|
|
||||||
$ bitbake core-image-minimal
|
Disk /dev/mmcblk0: 3951 MB, 3951034368 bytes
|
||||||
|
255 heads, 63 sectors/track, 480 cylinders, total 7716864 sectors
|
||||||
|
Units = sectors of 1 * 512 = 512 bytes
|
||||||
|
|
||||||
2. Use the "dd" utility to write the image to the SD card. For example:
|
Device Boot Start End Blocks Id System
|
||||||
|
/dev/mmcblk0p1 * 63 144584 72261 c Win95 FAT32 (LBA)
|
||||||
|
/dev/mmcblk0p2 144585 465884 160650 83 Linux
|
||||||
|
|
||||||
# dd core-image-minimal-beaglebone.wic of=/dev/sdb
|
# mkfs.vfat -F 16 -n "boot" /dev/mmcblk0p1
|
||||||
|
# mke2fs -j -L "root" /dev/mmcblk0p2
|
||||||
|
|
||||||
|
The following assumes the SD card partitions 1 and 2 are mounted at
|
||||||
|
/media/boot and /media/root respectively. Removing the card and reinserting
|
||||||
|
it will do just that on most modern Linux desktop environments.
|
||||||
|
|
||||||
|
The files referenced below are made available after the build in
|
||||||
|
build/tmp/deploy/images.
|
||||||
|
|
||||||
|
2. Install the boot loaders
|
||||||
|
# cp MLO-beaglebone /media/boot/MLO
|
||||||
|
# cp u-boot-beaglebone.img /media/boot/u-boot.img
|
||||||
|
|
||||||
|
3. Install the root filesystem
|
||||||
|
# tar x -C /media/root -f core-image-$IMAGE_TYPE-beaglebone.tar.bz2
|
||||||
|
|
||||||
|
4. If using core-image-base or core-image-sato images, the SD card is ready
|
||||||
|
and rootfs already contains the kernel, modules and device tree (DTB)
|
||||||
|
files necessary to be booted with U-boot's default configuration, so
|
||||||
|
skip directly to step 8.
|
||||||
|
For core-image-minimal, proceed through next steps.
|
||||||
|
|
||||||
|
5. If using core-image-minimal rootfs, install the modules
|
||||||
|
# tar x -C /media/root -f modules-beaglebone.tgz
|
||||||
|
|
||||||
|
6. If using core-image-minimal rootfs, install the kernel zImage into /boot
|
||||||
|
directory of rootfs
|
||||||
|
# cp zImage-beaglebone.bin /media/root/boot/zImage
|
||||||
|
|
||||||
|
7. If using core-image-minimal rootfs, also install device tree (DTB) files
|
||||||
|
into /boot directory of rootfs
|
||||||
|
# cp zImage-am335x-bone.dtb /media/root/boot/am335x-bone.dtb
|
||||||
|
# cp zImage-am335x-boneblack.dtb /media/root/boot/am335x-boneblack.dtb
|
||||||
|
|
||||||
|
8. Unmount the SD partitions, insert the SD card into the Beaglebone, and
|
||||||
|
boot the Beaglebone
|
||||||
|
|
||||||
3. Insert the SD card into the Beaglebone and boot the board.
|
|
||||||
|
|
||||||
Freescale MPC8315E-RDB (mpc8315e-rdb)
|
Freescale MPC8315E-RDB (mpc8315e-rdb)
|
||||||
=====================================
|
=====================================
|
||||||
@@ -301,25 +391,75 @@ Load the kernel, and boot the system as follows:
|
|||||||
=> tftp tftp $loadaddr vmlinux
|
=> tftp tftp $loadaddr vmlinux
|
||||||
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/nfs rw nfsroot=<nfsroot ip>:<rootfs path> ip=<board ip>:<server ip>:<gateway ip>:<netmask>:edgerouter:eth0:off mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)
|
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/nfs rw nfsroot=<nfsroot ip>:<rootfs path> ip=<board ip>:<server ip>:<gateway ip>:<netmask>:edgerouter:eth0:off mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)
|
||||||
|
|
||||||
--- Booting from USB disk ---
|
--- Booting from USB root ---
|
||||||
|
|
||||||
To boot from the USB disk, you either need to remove it from the edgerouter
|
To boot from the USB disk, you either need to remove it from the edgerouter
|
||||||
box and populate it from another computer, or use a previously booted NFS
|
box and populate it from another computer, or use a previously booted NFS
|
||||||
image and populate from the edgerouter itself.
|
image and populate from the edgerouter itself.
|
||||||
|
|
||||||
Type 1: Use partitioned image
|
Type 1: Mounted USB disk
|
||||||
-----------------------------
|
------------------------
|
||||||
|
|
||||||
|
To boot from the USB disk there are two available partitions on the factory
|
||||||
|
USB storage. The rest of this guide assumes that these partitions are left
|
||||||
|
intact. If you change the partition scheme, you must update your boot method
|
||||||
|
appropriately.
|
||||||
|
|
||||||
|
The standard partitions are:
|
||||||
|
|
||||||
|
- 1: vfat partition containing factory kernels
|
||||||
|
- 2: ext3 partition for the root filesystem.
|
||||||
|
|
||||||
|
You can place the kernel on either partition 1, or partition 2, but the roofs
|
||||||
|
must go on partition 2 (due to its size).
|
||||||
|
|
||||||
|
Note: If you place the kernel on the ext3 partition, you must re-create the
|
||||||
|
ext3 filesystem, since the factory u-boot can only handle 128 byte inodes and
|
||||||
|
cannot read the partition otherwise.
|
||||||
|
|
||||||
Steps:
|
Steps:
|
||||||
|
|
||||||
1. Remove the USB disk from the edgerouter and insert it into a computer
|
1. Remove the USB disk from the edgerouter and insert it into a computer
|
||||||
that has access to your build artifacts.
|
that has access to your build artifacts.
|
||||||
|
|
||||||
2. Flash the image.
|
2. Copy the kernel image to the USB storage (assuming discovered as 'sdb' on
|
||||||
|
the development machine):
|
||||||
|
|
||||||
# dd core-image-minimal-edgerouter.wic of=/dev/sdb
|
2a) if booting from vfat
|
||||||
|
|
||||||
|
# mount /dev/sdb1 /mnt
|
||||||
|
# cp tmp/deploy/images/edgerouter/vmlinux /mnt
|
||||||
|
# umount /mnt
|
||||||
|
|
||||||
3. Insert USB disk into the edgerouter and boot it.
|
2b) if booting from ext3
|
||||||
|
|
||||||
|
# mkfs.ext3 -I 128 /dev/sdb2
|
||||||
|
# mount /dev/sdb2 /mnt
|
||||||
|
# mkdir /mnt/boot
|
||||||
|
# cp tmp/deploy/images/edgerouter/vmlinux /mnt/boot
|
||||||
|
# umount /mnt
|
||||||
|
|
||||||
|
3. Extract the rootfs to the USB storage ext3 partition
|
||||||
|
|
||||||
|
# mount /dev/sdb2 /mnt
|
||||||
|
# tar -xvjpf core-image-minimal-XXX.tar.bz2 -C /mnt
|
||||||
|
# umount /mnt
|
||||||
|
|
||||||
|
4. Reboot the board and press a key on the terminal when prompted to get to the U-Boot
|
||||||
|
command line:
|
||||||
|
|
||||||
|
5. Load the kernel and boot:
|
||||||
|
|
||||||
|
5a) vfat boot
|
||||||
|
|
||||||
|
=> fatload usb 0:1 $loadaddr vmlinux
|
||||||
|
|
||||||
|
5b) ext3 boot
|
||||||
|
|
||||||
|
=> ext2load usb 0:2 $loadaddr boot/vmlinux
|
||||||
|
|
||||||
|
=> bootoctlinux $loadaddr coremask=0x3 root=/dev/sda2 rw rootwait mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@3072k(eeprom)
|
||||||
|
|
||||||
|
|
||||||
Type 2: NFS
|
Type 2: NFS
|
||||||
-----------
|
-----------
|
||||||
|
|||||||
@@ -5,13 +5,6 @@ The following external components are distributed with this software:
|
|||||||
* The Toaster Simple UI application is based upon the Django project template, the files of which are covered by the BSD license and are copyright (c) Django Software
|
* The Toaster Simple UI application is based upon the Django project template, the files of which are covered by the BSD license and are copyright (c) Django Software
|
||||||
Foundation and individual contributors.
|
Foundation and individual contributors.
|
||||||
|
|
||||||
* Twitter Bootstrap (including Glyphicons), redistributed under the MIT license
|
* Twitter Bootstrap (including Glyphicons), redistributed under the Apache License 2.0.
|
||||||
|
|
||||||
* jQuery is redistributed under the MIT license.
|
* jQuery is redistributed under the MIT license.
|
||||||
|
|
||||||
* Twitter typeahead.js redistributed under the MIT license. Note that the JS source has one small modification, so the full unminified file is currently included to make it obvious where this is.
|
|
||||||
|
|
||||||
* jsrender is redistributed under the MIT license.
|
|
||||||
|
|
||||||
* QUnit is redistributed under the MIT license.
|
|
||||||
|
|
||||||
* Font Awesome fonts redistributed under the SIL Open Font License 1.1
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# ex:ts=4:sw=4:sts=4:et
|
# ex:ts=4:sw=4:sts=4:et
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
#
|
#
|
||||||
@@ -35,10 +35,7 @@ except RuntimeError as exc:
|
|||||||
from bb import cookerdata
|
from bb import cookerdata
|
||||||
from bb.main import bitbake_main, BitBakeConfigParameters, BBMainException
|
from bb.main import bitbake_main, BitBakeConfigParameters, BBMainException
|
||||||
|
|
||||||
if sys.getfilesystemencoding() != "utf-8":
|
__version__ = "1.28.0"
|
||||||
sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.")
|
|
||||||
|
|
||||||
__version__ = "1.32.0"
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if __version__ != bb.__version__:
|
if __version__ != bb.__version__:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# bitbake-diffsigs
|
# bitbake-diffsigs
|
||||||
# BitBake task signature data comparison utility
|
# BitBake task signature data comparison utility
|
||||||
@@ -24,7 +24,6 @@ import warnings
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import optparse
|
import optparse
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ def find_compare_task(bbhandler, pn, taskname):
|
|||||||
# Recurse into signature comparison
|
# Recurse into signature comparison
|
||||||
output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
|
output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
|
||||||
if output:
|
if output:
|
||||||
print('\n'.join(output))
|
print '\n'.join(output)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -115,13 +114,14 @@ parser.add_option("-t", "--task",
|
|||||||
options, args = parser.parse_args(sys.argv)
|
options, args = parser.parse_args(sys.argv)
|
||||||
|
|
||||||
if options.taskargs:
|
if options.taskargs:
|
||||||
with bb.tinfoil.Tinfoil() as tinfoil:
|
tinfoil = bb.tinfoil.Tinfoil()
|
||||||
tinfoil.prepare(config_only=True)
|
tinfoil.prepare(config_only = True)
|
||||||
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
|
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
|
||||||
else:
|
else:
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
else:
|
else:
|
||||||
|
import cPickle
|
||||||
try:
|
try:
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
output = bb.siggen.dump_sigfile(sys.argv[1])
|
output = bb.siggen.dump_sigfile(sys.argv[1])
|
||||||
@@ -130,9 +130,9 @@ else:
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except (pickle.UnpicklingError, EOFError):
|
except cPickle.UnpicklingError, EOFError:
|
||||||
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
|
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if output:
|
if output:
|
||||||
print('\n'.join(output))
|
print '\n'.join(output)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# bitbake-dumpsig
|
# bitbake-dumpsig
|
||||||
# BitBake task signature dump utility
|
# BitBake task signature dump utility
|
||||||
@@ -23,7 +23,6 @@ import sys
|
|||||||
import warnings
|
import warnings
|
||||||
import optparse
|
import optparse
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||||
|
|
||||||
@@ -52,14 +51,15 @@ options, args = parser.parse_args(sys.argv)
|
|||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
else:
|
else:
|
||||||
|
import cPickle
|
||||||
try:
|
try:
|
||||||
output = bb.siggen.dump_sigfile(args[1])
|
output = bb.siggen.dump_sigfile(args[1])
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except (pickle.UnpicklingError, EOFError):
|
except cPickle.UnpicklingError, EOFError:
|
||||||
logger.error('Invalid signature data - ensure you are specifying a sigdata/siginfo file')
|
logger.error('Invalid signature data - ensure you are specifying a sigdata/siginfo file')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if output:
|
if output:
|
||||||
print('\n'.join(output))
|
print '\n'.join(output)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
import os
|
import os
|
||||||
import sys,logging
|
import sys,logging
|
||||||
import optparse
|
import optparse
|
||||||
@@ -50,6 +50,6 @@ if __name__ == "__main__":
|
|||||||
except Exception:
|
except Exception:
|
||||||
ret = 1
|
ret = 1
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc(5)
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012 Richard Purdie
|
# Copyright (C) 2012 Richard Purdie
|
||||||
#
|
#
|
||||||
@@ -25,48 +25,31 @@ try:
|
|||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
sys.exit(str(exc))
|
sys.exit(str(exc))
|
||||||
|
|
||||||
tests = ["bb.tests.codeparser",
|
def usage():
|
||||||
"bb.tests.cow",
|
print('usage: [BB_SKIP_NETTESTS=yes] %s [-v] [testname1 [testname2]...]' % os.path.basename(sys.argv[0]))
|
||||||
"bb.tests.data",
|
|
||||||
"bb.tests.fetch",
|
verbosity = 1
|
||||||
"bb.tests.parse",
|
|
||||||
"bb.tests.utils"]
|
tests = sys.argv[1:]
|
||||||
|
if '-v' in sys.argv:
|
||||||
|
tests.remove('-v')
|
||||||
|
verbosity = 2
|
||||||
|
|
||||||
|
if tests:
|
||||||
|
if '--help' in sys.argv[1:]:
|
||||||
|
usage()
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
tests = ["bb.tests.codeparser",
|
||||||
|
"bb.tests.cow",
|
||||||
|
"bb.tests.data",
|
||||||
|
"bb.tests.fetch",
|
||||||
|
"bb.tests.parse",
|
||||||
|
"bb.tests.utils"]
|
||||||
|
|
||||||
for t in tests:
|
for t in tests:
|
||||||
t = '.'.join(t.split('.')[:3])
|
t = '.'.join(t.split('.')[:3])
|
||||||
__import__(t)
|
__import__(t)
|
||||||
|
|
||||||
|
unittest.main(argv=["bitbake-selftest"] + tests, verbosity=verbosity)
|
||||||
|
|
||||||
# Set-up logging
|
|
||||||
class StdoutStreamHandler(logging.StreamHandler):
|
|
||||||
"""Special handler so that unittest is able to capture stdout"""
|
|
||||||
def __init__(self):
|
|
||||||
# Override __init__() because we don't want to set self.stream here
|
|
||||||
logging.Handler.__init__(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stream(self):
|
|
||||||
# We want to dynamically write wherever sys.stdout is pointing to
|
|
||||||
return sys.stdout
|
|
||||||
|
|
||||||
|
|
||||||
handler = StdoutStreamHandler()
|
|
||||||
bb.logger.addHandler(handler)
|
|
||||||
bb.logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
|
|
||||||
ENV_HELP = """\
|
|
||||||
Environment variables:
|
|
||||||
BB_SKIP_NETTESTS set to 'yes' in order to skip tests using network
|
|
||||||
connection
|
|
||||||
BB_TMPDIR_NOCLEAN set to 'yes' to preserve test tmp directories
|
|
||||||
"""
|
|
||||||
|
|
||||||
class main(unittest.main):
|
|
||||||
def _print_help(self, *args, **kwargs):
|
|
||||||
super(main, self)._print_help(*args, **kwargs)
|
|
||||||
print(ENV_HELP)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main(defaultTest=tests, buffer=True)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@@ -10,14 +10,7 @@ import bb
|
|||||||
import select
|
import select
|
||||||
import errno
|
import errno
|
||||||
import signal
|
import signal
|
||||||
import pickle
|
|
||||||
import traceback
|
|
||||||
import queue
|
|
||||||
from multiprocessing import Lock
|
from multiprocessing import Lock
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
if sys.getfilesystemencoding() != "utf-8":
|
|
||||||
sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.")
|
|
||||||
|
|
||||||
# Users shouldn't be running this code directly
|
# Users shouldn't be running this code directly
|
||||||
if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
|
if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
|
||||||
@@ -25,7 +18,7 @@ if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
profiling = False
|
profiling = False
|
||||||
if sys.argv[1].startswith("decafbadbad"):
|
if sys.argv[1] == "decafbadbad":
|
||||||
profiling = True
|
profiling = True
|
||||||
try:
|
try:
|
||||||
import cProfile as profile
|
import cProfile as profile
|
||||||
@@ -37,16 +30,19 @@ if sys.argv[1].startswith("decafbadbad"):
|
|||||||
# updates to log files for use with tail
|
# updates to log files for use with tail
|
||||||
try:
|
try:
|
||||||
if sys.stdout.name == '<stdout>':
|
if sys.stdout.name == '<stdout>':
|
||||||
import fcntl
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||||
fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
|
|
||||||
fl |= os.O_SYNC
|
|
||||||
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
|
|
||||||
#sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger = logging.getLogger("BitBake")
|
logger = logging.getLogger("BitBake")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||||
|
|
||||||
|
|
||||||
worker_pipe = sys.stdout.fileno()
|
worker_pipe = sys.stdout.fileno()
|
||||||
bb.utils.nonblockingfd(worker_pipe)
|
bb.utils.nonblockingfd(worker_pipe)
|
||||||
# Need to guard against multiprocessing being used in child processes
|
# Need to guard against multiprocessing being used in child processes
|
||||||
@@ -66,53 +62,36 @@ if 0:
|
|||||||
consolelog.setFormatter(conlogformat)
|
consolelog.setFormatter(conlogformat)
|
||||||
logger.addHandler(consolelog)
|
logger.addHandler(consolelog)
|
||||||
|
|
||||||
worker_queue = queue.Queue()
|
worker_queue = ""
|
||||||
|
|
||||||
def worker_fire(event, d):
|
def worker_fire(event, d):
|
||||||
data = b"<event>" + pickle.dumps(event) + b"</event>"
|
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||||
worker_fire_prepickled(data)
|
worker_fire_prepickled(data)
|
||||||
|
|
||||||
def worker_fire_prepickled(event):
|
def worker_fire_prepickled(event):
|
||||||
global worker_queue
|
global worker_queue
|
||||||
|
|
||||||
worker_queue.put(event)
|
worker_queue = worker_queue + event
|
||||||
|
worker_flush()
|
||||||
|
|
||||||
#
|
def worker_flush():
|
||||||
# We can end up with write contention with the cooker, it can be trying to send commands
|
global worker_queue, worker_pipe
|
||||||
# and we can be trying to send event data back. Therefore use a separate thread for writing
|
|
||||||
# back data to cooker.
|
|
||||||
#
|
|
||||||
worker_thread_exit = False
|
|
||||||
|
|
||||||
def worker_flush(worker_queue):
|
if not worker_queue:
|
||||||
worker_queue_int = b""
|
return
|
||||||
global worker_pipe, worker_thread_exit
|
|
||||||
|
|
||||||
while True:
|
try:
|
||||||
try:
|
written = os.write(worker_pipe, worker_queue)
|
||||||
worker_queue_int = worker_queue_int + worker_queue.get(True, 1)
|
worker_queue = worker_queue[written:]
|
||||||
except queue.Empty:
|
except (IOError, OSError) as e:
|
||||||
pass
|
if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
|
||||||
while (worker_queue_int or not worker_queue.empty()):
|
raise
|
||||||
try:
|
|
||||||
if not worker_queue.empty():
|
|
||||||
worker_queue_int = worker_queue_int + worker_queue.get()
|
|
||||||
written = os.write(worker_pipe, worker_queue_int)
|
|
||||||
worker_queue_int = worker_queue_int[written:]
|
|
||||||
except (IOError, OSError) as e:
|
|
||||||
if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
|
|
||||||
raise
|
|
||||||
if worker_thread_exit and worker_queue.empty() and not worker_queue_int:
|
|
||||||
return
|
|
||||||
|
|
||||||
worker_thread = Thread(target=worker_flush, args=(worker_queue,))
|
|
||||||
worker_thread.start()
|
|
||||||
|
|
||||||
def worker_child_fire(event, d):
|
def worker_child_fire(event, d):
|
||||||
global worker_pipe
|
global worker_pipe
|
||||||
global worker_pipe_lock
|
global worker_pipe_lock
|
||||||
|
|
||||||
data = b"<event>" + pickle.dumps(event) + b"</event>"
|
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||||
try:
|
try:
|
||||||
worker_pipe_lock.acquire()
|
worker_pipe_lock.acquire()
|
||||||
worker_pipe.write(data)
|
worker_pipe.write(data)
|
||||||
@@ -135,7 +114,7 @@ def sigterm_handler(signum, frame):
|
|||||||
os.killpg(0, signal.SIGTERM)
|
os.killpg(0, signal.SIGTERM)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, appends, taskdepdata, quieterrors=False):
|
def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdata, quieterrors=False):
|
||||||
# We need to setup the environment BEFORE the fork, since
|
# We need to setup the environment BEFORE the fork, since
|
||||||
# a fork() or exec*() activates PSEUDO...
|
# a fork() or exec*() activates PSEUDO...
|
||||||
|
|
||||||
@@ -180,8 +159,7 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
|||||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.critical("fork failed: %d (%s)" % (e.errno, e.strerror))
|
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if pid == 0:
|
if pid == 0:
|
||||||
def child():
|
def child():
|
||||||
@@ -213,50 +191,34 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
|||||||
if umask:
|
if umask:
|
||||||
os.umask(umask)
|
os.umask(umask)
|
||||||
|
|
||||||
|
data.setVar("BB_WORKERCONTEXT", "1")
|
||||||
|
data.setVar("BB_TASKDEPDATA", taskdepdata)
|
||||||
|
data.setVar("BUILDNAME", workerdata["buildname"])
|
||||||
|
data.setVar("DATE", workerdata["date"])
|
||||||
|
data.setVar("TIME", workerdata["time"])
|
||||||
|
bb.parse.siggen.set_taskdata(workerdata["sigdata"])
|
||||||
|
ret = 0
|
||||||
try:
|
try:
|
||||||
bb_cache = bb.cache.NoCache(databuilder)
|
the_data = bb.cache.Cache.loadDataFull(fn, appends, data)
|
||||||
(realfn, virtual, mc) = bb.cache.virtualfn2realfn(fn)
|
|
||||||
the_data = databuilder.mcdata[mc]
|
|
||||||
the_data.setVar("BB_WORKERCONTEXT", "1")
|
|
||||||
the_data.setVar("BB_TASKDEPDATA", taskdepdata)
|
|
||||||
the_data.setVar("BUILDNAME", workerdata["buildname"])
|
|
||||||
the_data.setVar("DATE", workerdata["date"])
|
|
||||||
the_data.setVar("TIME", workerdata["time"])
|
|
||||||
bb.parse.siggen.set_taskdata(workerdata["sigdata"])
|
|
||||||
ret = 0
|
|
||||||
|
|
||||||
the_data = bb_cache.loadDataFull(fn, appends)
|
|
||||||
the_data.setVar('BB_TASKHASH', workerdata["runq_hash"][task])
|
the_data.setVar('BB_TASKHASH', workerdata["runq_hash"][task])
|
||||||
|
|
||||||
bb.utils.set_process_name("%s:%s" % (the_data.getVar("PN", True), taskname.replace("do_", "")))
|
|
||||||
|
|
||||||
# exported_vars() returns a generator which *cannot* be passed to os.environ.update()
|
# exported_vars() returns a generator which *cannot* be passed to os.environ.update()
|
||||||
# successfully. We also need to unset anything from the environment which shouldn't be there
|
# successfully. We also need to unset anything from the environment which shouldn't be there
|
||||||
exports = bb.data.exported_vars(the_data)
|
exports = bb.data.exported_vars(the_data)
|
||||||
|
|
||||||
bb.utils.empty_environment()
|
bb.utils.empty_environment()
|
||||||
for e, v in exports:
|
for e, v in exports:
|
||||||
os.environ[e] = v
|
os.environ[e] = v
|
||||||
|
|
||||||
for e in fakeenv:
|
for e in fakeenv:
|
||||||
os.environ[e] = fakeenv[e]
|
os.environ[e] = fakeenv[e]
|
||||||
the_data.setVar(e, fakeenv[e])
|
the_data.setVar(e, fakeenv[e])
|
||||||
the_data.setVarFlag(e, 'export', "1")
|
the_data.setVarFlag(e, 'export', "1")
|
||||||
|
|
||||||
task_exports = the_data.getVarFlag(taskname, 'exports', True)
|
|
||||||
if task_exports:
|
|
||||||
for e in task_exports.split():
|
|
||||||
the_data.setVarFlag(e, 'export', '1')
|
|
||||||
v = the_data.getVar(e, True)
|
|
||||||
if v is not None:
|
|
||||||
os.environ[e] = v
|
|
||||||
|
|
||||||
if quieterrors:
|
if quieterrors:
|
||||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||||
|
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
if not quieterrors:
|
if not quieterrors:
|
||||||
logger.critical(traceback.format_exc())
|
logger.critical(str(exc))
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
try:
|
try:
|
||||||
if cfg.dry_run:
|
if cfg.dry_run:
|
||||||
@@ -276,7 +238,7 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
|||||||
bb.utils.process_profilelog(profname)
|
bb.utils.process_profilelog(profname)
|
||||||
os._exit(ret)
|
os._exit(ret)
|
||||||
else:
|
else:
|
||||||
for key, value in iter(envbackup.items()):
|
for key, value in envbackup.iteritems():
|
||||||
if value is None:
|
if value is None:
|
||||||
del os.environ[key]
|
del os.environ[key]
|
||||||
else:
|
else:
|
||||||
@@ -293,22 +255,22 @@ class runQueueWorkerPipe():
|
|||||||
if pipeout:
|
if pipeout:
|
||||||
pipeout.close()
|
pipeout.close()
|
||||||
bb.utils.nonblockingfd(self.input)
|
bb.utils.nonblockingfd(self.input)
|
||||||
self.queue = b""
|
self.queue = ""
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
start = len(self.queue)
|
start = len(self.queue)
|
||||||
try:
|
try:
|
||||||
self.queue = self.queue + (self.input.read(102400) or b"")
|
self.queue = self.queue + self.input.read(102400)
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
if e.errno != errno.EAGAIN:
|
if e.errno != errno.EAGAIN:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
end = len(self.queue)
|
end = len(self.queue)
|
||||||
index = self.queue.find(b"</event>")
|
index = self.queue.find("</event>")
|
||||||
while index != -1:
|
while index != -1:
|
||||||
worker_fire_prepickled(self.queue[:index+8])
|
worker_fire_prepickled(self.queue[:index+8])
|
||||||
self.queue = self.queue[index+8:]
|
self.queue = self.queue[index+8:]
|
||||||
index = self.queue.find(b"</event>")
|
index = self.queue.find("</event>")
|
||||||
return (end > start)
|
return (end > start)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@@ -324,7 +286,7 @@ class BitbakeWorker(object):
|
|||||||
def __init__(self, din):
|
def __init__(self, din):
|
||||||
self.input = din
|
self.input = din
|
||||||
bb.utils.nonblockingfd(self.input)
|
bb.utils.nonblockingfd(self.input)
|
||||||
self.queue = b""
|
self.queue = ""
|
||||||
self.cookercfg = None
|
self.cookercfg = None
|
||||||
self.databuilder = None
|
self.databuilder = None
|
||||||
self.data = None
|
self.data = None
|
||||||
@@ -334,16 +296,12 @@ class BitbakeWorker(object):
|
|||||||
signal.signal(signal.SIGTERM, self.sigterm_exception)
|
signal.signal(signal.SIGTERM, self.sigterm_exception)
|
||||||
# Let SIGHUP exit as SIGTERM
|
# Let SIGHUP exit as SIGTERM
|
||||||
signal.signal(signal.SIGHUP, self.sigterm_exception)
|
signal.signal(signal.SIGHUP, self.sigterm_exception)
|
||||||
if "beef" in sys.argv[1]:
|
|
||||||
bb.utils.set_process_name("Worker (Fakeroot)")
|
|
||||||
else:
|
|
||||||
bb.utils.set_process_name("Worker")
|
|
||||||
|
|
||||||
def sigterm_exception(self, signum, stackframe):
|
def sigterm_exception(self, signum, stackframe):
|
||||||
if signum == signal.SIGTERM:
|
if signum == signal.SIGTERM:
|
||||||
bb.warn("Worker received SIGTERM, shutting down...")
|
bb.warn("Worker recieved SIGTERM, shutting down...")
|
||||||
elif signum == signal.SIGHUP:
|
elif signum == signal.SIGHUP:
|
||||||
bb.warn("Worker received SIGHUP, shutting down...")
|
bb.warn("Worker recieved SIGHUP, shutting down...")
|
||||||
self.handle_finishnow(None)
|
self.handle_finishnow(None)
|
||||||
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
||||||
os.kill(os.getpid(), signal.SIGTERM)
|
os.kill(os.getpid(), signal.SIGTERM)
|
||||||
@@ -361,26 +319,27 @@ class BitbakeWorker(object):
|
|||||||
except (OSError, IOError):
|
except (OSError, IOError):
|
||||||
pass
|
pass
|
||||||
if len(self.queue):
|
if len(self.queue):
|
||||||
self.handle_item(b"cookerconfig", self.handle_cookercfg)
|
self.handle_item("cookerconfig", self.handle_cookercfg)
|
||||||
self.handle_item(b"workerdata", self.handle_workerdata)
|
self.handle_item("workerdata", self.handle_workerdata)
|
||||||
self.handle_item(b"runtask", self.handle_runtask)
|
self.handle_item("runtask", self.handle_runtask)
|
||||||
self.handle_item(b"finishnow", self.handle_finishnow)
|
self.handle_item("finishnow", self.handle_finishnow)
|
||||||
self.handle_item(b"ping", self.handle_ping)
|
self.handle_item("ping", self.handle_ping)
|
||||||
self.handle_item(b"quit", self.handle_quit)
|
self.handle_item("quit", self.handle_quit)
|
||||||
|
|
||||||
for pipe in self.build_pipes:
|
for pipe in self.build_pipes:
|
||||||
self.build_pipes[pipe].read()
|
self.build_pipes[pipe].read()
|
||||||
if len(self.build_pids):
|
if len(self.build_pids):
|
||||||
self.process_waitpid()
|
self.process_waitpid()
|
||||||
|
worker_flush()
|
||||||
|
|
||||||
|
|
||||||
def handle_item(self, item, func):
|
def handle_item(self, item, func):
|
||||||
if self.queue.startswith(b"<" + item + b">"):
|
if self.queue.startswith("<" + item + ">"):
|
||||||
index = self.queue.find(b"</" + item + b">")
|
index = self.queue.find("</" + item + ">")
|
||||||
while index != -1:
|
while index != -1:
|
||||||
func(self.queue[(len(item) + 2):index])
|
func(self.queue[(len(item) + 2):index])
|
||||||
self.queue = self.queue[(index + len(item) + 3):]
|
self.queue = self.queue[(index + len(item) + 3):]
|
||||||
index = self.queue.find(b"</" + item + b">")
|
index = self.queue.find("</" + item + ">")
|
||||||
|
|
||||||
def handle_cookercfg(self, data):
|
def handle_cookercfg(self, data):
|
||||||
self.cookercfg = pickle.loads(data)
|
self.cookercfg = pickle.loads(data)
|
||||||
@@ -394,13 +353,12 @@ class BitbakeWorker(object):
|
|||||||
bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
|
bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
|
||||||
bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
|
bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
|
||||||
bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
|
bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
|
||||||
for mc in self.databuilder.mcdata:
|
self.data.setVar("PRSERV_HOST", self.workerdata["prhost"])
|
||||||
self.databuilder.mcdata[mc].setVar("PRSERV_HOST", self.workerdata["prhost"])
|
|
||||||
|
|
||||||
def handle_ping(self, _):
|
def handle_ping(self, _):
|
||||||
workerlog_write("Handling ping\n")
|
workerlog_write("Handling ping\n")
|
||||||
|
|
||||||
logger.warning("Pong from bitbake-worker!")
|
logger.warn("Pong from bitbake-worker!")
|
||||||
|
|
||||||
def handle_quit(self, data):
|
def handle_quit(self, data):
|
||||||
workerlog_write("Handling quit\n")
|
workerlog_write("Handling quit\n")
|
||||||
@@ -413,7 +371,7 @@ class BitbakeWorker(object):
|
|||||||
fn, task, taskname, quieterrors, appends, taskdepdata = pickle.loads(data)
|
fn, task, taskname, quieterrors, appends, taskdepdata = pickle.loads(data)
|
||||||
workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
|
workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
|
||||||
|
|
||||||
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.databuilder, self.workerdata, fn, task, taskname, appends, taskdepdata, quieterrors)
|
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.workerdata, fn, task, taskname, appends, taskdepdata, quieterrors)
|
||||||
|
|
||||||
self.build_pids[pid] = task
|
self.build_pids[pid] = task
|
||||||
self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
|
self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
|
||||||
@@ -445,12 +403,12 @@ class BitbakeWorker(object):
|
|||||||
self.build_pipes[pid].close()
|
self.build_pipes[pid].close()
|
||||||
del self.build_pipes[pid]
|
del self.build_pipes[pid]
|
||||||
|
|
||||||
worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
|
worker_fire_prepickled("<exitcode>" + pickle.dumps((task, status)) + "</exitcode>")
|
||||||
|
|
||||||
def handle_finishnow(self, _):
|
def handle_finishnow(self, _):
|
||||||
if self.build_pids:
|
if self.build_pids:
|
||||||
logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
|
logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
|
||||||
for k, v in iter(self.build_pids.items()):
|
for k, v in self.build_pids.iteritems():
|
||||||
try:
|
try:
|
||||||
os.kill(-k, signal.SIGTERM)
|
os.kill(-k, signal.SIGTERM)
|
||||||
os.waitpid(-1, 0)
|
os.waitpid(-1, 0)
|
||||||
@@ -460,7 +418,7 @@ class BitbakeWorker(object):
|
|||||||
self.build_pipes[pipe].read()
|
self.build_pipes[pipe].read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
worker = BitbakeWorker(os.fdopen(sys.stdin.fileno(), 'rb'))
|
worker = BitbakeWorker(sys.stdin)
|
||||||
if not profiling:
|
if not profiling:
|
||||||
worker.serve()
|
worker.serve()
|
||||||
else:
|
else:
|
||||||
@@ -476,10 +434,8 @@ except BaseException as e:
|
|||||||
import traceback
|
import traceback
|
||||||
sys.stderr.write(traceback.format_exc())
|
sys.stderr.write(traceback.format_exc())
|
||||||
sys.stderr.write(str(e))
|
sys.stderr.write(str(e))
|
||||||
|
while len(worker_queue):
|
||||||
worker_thread_exit = True
|
worker_flush()
|
||||||
worker_thread.join()
|
|
||||||
|
|
||||||
workerlog_write("exitting")
|
workerlog_write("exitting")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# ex:ts=4:sw=4:sts=4:et
|
# ex:ts=4:sw=4:sts=4:et
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
#
|
#
|
||||||
@@ -462,7 +462,7 @@ def main():
|
|||||||
state_group = 2
|
state_group = 2
|
||||||
|
|
||||||
for key in bb.data.keys(documentation):
|
for key in bb.data.keys(documentation):
|
||||||
data = documentation.getVarFlag(key, "doc", False)
|
data = documentation.getVarFlag(key, "doc")
|
||||||
if not data:
|
if not data:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
122
bitbake/bin/image-writer
Executable file
122
bitbake/bin/image-writer
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/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", "ext4", "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)
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
#!/bin/echo ERROR: This script needs to be sourced. Please run as .
|
#!/bin/sh
|
||||||
|
# (c) 2013 Intel Corp.
|
||||||
# toaster - shell script to start Toaster
|
|
||||||
|
|
||||||
# Copyright (C) 2013-2015 Intel Corp.
|
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -15,24 +12,33 @@
|
|||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see http://www.gnu.org/licenses/.
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
HELP="
|
|
||||||
Usage: source toaster start|stop [webport=<address:port>] [noweb]
|
# This script can be run in two modes.
|
||||||
Optional arguments:
|
|
||||||
[noweb] Setup the environment for building with toaster but don't start the development server
|
# When used with "source", from a build directory,
|
||||||
[webport] Set the development server (default: localhost:8000)
|
# it enables toaster event logging and starts the bitbake resident server.
|
||||||
"
|
# use as: source toaster [start|stop] [noweb] [noui]
|
||||||
|
|
||||||
|
# When it is called as a stand-alone script, it starts just the
|
||||||
|
# web server, and the building shall be done through the web interface.
|
||||||
|
# As script, it will not return to the command prompt. Stop with Ctrl-C.
|
||||||
|
|
||||||
|
# Helper function to kill a background toaster development server
|
||||||
|
|
||||||
webserverKillAll()
|
webserverKillAll()
|
||||||
{
|
{
|
||||||
local pidfile
|
local pidfile
|
||||||
for pidfile in ${BUILDDIR}/.toastermain.pid ${BUILDDIR}/.runbuilds.pid; do
|
for pidfile in ${BUILDDIR}/.toastermain.pid; do
|
||||||
if [ -f ${pidfile} ]; then
|
if [ -f ${pidfile} ]; then
|
||||||
pid=`cat ${pidfile}`
|
pid=`cat ${pidfile}`
|
||||||
while kill -0 $pid 2>/dev/null; do
|
while kill -0 $pid 2>/dev/null; do
|
||||||
kill -SIGTERM -$pid 2>/dev/null
|
kill -SIGTERM -$pid 2>/dev/null
|
||||||
sleep 1
|
sleep 1
|
||||||
|
# Kill processes if they are still running - may happen in interactive shells
|
||||||
|
ps fux | grep "python.*manage.py runserver" | awk '{print $2}' | xargs kill
|
||||||
done
|
done
|
||||||
rm ${pidfile}
|
rm ${pidfile}
|
||||||
fi
|
fi
|
||||||
@@ -49,16 +55,35 @@ webserverStartAll()
|
|||||||
|
|
||||||
retval=0
|
retval=0
|
||||||
# you can always add a superuser later via
|
# you can always add a superuser later via
|
||||||
# ../bitbake/lib/toaster/manage.py createsuperuser --username=<ME>
|
# python bitbake/lib/toaster/manage.py python manage.py createsuperuser --username=<ME>
|
||||||
$MANAGE migrate --noinput || retval=1
|
python $BBBASEDIR/lib/toaster/manage.py syncdb --noinput || retval=1
|
||||||
|
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=2
|
||||||
|
|
||||||
if [ $retval -eq 1 ]; then
|
if [ $retval -eq 1 ]; then
|
||||||
echo "Failed migrations, aborting system start" 1>&2
|
echo "Failed db sync, aborting system start" 1>&2
|
||||||
return $retval
|
return $retval
|
||||||
fi
|
fi
|
||||||
# Make sure that checksettings can pick up any value for TEMPLATECONF
|
|
||||||
export TEMPLATECONF
|
python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1
|
||||||
$MANAGE checksettings --traceback || retval=1
|
|
||||||
|
if [ $retval -eq 1 ]; then
|
||||||
|
printf "\nError on orm migration, rolling back...\n"
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py migrate orm 0001_initial --fake
|
||||||
|
return $retval
|
||||||
|
fi
|
||||||
|
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
|
||||||
|
|
||||||
|
if [ $retval -eq 1 ]; then
|
||||||
|
printf "\nError on bldcontrol migration, rolling back...\n"
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol 0001_initial --fake
|
||||||
|
return $retval
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TOASTER_MANAGED" = '1' ]; then
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py checksettings --traceback || retval=1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ $retval -eq 1 ]; then
|
if [ $retval -eq 1 ]; then
|
||||||
printf "\nError while checking settings; aborting\n"
|
printf "\nError while checking settings; aborting\n"
|
||||||
@@ -67,9 +92,7 @@ webserverStartAll()
|
|||||||
|
|
||||||
echo "Starting webserver..."
|
echo "Starting webserver..."
|
||||||
|
|
||||||
$MANAGE runserver "$ADDR_PORT" \
|
python $BBBASEDIR/lib/toaster/manage.py runserver "0.0.0.0:$WEB_PORT" </dev/null >>${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
|
||||||
</dev/null >>${BUILDDIR}/toaster_web.log 2>&1 \
|
|
||||||
& echo $! >${BUILDDIR}/.toastermain.pid
|
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
@@ -77,13 +100,22 @@ webserverStartAll()
|
|||||||
retval=1
|
retval=1
|
||||||
rm "${BUILDDIR}/.toastermain.pid"
|
rm "${BUILDDIR}/.toastermain.pid"
|
||||||
else
|
else
|
||||||
echo "Toaster development webserver started at http://$ADDR_PORT"
|
echo "Webserver address: http://0.0.0.0:$WEB_PORT/"
|
||||||
echo -e "\nYou can now run 'bitbake <target>' on the command line and monitor your build in Toaster.\nYou can also use a Toaster project to configure and run a build.\n"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return $retval
|
return $retval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Helper functions to add a special configuration file
|
||||||
|
|
||||||
|
addtoConfiguration()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
shift
|
||||||
|
echo "#Created by toaster start script" > ${BUILDDIR}/conf/$file
|
||||||
|
for var in "$@"; do echo $var >> ${BUILDDIR}/conf/$file; done
|
||||||
|
}
|
||||||
|
|
||||||
INSTOPSYSTEM=0
|
INSTOPSYSTEM=0
|
||||||
|
|
||||||
# define the stop command
|
# define the stop command
|
||||||
@@ -92,32 +124,49 @@ stop_system()
|
|||||||
# prevent reentry
|
# prevent reentry
|
||||||
if [ $INSTOPSYSTEM -eq 1 ]; then return; fi
|
if [ $INSTOPSYSTEM -eq 1 ]; then return; fi
|
||||||
INSTOPSYSTEM=1
|
INSTOPSYSTEM=1
|
||||||
|
if [ -f ${BUILDDIR}/.toasterui.pid ]; then
|
||||||
|
kill `cat ${BUILDDIR}/.toasterui.pid` 2>/dev/null
|
||||||
|
rm ${BUILDDIR}/.toasterui.pid
|
||||||
|
fi
|
||||||
|
BBSERVER=0.0.0.0:-1 bitbake -m
|
||||||
|
unset BBSERVER
|
||||||
webserverKillAll
|
webserverKillAll
|
||||||
# unset exported variables
|
# force stop any misbehaving bitbake server
|
||||||
unset TOASTER_DIR
|
lsof bitbake.lock | awk '{print $2}' | grep "[0-9]\+" | xargs -n1 -r kill
|
||||||
unset BITBAKE_UI
|
|
||||||
unset BBBASEDIR
|
|
||||||
trap - SIGHUP
|
trap - SIGHUP
|
||||||
#trap - SIGCHLD
|
#trap - SIGCHLD
|
||||||
INSTOPSYSTEM=0
|
INSTOPSYSTEM=0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_pidbyfile() {
|
||||||
|
[ -e $1 ] && kill -0 `cat $1` 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notify_chldexit() {
|
||||||
|
if [ $NOTOASTERUI -eq 0 ]; then
|
||||||
|
check_pidbyfile ${BUILDDIR}/.toasterui.pid && return
|
||||||
|
stop_system
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
verify_prereq() {
|
verify_prereq() {
|
||||||
# Verify Django version
|
# Verify prerequisites
|
||||||
reqfile=$(python3 -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))")
|
|
||||||
exp='s/Django\([><=]\+\)\([^,]\+\),\([><=]\+\)\(.\+\)/'
|
if ! echo "import django; print (1,) == django.VERSION[0:1] and django.VERSION[1:2][0] in (6,)" | python 2>/dev/null | grep True >/dev/null; then
|
||||||
exp=$exp'import sys,django;version=django.get_version().split(".");'
|
printf "This program needs Django 1.6. Please install with\n\npip install django==1.6\n"
|
||||||
exp=$exp'sys.exit(not (version \1 "\2".split(".") and version \3 "\4".split(".")))/p'
|
|
||||||
if ! sed -n "$exp" $reqfile | python3 - ; then
|
|
||||||
req=`grep ^Django $reqfile`
|
|
||||||
echo "This program needs $req"
|
|
||||||
echo "Please install with pip install -r $reqfile"
|
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! echo "import south; print reduce(lambda x, y: 2 if x==2 else 0 if x == 0 else y, map(lambda x: 1+cmp(x[1]-x[0],0), zip([0,8,4], map(int,south.__version__.split(\".\"))))) > 0" | python 2>/dev/null | grep True >/dev/null; then
|
||||||
|
printf "This program needs South 0.8.4. Please install with\n\npip install south==0.8.4\n"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# read command line parameters
|
# read command line parameters
|
||||||
if [ -n "$BASH_SOURCE" ] ; then
|
if [ -n "$BASH_SOURCE" ] ; then
|
||||||
TOASTER=${BASH_SOURCE}
|
TOASTER=${BASH_SOURCE}
|
||||||
@@ -127,30 +176,33 @@ else
|
|||||||
TOASTER=$0
|
TOASTER=$0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export BBBASEDIR=`dirname $TOASTER`/..
|
[ `basename \"$0\"` = `basename \"${TOASTER}\"` ] && TOASTER_MANAGED=1
|
||||||
MANAGE="python3 $BBBASEDIR/lib/toaster/manage.py"
|
|
||||||
OE_ROOT=`dirname $TOASTER`/../..
|
|
||||||
|
|
||||||
# this is the configuraton file we are using for toaster
|
BBBASEDIR=`dirname $TOASTER`/..
|
||||||
# we are using the same logic that oe-setup-builddir uses
|
|
||||||
# (based on TEMPLATECONF and .templateconf) to determine
|
|
||||||
# which toasterconf.json to use.
|
|
||||||
# note: There are a number of relative path assumptions
|
|
||||||
# in the local layers that currently make using an arbitrary
|
|
||||||
# toasterconf.json difficult.
|
|
||||||
|
|
||||||
. $OE_ROOT/.templateconf
|
RUNNING=0
|
||||||
if [ -n "$TEMPLATECONF" ]; then
|
|
||||||
if [ ! -d "$TEMPLATECONF" ]; then
|
NOTOASTERUI=0
|
||||||
# Allow TEMPLATECONF=meta-xyz/conf as a shortcut
|
WEBSERVER=1
|
||||||
if [ -d "$OE_ROOT/$TEMPLATECONF" ]; then
|
TOASTER_BRBE=""
|
||||||
TEMPLATECONF="$OE_ROOT/$TEMPLATECONF"
|
if [ "$WEB_PORT" = "" ]; then
|
||||||
fi
|
WEB_PORT="8000"
|
||||||
fi
|
fi
|
||||||
|
# this is the configuraton file we are using for toaster
|
||||||
|
# note default is assuming yocto. Override this if you are
|
||||||
|
# running in a pure OE environment and use the toasterconf.json
|
||||||
|
# in meta/conf/toasterconf.json
|
||||||
|
# note: for future there are a number of relative path assumptions
|
||||||
|
# in the local layers that currently prevent using an arbitrary
|
||||||
|
# toasterconf.json
|
||||||
|
if [ "$TOASTER_CONF" = "" ]; then
|
||||||
|
TOASTER_CONF="$(dirname $TOASTER)/../../meta-yocto/conf/toasterconf.json"
|
||||||
|
export TOASTER_CONF=$(python -c "import os; print os.path.realpath('$TOASTER_CONF')")
|
||||||
|
fi
|
||||||
|
if [ ! -f $TOASTER_CONF ]; then
|
||||||
|
echo "$TOASTER_CONF configuration file not found. set TOASTER_CONF to specify a path"
|
||||||
|
[ "$TOASTER_MANAGED" = '1' ] && exit 1 || return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
unset OE_ROOT
|
|
||||||
|
|
||||||
# this defines the dir toaster will use for
|
# this defines the dir toaster will use for
|
||||||
# 1) clones of layers (in _toaster_clones )
|
# 1) clones of layers (in _toaster_clones )
|
||||||
# 2) the build dir (in build)
|
# 2) the build dir (in build)
|
||||||
@@ -160,48 +212,102 @@ unset OE_ROOT
|
|||||||
# make sure that the toaster.sqlite file doesn't default to `pwd` like it currently does.
|
# make sure that the toaster.sqlite file doesn't default to `pwd` like it currently does.
|
||||||
export TOASTER_DIR=`pwd`
|
export TOASTER_DIR=`pwd`
|
||||||
|
|
||||||
WEBSERVER=1
|
|
||||||
ADDR_PORT="localhost:8000"
|
NOBROWSER=0
|
||||||
unset CMD
|
|
||||||
for param in $*; do
|
for param in $*; do
|
||||||
case $param in
|
case $param in
|
||||||
|
noui )
|
||||||
|
NOTOASTERUI=1
|
||||||
|
;;
|
||||||
noweb )
|
noweb )
|
||||||
WEBSERVER=0
|
WEBSERVER=0
|
||||||
;;
|
;;
|
||||||
start )
|
nobrowser )
|
||||||
CMD=$param
|
NOBROWSER=1
|
||||||
;;
|
;;
|
||||||
stop )
|
brbe=* )
|
||||||
CMD=$param
|
TOASTER_BRBE=$'\n'"TOASTER_BRBE=\""${param#*=}"\""
|
||||||
;;
|
;;
|
||||||
webport=*)
|
webport=*)
|
||||||
ADDR_PORT="${param#*=}"
|
WEB_PORT="${param#*=}"
|
||||||
# Split the addr:port string
|
|
||||||
ADDR=`echo $ADDR_PORT | cut -f 1 -d ':'`
|
|
||||||
PORT=`echo $ADDR_PORT | cut -f 2 -d ':'`
|
|
||||||
# If only a port has been speified then set address to localhost.
|
|
||||||
if [ $ADDR = $PORT ] ; then
|
|
||||||
ADDR_PORT="localhost:$PORT"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--help)
|
|
||||||
echo "$HELP"
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "$HELP"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ `basename \"$0\"` = `basename \"${TOASTER}\"` ]; then
|
if [ "$TOASTER_MANAGED" = '1' ]; then
|
||||||
echo "Error: This script needs to be sourced. Please run as . $TOASTER"
|
# We are called as standalone. We refuse to run in a build environment - we need the interactive mode for that.
|
||||||
|
# Start just the web server, point the web browser to the interface, and start any Django services.
|
||||||
|
|
||||||
|
if ! verify_prereq; then
|
||||||
|
echo "Error: Could not verify that the needed dependencies are installed. Please use virtualenv and pip to install dependencies listed in toaster-requirements.txt" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$BUILDDIR" ]; then
|
||||||
|
printf "Error: It looks like you sourced oe-init-build-env. Toaster cannot start in build mode from an oe-core build environment.\n You should be starting Toaster from a new terminal window.\n" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define a fake builddir where only the pid files are actually created. No real builds will take place here.
|
||||||
|
BUILDDIR=/tmp/toaster_$$
|
||||||
|
if [ -d "$BUILDDIR" ]; then
|
||||||
|
echo "Previous toaster run directory $BUILDDIR found, cowardly refusing to start. Please remove the directory when that toaster instance is over" 2>&1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$BUILDDIR"
|
||||||
|
|
||||||
|
RUNNING=1
|
||||||
|
trap_ctrlc() {
|
||||||
|
echo "** Stopping system"
|
||||||
|
webserverKillAll
|
||||||
|
RUNNING=0
|
||||||
|
}
|
||||||
|
|
||||||
|
do_cleanup() {
|
||||||
|
find "$BUILDDIR" -type f | xargs rm
|
||||||
|
rmdir "$BUILDDIR"
|
||||||
|
}
|
||||||
|
cleanup() {
|
||||||
|
if grep -ir error "$BUILDDIR" >/dev/null; then
|
||||||
|
if grep -irn "That port is already in use" "$BUILDDIR"; then
|
||||||
|
echo "You can use the \"webport=PORTNUMBER\" parameter to start Toaster on a different port (port $WEB_PORT is already in use)"
|
||||||
|
do_cleanup
|
||||||
|
else
|
||||||
|
printf "\nErrors found in the Toaster log files present in '$BUILDDIR'. Directory will not be cleaned.\n Please review the errors and notify toaster@yoctoproject.org or submit a bug https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Toaster"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No errors found, removing the run directory '$BUILDDIR'"
|
||||||
|
do_cleanup
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export TOASTER_MANAGED=1
|
||||||
|
if [ $WEBSERVER -gt 0 ] && ! webserverStartAll; then
|
||||||
|
echo "Failed to start the web server, stopping" 1>&2
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $WEBSERVER -gt 0 ] && [ $NOBROWSER -eq 0 ] ; then
|
||||||
|
echo "Starting browser..."
|
||||||
|
xdg-open http://127.0.0.1:$WEB_PORT/ >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
trap trap_ctrlc 2
|
||||||
|
echo "Toaster is now running. You can stop it with Ctrl-C"
|
||||||
|
while [ $RUNNING -gt 0 ]; do
|
||||||
|
python $BBBASEDIR/lib/toaster/manage.py runbuilds 2>&1 | tee -a "$BUILDDIR/toaster.log"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
cleanup
|
||||||
|
echo "**** Exit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if ! verify_prereq; then
|
||||||
|
echo "Error: Could not verify that the needed dependencies are installed. Please use virtualenv and pip to install dependencies listed in toaster-requirements.txt" 1>&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
verify_prereq || return 1
|
|
||||||
|
|
||||||
# We make sure we're running in the current shell and in a good environment
|
# We make sure we're running in the current shell and in a good environment
|
||||||
if [ -z "$BUILDDIR" ] || ! which bitbake >/dev/null 2>&1 ; then
|
if [ -z "$BUILDDIR" ] || ! which bitbake >/dev/null 2>&1 ; then
|
||||||
@@ -209,59 +315,84 @@ if [ -z "$BUILDDIR" ] || ! which bitbake >/dev/null 2>&1 ; then
|
|||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# this defines the dir toaster will use for
|
|
||||||
# 1) clones of layers (in _toaster_clones )
|
|
||||||
# 2) the build dir (in build)
|
|
||||||
# 3) the sqlite db if that is being used.
|
|
||||||
# 4) pid's we need to clean up on exit/shutdown
|
|
||||||
# note: for future. in order to make this an arbitrary directory, we need to
|
|
||||||
# make sure that the toaster.sqlite file doesn't default to `pwd`
|
|
||||||
# like it currently does.
|
|
||||||
export TOASTER_DIR=`dirname $BUILDDIR`
|
|
||||||
|
|
||||||
# Determine the action. If specified by arguments, fine, if not, toggle it
|
# Determine the action. If specified by arguments, fine, if not, toggle it
|
||||||
if [ "$CMD" = "start" ] ; then
|
if [ "$1" = 'start' ] || [ "$1" = 'stop' ]; then
|
||||||
if [ -n "$BBSERVER" ]; then
|
CMD="$1"
|
||||||
echo " Toaster is already running. Exiting..."
|
else
|
||||||
return 1
|
if [ -z "$BBSERVER" ]; then
|
||||||
fi
|
CMD="start"
|
||||||
elif [ "$CMD" = "" ]; then
|
else
|
||||||
echo "No command specified"
|
CMD="stop"
|
||||||
echo "$HELP"
|
fi
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "The system will $CMD."
|
echo "The system will $CMD."
|
||||||
|
|
||||||
|
# Make sure it's safe to run by checking bitbake lock
|
||||||
|
|
||||||
|
lock=1
|
||||||
|
if [ -e $BUILDDIR/bitbake.lock ]; then
|
||||||
|
python -c "import fcntl; fcntl.flock(open(\"$BUILDDIR/bitbake.lock\"), fcntl.LOCK_EX|fcntl.LOCK_NB)" 2>/dev/null || lock=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${CMD} = 'start' ] && [ $lock -eq 0 ]; then
|
||||||
|
echo "Error: bitbake lock state error. File locks show that the system is on." 1>&2
|
||||||
|
echo "Please wait for the current build to finish, stop and then start the system again." 1>&2
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${CMD} = 'start' ] && [ -e $BUILDDIR/.toastermain.pid ] && kill -0 `cat $BUILDDIR/.toastermain.pid`; then
|
||||||
|
echo "Warning: bitbake appears to be dead, but the Toaster web server is running. Something fishy is going on." 1>&2
|
||||||
|
echo "Cleaning up the web server to start from a clean slate."
|
||||||
|
webserverKillAll
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Execute the commands
|
# Execute the commands
|
||||||
|
|
||||||
case $CMD in
|
case $CMD in
|
||||||
start )
|
start )
|
||||||
# check if addr:port is not in use
|
start_success=1
|
||||||
if [ "$CMD" == 'start' ]; then
|
addtoConfiguration toaster.conf "INHERIT+=\"toaster buildhistory\"" $TOASTER_BRBE
|
||||||
if [ $WEBSERVER -gt 0 ]; then
|
|
||||||
$MANAGE checksocket "$ADDR_PORT" || return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create configuration file
|
|
||||||
conf=${BUILDDIR}/conf/local.conf
|
|
||||||
line='INHERIT+="toaster buildhistory"'
|
|
||||||
grep -q "$line" $conf || echo $line >> $conf
|
|
||||||
|
|
||||||
if [ $WEBSERVER -gt 0 ] && ! webserverStartAll; then
|
if [ $WEBSERVER -gt 0 ] && ! webserverStartAll; then
|
||||||
echo "Failed ${CMD}."
|
echo "Failed ${CMD}."
|
||||||
return 4
|
return 4
|
||||||
fi
|
fi
|
||||||
export BITBAKE_UI='toasterui'
|
unset BBSERVER
|
||||||
$MANAGE runbuilds & echo $! >${BUILDDIR}/.runbuilds.pid
|
PREREAD=""
|
||||||
# set fail safe stop system on terminal exit
|
if [ -e ${BUILDDIR}/conf/toaster-pre.conf ]; then
|
||||||
|
rm ${BUILDDIR}/conf/toaster-pre.conf
|
||||||
|
fi
|
||||||
|
bitbake $PREREAD --postread conf/toaster.conf --server-only -t xmlrpc -B 0.0.0.0:0
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
start_success=0
|
||||||
|
echo "Bitbake server start failed"
|
||||||
|
else
|
||||||
|
export BBSERVER=0.0.0.0:-1
|
||||||
|
if [ $NOTOASTERUI -eq 0 ]; then # we start the TOASTERUI only if not inhibited
|
||||||
|
bitbake --observe-only -u toasterui >>${BUILDDIR}/toaster_ui.log 2>&1 & echo $! >${BUILDDIR}/.toasterui.pid
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ $start_success -eq 1 ]; then
|
||||||
|
# set fail safe stop system on terminal exit
|
||||||
|
trap stop_system SIGHUP
|
||||||
|
echo "Successful ${CMD}."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# failed start, do stop
|
||||||
|
stop_system
|
||||||
|
echo "Failed ${CMD}."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# stop system on terminal exit
|
||||||
|
set -o monitor
|
||||||
trap stop_system SIGHUP
|
trap stop_system SIGHUP
|
||||||
echo "Successful ${CMD}."
|
#trap notify_chldexit SIGCHLD
|
||||||
return 0
|
|
||||||
;;
|
;;
|
||||||
stop )
|
stop )
|
||||||
stop_system
|
stop_system
|
||||||
echo "Successful ${CMD}."
|
echo "Successful ${CMD}."
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# ex:ts=4:sw=4:sts=4:et
|
# ex:ts=4:sw=4:sts=4:et
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
#
|
#
|
||||||
@@ -21,106 +21,154 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
This command takes a filename as a single parameter. The filename is read
|
|
||||||
as a build eventlog, and the ToasterUI is used to process events in the file
|
|
||||||
and log data in the database
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
# This command takes a filename as a single parameter. The filename is read
|
||||||
|
# as a build eventlog, and the ToasterUI is used to process events in the file
|
||||||
|
# and log data in the database
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys, logging
|
||||||
import json
|
|
||||||
import pickle
|
|
||||||
import codecs
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
# mangle syspath to allow easy import of modules
|
# mangle syspath to allow easy import of modules
|
||||||
from os.path import join, dirname, abspath
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
sys.path.insert(0, join(dirname(dirname(abspath(__file__))), 'lib'))
|
'lib'))
|
||||||
|
|
||||||
|
|
||||||
import bb.cooker
|
import bb.cooker
|
||||||
from bb.ui import toasterui
|
from bb.ui import toasterui
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
class EventPlayer:
|
import json, pickle
|
||||||
"""Emulate a connection to a bitbake server."""
|
|
||||||
|
|
||||||
def __init__(self, eventfile, variables):
|
|
||||||
self.eventfile = eventfile
|
|
||||||
self.variables = variables
|
|
||||||
self.eventmask = []
|
|
||||||
|
|
||||||
def waitEvent(self, _timeout):
|
class FileReadEventsServerConnection():
|
||||||
"""Read event from the file."""
|
""" Emulates a connection to a bitbake server that feeds
|
||||||
line = self.eventfile.readline().strip()
|
events coming actually read from a saved log file.
|
||||||
if not line:
|
"""
|
||||||
return
|
|
||||||
try:
|
|
||||||
event_str = json.loads(line)['vars'].encode('utf-8')
|
|
||||||
event = pickle.loads(codecs.decode(event_str, 'base64'))
|
|
||||||
event_name = "%s.%s" % (event.__module__, event.__class__.__name__)
|
|
||||||
if event_name not in self.eventmask:
|
|
||||||
return
|
|
||||||
return event
|
|
||||||
except ValueError as err:
|
|
||||||
print("Failed loading ", line)
|
|
||||||
raise err
|
|
||||||
|
|
||||||
def runCommand(self, command_line):
|
class MockConnection():
|
||||||
"""Emulate running a command on the server."""
|
""" fill-in for the proxy to the server. we just return generic data
|
||||||
name = command_line[0]
|
|
||||||
|
|
||||||
if name == "getVariable":
|
|
||||||
var_name = command_line[1]
|
|
||||||
variable = self.variables.get(var_name)
|
|
||||||
if variable:
|
|
||||||
return variable['v'], None
|
|
||||||
return None, "Missing variable %s" % var_name
|
|
||||||
|
|
||||||
elif name == "getAllKeysWithFlags":
|
|
||||||
dump = {}
|
|
||||||
flaglist = command_line[1]
|
|
||||||
for key, val in self.variables.items():
|
|
||||||
try:
|
|
||||||
if not key.startswith("__"):
|
|
||||||
dump[key] = {
|
|
||||||
'v': val['v'],
|
|
||||||
'history' : val['history'],
|
|
||||||
}
|
|
||||||
for flag in flaglist:
|
|
||||||
dump[key][flag] = val[flag]
|
|
||||||
except Exception as err:
|
|
||||||
print(err)
|
|
||||||
return (dump, None)
|
|
||||||
|
|
||||||
elif name == 'setEventMask':
|
|
||||||
self.eventmask = command_line[-1]
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise Exception("Command %s not implemented" % command_line[0])
|
|
||||||
|
|
||||||
def getEventHandle(self):
|
|
||||||
"""
|
"""
|
||||||
This method is called by toasterui.
|
def __init__(self, sc):
|
||||||
The return value is passed to self.runCommand but not used there.
|
self._sc = sc
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def main(argv):
|
def runCommand(self, commandArray):
|
||||||
with open(argv[-1]) as eventfile:
|
""" emulates running a command on the server; only read-only commands are accepted """
|
||||||
# load variables from the first line
|
command_name = commandArray[0]
|
||||||
variables = json.loads(eventfile.readline().strip())['allvariables']
|
|
||||||
|
|
||||||
params = namedtuple('ConfigParams', ['observe_only'])(True)
|
if command_name == "getVariable":
|
||||||
player = EventPlayer(eventfile, variables)
|
if commandArray[1] in self._sc._variables:
|
||||||
|
return (self._sc._variables[commandArray[1]]['v'], None)
|
||||||
|
return (None, "Missing variable")
|
||||||
|
|
||||||
|
elif command_name == "getAllKeysWithFlags":
|
||||||
|
dump = {}
|
||||||
|
flaglist = commandArray[1]
|
||||||
|
for k in self._sc._variables.keys():
|
||||||
|
try:
|
||||||
|
if not k.startswith("__"):
|
||||||
|
v = self._sc._variables[k]['v']
|
||||||
|
dump[k] = {
|
||||||
|
'v' : v ,
|
||||||
|
'history' : self._sc._variables[k]['history'],
|
||||||
|
}
|
||||||
|
for d in flaglist:
|
||||||
|
dump[k][d] = self._sc._variables[k][d]
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return (dump, None)
|
||||||
|
else:
|
||||||
|
raise Exception("Command %s not implemented" % commandArray[0])
|
||||||
|
|
||||||
|
def terminateServer(self):
|
||||||
|
""" do not do anything """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class EventReader():
|
||||||
|
def __init__(self, sc):
|
||||||
|
self._sc = sc
|
||||||
|
self.firstraise = 0
|
||||||
|
|
||||||
|
def _create_event(self, line):
|
||||||
|
def _import_class(name):
|
||||||
|
assert len(name) > 0
|
||||||
|
assert "." in name, name
|
||||||
|
|
||||||
|
components = name.strip().split(".")
|
||||||
|
modulename = ".".join(components[:-1])
|
||||||
|
moduleklass = components[-1]
|
||||||
|
|
||||||
|
module = __import__(modulename, fromlist=[str(moduleklass)])
|
||||||
|
return getattr(module, moduleklass)
|
||||||
|
|
||||||
|
# we build a toaster event out of current event log line
|
||||||
|
try:
|
||||||
|
event_data = json.loads(line.strip())
|
||||||
|
event_class = _import_class(event_data['class'])
|
||||||
|
event_object = pickle.loads(json.loads(event_data['vars']))
|
||||||
|
except ValueError as e:
|
||||||
|
print("Failed loading ", line)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if not isinstance(event_object, event_class):
|
||||||
|
raise Exception("Error loading objects %s class %s ", event_object, event_class)
|
||||||
|
|
||||||
|
return event_object
|
||||||
|
|
||||||
|
def waitEvent(self, timeout):
|
||||||
|
|
||||||
|
nextline = self._sc._eventfile.readline()
|
||||||
|
if len(nextline) == 0:
|
||||||
|
# the build data ended, while toasterui still waits for events.
|
||||||
|
# this happens when the server was abruptly stopped, so we simulate this
|
||||||
|
self.firstraise += 1
|
||||||
|
if self.firstraise == 1:
|
||||||
|
raise KeyboardInterrupt()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
self._sc.lineno += 1
|
||||||
|
return self._create_event(nextline)
|
||||||
|
|
||||||
|
|
||||||
|
def _readVariables(self, variableline):
|
||||||
|
self._variables = json.loads(variableline.strip())['allvariables']
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, file_name):
|
||||||
|
self.connection = FileReadEventsServerConnection.MockConnection(self)
|
||||||
|
self._eventfile = open(file_name, "r")
|
||||||
|
|
||||||
|
# we expect to have the variable dump at the start of the file
|
||||||
|
self.lineno = 1
|
||||||
|
self._readVariables(self._eventfile.readline())
|
||||||
|
|
||||||
|
self.events = FileReadEventsServerConnection.EventReader(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MockConfigParameters():
|
||||||
|
""" stand-in for cookerdata.ConfigParameters; as we don't really config a cooker, this
|
||||||
|
serves just to supply needed interfaces for the toaster ui to work """
|
||||||
|
def __init__(self):
|
||||||
|
self.observe_only = True # we can only read files
|
||||||
|
|
||||||
return toasterui.main(player, player, params)
|
|
||||||
|
|
||||||
# run toaster ui on our mock bitbake class
|
# run toaster ui on our mock bitbake class
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: %s <event file>" % os.path.basename(sys.argv[0]))
|
print("Usage: %s event.log " % sys.argv[0])
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sys.exit(main(sys.argv))
|
file_name = sys.argv[-1]
|
||||||
|
mock_connection = FileReadEventsServerConnection(file_name)
|
||||||
|
configParams = MockConfigParameters()
|
||||||
|
|
||||||
|
# run the main program and set exit code to the returned value
|
||||||
|
sys.exit(toasterui.main(mock_connection.connection, mock_connection.events, configParams))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# ex:ts=4:sw=4:sts=4:et
|
# ex:ts=4:sw=4:sts=4:et
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
#
|
#
|
||||||
@@ -29,14 +29,14 @@ import warnings
|
|||||||
sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), '../lib'))
|
sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), '../lib'))
|
||||||
from bb.cache import CoreRecipeInfo
|
from bb.cache import CoreRecipeInfo
|
||||||
|
|
||||||
import pickle as pickle
|
import cPickle as pickle
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
"""
|
"""
|
||||||
Get the mapping for the target recipe.
|
Get the mapping for the target recipe.
|
||||||
"""
|
"""
|
||||||
if len(argv) != 1:
|
if len(argv) != 1:
|
||||||
print("Error, need one argument!", file=sys.stderr)
|
print >>sys.stderr, "Error, need one argument!"
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
cachefile = argv[0]
|
cachefile = argv[0]
|
||||||
@@ -56,7 +56,7 @@ def main(argv=None):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# 1.0 is the default version for a no PV recipe.
|
# 1.0 is the default version for a no PV recipe.
|
||||||
if "pv" in val.__dict__:
|
if val.__dict__.has_key("pv"):
|
||||||
pv = val.pv
|
pv = val.pv
|
||||||
else:
|
else:
|
||||||
pv = "1.0"
|
pv = "1.0"
|
||||||
|
|||||||
@@ -42,9 +42,9 @@
|
|||||||
<literallayout class='monospaced'>
|
<literallayout class='monospaced'>
|
||||||
$ grep processor /proc/cpuinfo
|
$ grep processor /proc/cpuinfo
|
||||||
</literallayout>
|
</literallayout>
|
||||||
This command returns the number of processors, which takes into
|
This command returns the number of processors, which takes into
|
||||||
account hyper-threading.
|
account hyper-threading.
|
||||||
Thus, a quad-core build host with hyper-threading most likely
|
Thus, a quad-core build host with hyper-threading most likely
|
||||||
shows eight processors, which is the value you would then assign to
|
shows eight processors, which is the value you would then assign to
|
||||||
<filename>BB_NUMBER_THREADS</filename>.
|
<filename>BB_NUMBER_THREADS</filename>.
|
||||||
</para>
|
</para>
|
||||||
@@ -116,35 +116,16 @@
|
|||||||
Prior to parsing configuration files, Bitbake looks
|
Prior to parsing configuration files, Bitbake looks
|
||||||
at certain variables, including:
|
at certain variables, including:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>
|
<listitem><para><link linkend='var-BB_ENV_WHITELIST'><filename>BB_ENV_WHITELIST</filename></link></para></listitem>
|
||||||
<link linkend='var-BB_ENV_WHITELIST'><filename>BB_ENV_WHITELIST</filename></link>
|
<listitem><para><link linkend='var-BB_PRESERVE_ENV'><filename>BB_PRESERVE_ENV</filename></link></para></listitem>
|
||||||
</para></listitem>
|
<listitem><para><link linkend='var-BB_ENV_EXTRAWHITE'><filename>BB_ENV_EXTRAWHITE</filename></link></para></listitem>
|
||||||
<listitem><para>
|
|
||||||
<link linkend='var-BB_ENV_EXTRAWHITE'><filename>BB_ENV_EXTRAWHITE</filename></link>
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<link linkend='var-BB_PRESERVE_ENV'><filename>BB_PRESERVE_ENV</filename></link>
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<link linkend='var-BB_ORIGENV'><filename>BB_ORIGENV</filename></link>
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
<link linkend='var-BITBAKE_UI'><filename>BITBAKE_UI</filename></link>
|
<link linkend='var-BITBAKE_UI'><filename>BITBAKE_UI</filename></link>
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
The first four variables in this list relate to how BitBake treats shell
|
You can find information on how to pass environment variables into the BitBake
|
||||||
environment variables during task execution.
|
execution environment in the
|
||||||
By default, BitBake cleans the environment variables and provides tight
|
"<link linkend='passing-information-into-the-build-task-environment'>Passing Information Into the Build Task Environment</link>" section.
|
||||||
control over the shell execution environment.
|
|
||||||
However, through the use of these first four variables, you can
|
|
||||||
apply your control regarding the
|
|
||||||
environment variables allowed to be used by BitBake in the shell
|
|
||||||
during execution of tasks.
|
|
||||||
See the
|
|
||||||
"<link linkend='passing-information-into-the-build-task-environment'>Passing Information Into the Build Task Environment</link>"
|
|
||||||
section and the information about these variables in the
|
|
||||||
variable glossary for more information on how they work and
|
|
||||||
on how to use them.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@@ -596,7 +577,7 @@
|
|||||||
"<link linkend='checksums'>Checksums (Signatures)</link>"
|
"<link linkend='checksums'>Checksums (Signatures)</link>"
|
||||||
section for information).
|
section for information).
|
||||||
It is also possible to append extra metadata to the stamp using
|
It is also possible to append extra metadata to the stamp using
|
||||||
the <filename>[stamp-extra-info]</filename> task flag.
|
the "stamp-extra-info" task flag.
|
||||||
For example, OpenEmbedded uses this flag to make some tasks machine-specific.
|
For example, OpenEmbedded uses this flag to make some tasks machine-specific.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@@ -653,8 +634,7 @@
|
|||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
It is possible to have functions run before and after a task's main
|
It is possible to have functions run before and after a task's main
|
||||||
function.
|
function.
|
||||||
This is done using the <filename>[prefuncs]</filename>
|
This is done using the "prefuncs" and "postfuncs" flags of the task
|
||||||
and <filename>[postfuncs]</filename> flags of the task
|
|
||||||
that lists the functions to run.
|
that lists the functions to run.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
@@ -828,7 +808,7 @@
|
|||||||
itself.
|
itself.
|
||||||
The simplest parameter to pass is "none", which causes a
|
The simplest parameter to pass is "none", which causes a
|
||||||
set of signature information to be written out into
|
set of signature information to be written out into
|
||||||
<filename>STAMPS_DIR</filename>
|
<filename>STAMP_DIR</filename>
|
||||||
corresponding to the targets specified.
|
corresponding to the targets specified.
|
||||||
The other currently available parameter is "printdiff",
|
The other currently available parameter is "printdiff",
|
||||||
which causes BitBake to try to establish the closest
|
which causes BitBake to try to establish the closest
|
||||||
@@ -916,7 +896,7 @@
|
|||||||
<para>
|
<para>
|
||||||
Finally, after all the setscene tasks have executed, BitBake calls the
|
Finally, after all the setscene tasks have executed, BitBake calls the
|
||||||
function listed in
|
function listed in
|
||||||
<link linkend='var-BB_SETSCENE_VERIFY_FUNCTION2'><filename>BB_SETSCENE_VERIFY_FUNCTION2</filename></link>
|
<link linkend='var-BB_SETSCENE_VERIFY_FUNCTION'><filename>BB_SETSCENE_VERIFY_FUNCTION</filename></link>
|
||||||
with the list of tasks BitBake thinks has been "covered".
|
with the list of tasks BitBake thinks has been "covered".
|
||||||
The metadata can then ensure that this list is correct and can
|
The metadata can then ensure that this list is correct and can
|
||||||
inform BitBake that it wants specific tasks to be run regardless
|
inform BitBake that it wants specific tasks to be run regardless
|
||||||
|
|||||||
@@ -670,8 +670,8 @@
|
|||||||
The <filename>module</filename> and <filename>vob</filename>
|
The <filename>module</filename> and <filename>vob</filename>
|
||||||
options are combined to create the <filename>load</filename> rule in
|
options are combined to create the <filename>load</filename> rule in
|
||||||
the view config spec.
|
the view config spec.
|
||||||
As an example, consider the <filename>vob</filename> and
|
As an example, consider the <filename>vob</filename> and
|
||||||
<filename>module</filename> values from the
|
<filename>module</filename> values from the
|
||||||
<filename>SRC_URI</filename> statement at the start of this section.
|
<filename>SRC_URI</filename> statement at the start of this section.
|
||||||
Combining those values results in the following:
|
Combining those values results in the following:
|
||||||
<literallayout class='monospaced'>
|
<literallayout class='monospaced'>
|
||||||
@@ -716,68 +716,6 @@
|
|||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id='perforce-fetcher'>
|
|
||||||
<title>Perforce Fetcher (<filename>p4://</filename>)</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
This fetcher submodule fetches code from the
|
|
||||||
<ulink url='https://www.perforce.com/'>Perforce</ulink>
|
|
||||||
source control system.
|
|
||||||
The executable used is specified by
|
|
||||||
<filename>FETCHCMD_p4</filename>, which defaults
|
|
||||||
to "p4".
|
|
||||||
The fetcher's temporary working directory is set by
|
|
||||||
<link linkend='var-P4DIR'><filename>P4DIR</filename></link>,
|
|
||||||
which defaults to "DL_DIR/p4".
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
To use this fetcher, make sure your recipe has proper
|
|
||||||
<link linkend='var-SRC_URI'><filename>SRC_URI</filename></link>,
|
|
||||||
<link linkend='var-SRCREV'><filename>SRCREV</filename></link>, and
|
|
||||||
<link linkend='var-PV'><filename>PV</filename></link> values.
|
|
||||||
The p4 executable is able to use the config file defined by your
|
|
||||||
system's <filename>P4CONFIG</filename> environment variable in
|
|
||||||
order to define the Perforce server URL and port, username, and
|
|
||||||
password if you do not wish to keep those values in a recipe
|
|
||||||
itself.
|
|
||||||
If you choose not to use <filename>P4CONFIG</filename>,
|
|
||||||
or to explicitly set variables that <filename>P4CONFIG</filename>
|
|
||||||
can contain, you can specify the <filename>P4PORT</filename> value,
|
|
||||||
which is the server's URL and port number, and you can
|
|
||||||
specify a username and password directly in your recipe within
|
|
||||||
<filename>SRC_URI</filename>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Here is an example that relies on <filename>P4CONFIG</filename>
|
|
||||||
to specify the server URL and port, username, and password, and
|
|
||||||
fetches the Head Revision:
|
|
||||||
<literallayout class='monospaced'>
|
|
||||||
SRC_URI = "p4://example-depot/main/source/..."
|
|
||||||
SRCREV = "${AUTOREV}"
|
|
||||||
PV = "p4-${SRCPV}"
|
|
||||||
S = "${WORKDIR}/p4"
|
|
||||||
</literallayout>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Here is an example that specifies the server URL and port,
|
|
||||||
username, and password, and fetches a Revision based on a Label:
|
|
||||||
<literallayout class='monospaced'>
|
|
||||||
P4PORT = "tcp:p4server.example.net:1666"
|
|
||||||
SRC_URI = "p4://user:passwd@example-depot/main/source/..."
|
|
||||||
SRCREV = "release-1.0"
|
|
||||||
PV = "p4-${SRCPV}"
|
|
||||||
S = "${WORKDIR}/p4"
|
|
||||||
</literallayout>
|
|
||||||
<note>
|
|
||||||
You should always set <filename>S</filename>
|
|
||||||
to <filename>"${WORKDIR}/p4"</filename> in your recipe.
|
|
||||||
</note>
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id='other-fetchers'>
|
<section id='other-fetchers'>
|
||||||
<title>Other Fetchers</title>
|
<title>Other Fetchers</title>
|
||||||
|
|
||||||
@@ -787,6 +725,9 @@
|
|||||||
<listitem><para>
|
<listitem><para>
|
||||||
Bazaar (<filename>bzr://</filename>)
|
Bazaar (<filename>bzr://</filename>)
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
Perforce (<filename>p4://</filename>)
|
||||||
|
</para></listitem>
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
Trees using Git Annex (<filename>gitannex://</filename>)
|
Trees using Git Annex (<filename>gitannex://</filename>)
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
-rw-rw-r--. 1 wmat wmat 849 Nov 26 04:55 HEADER
|
-rw-rw-r--. 1 wmat wmat 849 Nov 26 04:55 HEADER
|
||||||
drwxrwxr-x. 5 wmat wmat 4096 Jan 31 13:44 lib
|
drwxrwxr-x. 5 wmat wmat 4096 Jan 31 13:44 lib
|
||||||
-rw-rw-r--. 1 wmat wmat 195 Nov 26 04:55 MANIFEST.in
|
-rw-rw-r--. 1 wmat wmat 195 Nov 26 04:55 MANIFEST.in
|
||||||
|
-rwxrwxr-x. 1 wmat wmat 3195 Jan 31 11:57 setup.py
|
||||||
-rw-rw-r--. 1 wmat wmat 2887 Nov 26 04:55 TODO
|
-rw-rw-r--. 1 wmat wmat 2887 Nov 26 04:55 TODO
|
||||||
</literallayout>
|
</literallayout>
|
||||||
</para>
|
</para>
|
||||||
@@ -399,7 +400,7 @@ ERROR: Unable to parse base: ParseError in configuration INHERITs: Could not inh
|
|||||||
<link linkend='var-BBFILES'>BBFILES</link> += "${LAYERDIR}/*.bb"
|
<link linkend='var-BBFILES'>BBFILES</link> += "${LAYERDIR}/*.bb"
|
||||||
|
|
||||||
<link linkend='var-BBFILE_COLLECTIONS'>BBFILE_COLLECTIONS</link> += "mylayer"
|
<link linkend='var-BBFILE_COLLECTIONS'>BBFILE_COLLECTIONS</link> += "mylayer"
|
||||||
<link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR_RE}/"
|
<link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR}/"
|
||||||
</literallayout>
|
</literallayout>
|
||||||
For information on these variables, click the links
|
For information on these variables, click the links
|
||||||
to go to the definitions in the glossary.</para>
|
to go to the definitions in the glossary.</para>
|
||||||
|
|||||||
@@ -471,7 +471,7 @@
|
|||||||
Following is the usage and syntax for BitBake:
|
Following is the usage and syntax for BitBake:
|
||||||
<literallayout class='monospaced'>
|
<literallayout class='monospaced'>
|
||||||
$ bitbake -h
|
$ bitbake -h
|
||||||
Usage: bitbake [options] [recipename/target recipe:do_task ...]
|
Usage: bitbake [options] [recipename/target ...]
|
||||||
|
|
||||||
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
|
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
|
||||||
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
|
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
|
||||||
@@ -529,11 +529,9 @@
|
|||||||
-l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS
|
-l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS
|
||||||
Show debug logging for the specified logging domains
|
Show debug logging for the specified logging domains
|
||||||
-P, --profile Profile the command and save reports.
|
-P, --profile Profile the command and save reports.
|
||||||
-u UI, --ui=UI The user interface to use (depexp, goggle, hob, knotty
|
-u UI, --ui=UI The user interface to use (e.g. knotty, hob, depexp).
|
||||||
or ncurses - default knotty).
|
|
||||||
-t SERVERTYPE, --servertype=SERVERTYPE
|
-t SERVERTYPE, --servertype=SERVERTYPE
|
||||||
Choose which server type to use (process or xmlrpc -
|
Choose which server to use, process or xmlrpc.
|
||||||
default process).
|
|
||||||
--token=XMLRPCTOKEN Specify the connection token to be used when
|
--token=XMLRPCTOKEN Specify the connection token to be used when
|
||||||
connecting to a remote server.
|
connecting to a remote server.
|
||||||
--revisions-changed Set the exit code depending on whether upstream
|
--revisions-changed Set the exit code depending on whether upstream
|
||||||
@@ -548,10 +546,6 @@
|
|||||||
-m, --kill-server Terminate the remote server.
|
-m, --kill-server Terminate the remote server.
|
||||||
--observe-only Connect to a server as an observing-only client.
|
--observe-only Connect to a server as an observing-only client.
|
||||||
--status-only Check the status of the remote bitbake server.
|
--status-only Check the status of the remote bitbake server.
|
||||||
-w WRITEEVENTLOG, --write-log=WRITEEVENTLOG
|
|
||||||
Writes the event log of the build to a bitbake event
|
|
||||||
json file. Use '' (empty string) to assign the name
|
|
||||||
automatically.
|
|
||||||
</literallayout>
|
</literallayout>
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
@@ -634,25 +628,6 @@
|
|||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id='executing-a-list-of-task-and-recipe-combinations'>
|
|
||||||
<title>Executing a List of Task and Recipe Combinations</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The BitBake command line supports specifying different
|
|
||||||
tasks for individual targets when you specify multiple
|
|
||||||
targets.
|
|
||||||
For example, suppose you had two targets (or recipes)
|
|
||||||
<filename>myfirstrecipe</filename> and
|
|
||||||
<filename>mysecondrecipe</filename> and you needed
|
|
||||||
BitBake to run <filename>taskA</filename> for the first
|
|
||||||
recipe and <filename>taskB</filename> for the second
|
|
||||||
recipe:
|
|
||||||
<literallayout class='monospaced'>
|
|
||||||
$ bitbake myfirstrecipe:do_taskA mysecondrecipe:do_taskB
|
|
||||||
</literallayout>
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id='generating-dependency-graphs'>
|
<section id='generating-dependency-graphs'>
|
||||||
<title>Generating Dependency Graphs</title>
|
<title>Generating Dependency Graphs</title>
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@
|
|||||||
<link linkend='var-MIRRORS'>M</link>
|
<link linkend='var-MIRRORS'>M</link>
|
||||||
<!-- <link linkend='var-glossary-n'>N</link> -->
|
<!-- <link linkend='var-glossary-n'>N</link> -->
|
||||||
<link linkend='var-OVERRIDES'>O</link>
|
<link linkend='var-OVERRIDES'>O</link>
|
||||||
<link linkend='var-P4DIR'>P</link>
|
<link linkend='var-PACKAGES'>P</link>
|
||||||
<!-- <link linkend='var-QMAKE_PROFILES'>Q</link> -->
|
<!-- <link linkend='var-QMAKE_PROFILES'>Q</link> -->
|
||||||
<link linkend='var-RDEPENDS'>R</link>
|
<link linkend='var-RDEPENDS'>R</link>
|
||||||
<link linkend='var-SECTION'>S</link>
|
<link linkend='var-SECTION'>S</link>
|
||||||
@@ -716,7 +716,7 @@
|
|||||||
</glossdef>
|
</glossdef>
|
||||||
</glossentry>
|
</glossentry>
|
||||||
|
|
||||||
<glossentry id='var-BB_SETSCENE_VERIFY_FUNCTION2'><glossterm>BB_SETSCENE_VERIFY_FUNCTION2</glossterm>
|
<glossentry id='var-BB_SETSCENE_VERIFY_FUNCTION'><glossterm>BB_SETSCENE_VERIFY_FUNCTION</glossterm>
|
||||||
<glossdef>
|
<glossdef>
|
||||||
<para>
|
<para>
|
||||||
Specifies a function to call that verifies the list of
|
Specifies a function to call that verifies the list of
|
||||||
@@ -986,29 +986,6 @@
|
|||||||
BBCLASSEXTEND =+ "native nativesdk"
|
BBCLASSEXTEND =+ "native nativesdk"
|
||||||
BBCLASSEXTEND =+ "multilib:<replaceable>multilib_name</replaceable>"
|
BBCLASSEXTEND =+ "multilib:<replaceable>multilib_name</replaceable>"
|
||||||
</literallayout>
|
</literallayout>
|
||||||
<note>
|
|
||||||
<para>
|
|
||||||
Internally, the <filename>BBCLASSEXTEND</filename>
|
|
||||||
mechanism generates recipe variants by rewriting
|
|
||||||
variable values and applying overrides such as
|
|
||||||
<filename>_class-native</filename>.
|
|
||||||
For example, to generate a native version of a recipe,
|
|
||||||
a
|
|
||||||
<link linkend='var-DEPENDS'><filename>DEPENDS</filename></link>
|
|
||||||
on "foo" is rewritten to a <filename>DEPENDS</filename>
|
|
||||||
on "foo-native".
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Even when using <filename>BBCLASSEXTEND</filename>, the
|
|
||||||
recipe is only parsed once.
|
|
||||||
Parsing once adds some limitations.
|
|
||||||
For example, it is not possible to
|
|
||||||
include a different file depending on the variant,
|
|
||||||
since <filename>include</filename> statements are
|
|
||||||
processed when the recipe is parsed.
|
|
||||||
</para>
|
|
||||||
</note>
|
|
||||||
</para>
|
</para>
|
||||||
</glossdef>
|
</glossdef>
|
||||||
</glossentry>
|
</glossentry>
|
||||||
@@ -1017,7 +994,7 @@
|
|||||||
<glossdef>
|
<glossdef>
|
||||||
<para>
|
<para>
|
||||||
Sets the BitBake debug output level to a specific value
|
Sets the BitBake debug output level to a specific value
|
||||||
as incremented by the <filename>-D</filename> command line
|
as incremented by the <filename>-d</filename> command line
|
||||||
option.
|
option.
|
||||||
<note>
|
<note>
|
||||||
You must set this variable in the external environment
|
You must set this variable in the external environment
|
||||||
@@ -1165,14 +1142,14 @@
|
|||||||
to "hide" these <filename>.bb</filename> and
|
to "hide" these <filename>.bb</filename> and
|
||||||
<filename>.bbappend</filename> files.
|
<filename>.bbappend</filename> files.
|
||||||
BitBake ignores any recipe or recipe append files that
|
BitBake ignores any recipe or recipe append files that
|
||||||
match any of the expressions.
|
match the expression.
|
||||||
It is as if BitBake does not see them at all.
|
It is as if BitBake does not see them at all.
|
||||||
Consequently, matching files are not parsed or otherwise
|
Consequently, matching files are not parsed or otherwise
|
||||||
used by BitBake.</para>
|
used by BitBake.</para>
|
||||||
<para>
|
<para>
|
||||||
The values you provide are passed to Python's regular
|
The value you provide is passed to Python's regular
|
||||||
expression compiler.
|
expression compiler.
|
||||||
The expressions are compared against the full paths to
|
The expression is compared against the full paths to
|
||||||
the files.
|
the files.
|
||||||
For complete syntax information, see Python's
|
For complete syntax information, see Python's
|
||||||
documentation at
|
documentation at
|
||||||
@@ -1188,16 +1165,18 @@
|
|||||||
BBMASK = "meta-ti/recipes-misc/"
|
BBMASK = "meta-ti/recipes-misc/"
|
||||||
</literallayout>
|
</literallayout>
|
||||||
If you want to mask out multiple directories or recipes,
|
If you want to mask out multiple directories or recipes,
|
||||||
you can specify multiple regular expression fragments.
|
use the vertical bar to separate the regular expression
|
||||||
|
fragments.
|
||||||
This next example masks out multiple directories and
|
This next example masks out multiple directories and
|
||||||
individual recipes:
|
individual recipes:
|
||||||
<literallayout class='monospaced'>
|
<literallayout class='monospaced'>
|
||||||
BBMASK += "/meta-ti/recipes-misc/ meta-ti/recipes-ti/packagegroup/"
|
BBMASK = "meta-ti/recipes-misc/|meta-ti/recipes-ti/packagegroup/"
|
||||||
BBMASK += "/meta-oe/recipes-support/"
|
BBMASK .= "|.*meta-oe/recipes-support/"
|
||||||
BBMASK += "/meta-foo/.*/openldap"
|
BBMASK .= "|.*openldap"
|
||||||
BBMASK += "opencv.*\.bbappend"
|
BBMASK .= "|.*opencv"
|
||||||
BBMASK += "lzma"
|
BBMASK .= "|.*lzma"
|
||||||
</literallayout>
|
</literallayout>
|
||||||
|
Notice how the vertical bar is used to append the fragments.
|
||||||
<note>
|
<note>
|
||||||
When specifying a directory name, use the trailing
|
When specifying a directory name, use the trailing
|
||||||
slash character to ensure you match just that directory
|
slash character to ensure you match just that directory
|
||||||
@@ -1659,17 +1638,6 @@
|
|||||||
</glossdef>
|
</glossdef>
|
||||||
</glossentry>
|
</glossentry>
|
||||||
|
|
||||||
<glossentry id='var-LAYERDIR_RE'><glossterm>LAYERDIR_RE</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>When used inside the <filename>layer.conf</filename> configuration
|
|
||||||
file, this variable provides the path of the current layer,
|
|
||||||
escaped for use in a regular expression
|
|
||||||
(<link linkend='var-BBFILE_PATTERN'><filename>BBFILE_PATTERN</filename></link>).
|
|
||||||
This variable is not available outside of <filename>layer.conf</filename>
|
|
||||||
and references are expanded immediately when parsing of the file completes.</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry id='var-LAYERVERSION'><glossterm>LAYERVERSION</glossterm>
|
<glossentry id='var-LAYERVERSION'><glossterm>LAYERVERSION</glossterm>
|
||||||
<glossdef>
|
<glossdef>
|
||||||
<para>Optionally specifies the version of a layer as a single number.
|
<para>Optionally specifies the version of a layer as a single number.
|
||||||
@@ -1771,15 +1739,6 @@
|
|||||||
|
|
||||||
<glossdiv id='var-glossary-p'><title>P</title>
|
<glossdiv id='var-glossary-p'><title>P</title>
|
||||||
|
|
||||||
<glossentry id='var-P4DIR'><glossterm>P4DIR</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>
|
|
||||||
The directory in which a local copy of a Perforce depot
|
|
||||||
is stored when it is fetched.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry id='var-PACKAGES'><glossterm>PACKAGES</glossterm>
|
<glossentry id='var-PACKAGES'><glossterm>PACKAGES</glossterm>
|
||||||
<glossdef>
|
<glossdef>
|
||||||
<para>The list of packages the recipe creates.
|
<para>The list of packages the recipe creates.
|
||||||
@@ -1976,27 +1935,6 @@
|
|||||||
The <filename>PROVIDES</filename> statement results in
|
The <filename>PROVIDES</filename> statement results in
|
||||||
the "libav" recipe also being known as "libpostproc".
|
the "libav" recipe also being known as "libpostproc".
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
In addition to providing recipes under alternate names,
|
|
||||||
the <filename>PROVIDES</filename> mechanism is also used
|
|
||||||
to implement virtual targets.
|
|
||||||
A virtual target is a name that corresponds to some
|
|
||||||
particular functionality (e.g. a Linux kernel).
|
|
||||||
Recipes that provide the functionality in question list the
|
|
||||||
virtual target in <filename>PROVIDES</filename>.
|
|
||||||
Recipes that depend on the functionality in question can
|
|
||||||
include the virtual target in
|
|
||||||
<link linkend='var-DEPENDS'><filename>DEPENDS</filename></link>
|
|
||||||
to leave the choice of provider open.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Conventionally, virtual targets have names on the form
|
|
||||||
"virtual/function" (e.g. "virtual/kernel").
|
|
||||||
The slash is simply part of the name and has no
|
|
||||||
syntactical significance.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
</glossdef>
|
||||||
</glossentry>
|
</glossentry>
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<copyright>
|
<copyright>
|
||||||
<year>2004-2016</year>
|
<year>2004-2015</year>
|
||||||
<holder>Richard Purdie</holder>
|
<holder>Richard Purdie</holder>
|
||||||
<holder>Chris Larson</holder>
|
<holder>Chris Larson</holder>
|
||||||
<holder>and Phil Blundell</holder>
|
<holder>and Phil Blundell</holder>
|
||||||
|
|||||||
@@ -23,17 +23,19 @@
|
|||||||
# Assign a file to __warn__ to get warnings about slow operations.
|
# Assign a file to __warn__ to get warnings about slow operations.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
import copy
|
import copy
|
||||||
import types
|
import types
|
||||||
ImmutableTypes = (
|
ImmutableTypes = (
|
||||||
|
types.NoneType,
|
||||||
bool,
|
bool,
|
||||||
complex,
|
complex,
|
||||||
float,
|
float,
|
||||||
int,
|
int,
|
||||||
|
long,
|
||||||
tuple,
|
tuple,
|
||||||
frozenset,
|
frozenset,
|
||||||
str
|
basestring
|
||||||
)
|
)
|
||||||
|
|
||||||
MUTABLE = "__mutable__"
|
MUTABLE = "__mutable__"
|
||||||
@@ -59,7 +61,7 @@ class COWDictMeta(COWMeta):
|
|||||||
__call__ = cow
|
__call__ = cow
|
||||||
|
|
||||||
def __setitem__(cls, key, value):
|
def __setitem__(cls, key, value):
|
||||||
if value is not None and not isinstance(value, ImmutableTypes):
|
if not isinstance(value, ImmutableTypes):
|
||||||
if not isinstance(value, COWMeta):
|
if not isinstance(value, COWMeta):
|
||||||
cls.__hasmutable__ = True
|
cls.__hasmutable__ = True
|
||||||
key += MUTABLE
|
key += MUTABLE
|
||||||
@@ -114,7 +116,7 @@ class COWDictMeta(COWMeta):
|
|||||||
cls.__setitem__(key, cls.__marker__)
|
cls.__setitem__(key, cls.__marker__)
|
||||||
|
|
||||||
def __revertitem__(cls, key):
|
def __revertitem__(cls, key):
|
||||||
if key not in cls.__dict__:
|
if not cls.__dict__.has_key(key):
|
||||||
key += MUTABLE
|
key += MUTABLE
|
||||||
delattr(cls, key)
|
delattr(cls, key)
|
||||||
|
|
||||||
@@ -181,7 +183,7 @@ class COWSetMeta(COWDictMeta):
|
|||||||
COWDictMeta.__delitem__(cls, repr(hash(value)))
|
COWDictMeta.__delitem__(cls, repr(hash(value)))
|
||||||
|
|
||||||
def __in__(cls, value):
|
def __in__(cls, value):
|
||||||
return repr(hash(value)) in COWDictMeta
|
return COWDictMeta.has_key(repr(hash(value)))
|
||||||
|
|
||||||
def iterkeys(cls):
|
def iterkeys(cls):
|
||||||
raise TypeError("sets don't have keys")
|
raise TypeError("sets don't have keys")
|
||||||
@@ -190,10 +192,12 @@ class COWSetMeta(COWDictMeta):
|
|||||||
raise TypeError("sets don't have 'items'")
|
raise TypeError("sets don't have 'items'")
|
||||||
|
|
||||||
# These are the actual classes you use!
|
# These are the actual classes you use!
|
||||||
class COWDictBase(object, metaclass = COWDictMeta):
|
class COWDictBase(object):
|
||||||
|
__metaclass__ = COWDictMeta
|
||||||
__count__ = 0
|
__count__ = 0
|
||||||
|
|
||||||
class COWSetBase(object, metaclass = COWSetMeta):
|
class COWSetBase(object):
|
||||||
|
__metaclass__ = COWSetMeta
|
||||||
__count__ = 0
|
__count__ = 0
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -213,11 +217,11 @@ if __name__ == "__main__":
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
for x in b.items():
|
for x in b.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -225,11 +229,11 @@ if __name__ == "__main__":
|
|||||||
b['a'] = 'c'
|
b['a'] = 'c'
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
for x in b.items():
|
for x in b.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -244,22 +248,22 @@ if __name__ == "__main__":
|
|||||||
a['set'].add("o2")
|
a['set'].add("o2")
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a['set'].values():
|
for x in a['set'].itervalues():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
for x in b['set'].values():
|
for x in b['set'].itervalues():
|
||||||
print(x)
|
print(x)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
b['set'].add('o3')
|
b['set'].add('o3')
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a['set'].values():
|
for x in a['set'].itervalues():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
for x in b['set'].values():
|
for x in b['set'].itervalues():
|
||||||
print(x)
|
print(x)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -269,7 +273,7 @@ if __name__ == "__main__":
|
|||||||
a['set2'].add("o2")
|
a['set2'].add("o2")
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
@@ -283,13 +287,13 @@ if __name__ == "__main__":
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
print("Yay! deleted key raises error")
|
print("Yay! deleted key raises error")
|
||||||
|
|
||||||
if 'b' in b:
|
if b.has_key('b'):
|
||||||
print("Boo!")
|
print("Boo!")
|
||||||
else:
|
else:
|
||||||
print("Yay - has_key with delete works!")
|
print("Yay - has_key with delete works!")
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
@@ -300,7 +304,7 @@ if __name__ == "__main__":
|
|||||||
b.__revertitem__('b')
|
b.__revertitem__('b')
|
||||||
|
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
@@ -310,7 +314,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
b.__revertitem__('dict')
|
b.__revertitem__('dict')
|
||||||
print("a", a)
|
print("a", a)
|
||||||
for x in a.items():
|
for x in a.iteritems():
|
||||||
print(x)
|
print(x)
|
||||||
print("--")
|
print("--")
|
||||||
print("b", b)
|
print("b", b)
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
__version__ = "1.32.0"
|
__version__ = "1.28.0"
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info < (3, 4, 0):
|
if sys.version_info < (2, 7, 3):
|
||||||
raise RuntimeError("Sorry, python 3.4.0 or later is required for this version of bitbake")
|
raise RuntimeError("Sorry, python 2.7.3 or later is required for this version of bitbake")
|
||||||
|
|
||||||
|
|
||||||
class BBHandledException(Exception):
|
class BBHandledException(Exception):
|
||||||
@@ -70,8 +70,6 @@ logger = logging.getLogger("BitBake")
|
|||||||
logger.addHandler(NullHandler())
|
logger.addHandler(NullHandler())
|
||||||
logger.setLevel(logging.DEBUG - 2)
|
logger.setLevel(logging.DEBUG - 2)
|
||||||
|
|
||||||
mainlogger = logging.getLogger("BitBake.Main")
|
|
||||||
|
|
||||||
# This has to be imported after the setLoggerClass, as the import of bb.msg
|
# This has to be imported after the setLoggerClass, as the import of bb.msg
|
||||||
# can result in construction of the various loggers.
|
# can result in construction of the various loggers.
|
||||||
import bb.msg
|
import bb.msg
|
||||||
@@ -81,26 +79,26 @@ sys.modules['bb.fetch'] = sys.modules['bb.fetch2']
|
|||||||
|
|
||||||
# Messaging convenience functions
|
# Messaging convenience functions
|
||||||
def plain(*args):
|
def plain(*args):
|
||||||
mainlogger.plain(''.join(args))
|
logger.plain(''.join(args))
|
||||||
|
|
||||||
def debug(lvl, *args):
|
def debug(lvl, *args):
|
||||||
if isinstance(lvl, str):
|
if isinstance(lvl, basestring):
|
||||||
mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
|
logger.warn("Passed invalid debug level '%s' to bb.debug", lvl)
|
||||||
args = (lvl,) + args
|
args = (lvl,) + args
|
||||||
lvl = 1
|
lvl = 1
|
||||||
mainlogger.debug(lvl, ''.join(args))
|
logger.debug(lvl, ''.join(args))
|
||||||
|
|
||||||
def note(*args):
|
def note(*args):
|
||||||
mainlogger.info(''.join(args))
|
logger.info(''.join(args))
|
||||||
|
|
||||||
def warn(*args):
|
def warn(*args):
|
||||||
mainlogger.warning(''.join(args))
|
logger.warn(''.join(args))
|
||||||
|
|
||||||
def error(*args, **kwargs):
|
def error(*args, **kwargs):
|
||||||
mainlogger.error(''.join(args), extra=kwargs)
|
logger.error(''.join(args), extra=kwargs)
|
||||||
|
|
||||||
def fatal(*args, **kwargs):
|
def fatal(*args, **kwargs):
|
||||||
mainlogger.critical(''.join(args), extra=kwargs)
|
logger.critical(''.join(args), extra=kwargs)
|
||||||
raise BBHandledException()
|
raise BBHandledException()
|
||||||
|
|
||||||
def deprecated(func, name=None, advice=""):
|
def deprecated(func, name=None, advice=""):
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import stat
|
|||||||
import bb
|
import bb
|
||||||
import bb.msg
|
import bb.msg
|
||||||
import bb.process
|
import bb.process
|
||||||
import bb.progress
|
from contextlib import nested
|
||||||
from bb import data, event, utils
|
from bb import event, utils
|
||||||
|
|
||||||
bblogger = logging.getLogger('BitBake')
|
bblogger = logging.getLogger('BitBake')
|
||||||
logger = logging.getLogger('BitBake.Build')
|
logger = logging.getLogger('BitBake.Build')
|
||||||
@@ -61,13 +61,8 @@ def reset_cache():
|
|||||||
# in all namespaces, hence we add them to __builtins__.
|
# in all namespaces, hence we add them to __builtins__.
|
||||||
# If we do not do this and use the exec globals, they will
|
# If we do not do this and use the exec globals, they will
|
||||||
# not be available to subfunctions.
|
# not be available to subfunctions.
|
||||||
if hasattr(__builtins__, '__setitem__'):
|
__builtins__['bb'] = bb
|
||||||
builtins = __builtins__
|
__builtins__['os'] = os
|
||||||
else:
|
|
||||||
builtins = __builtins__.__dict__
|
|
||||||
|
|
||||||
builtins['bb'] = bb
|
|
||||||
builtins['os'] = os
|
|
||||||
|
|
||||||
class FuncFailed(Exception):
|
class FuncFailed(Exception):
|
||||||
def __init__(self, name = None, logfile = None):
|
def __init__(self, name = None, logfile = None):
|
||||||
@@ -92,7 +87,6 @@ class TaskBase(event.Event):
|
|||||||
def __init__(self, t, logfile, d):
|
def __init__(self, t, logfile, d):
|
||||||
self._task = t
|
self._task = t
|
||||||
self._package = d.getVar("PF", True)
|
self._package = d.getVar("PF", True)
|
||||||
self._mc = d.getVar("BB_CURRENT_MC", True)
|
|
||||||
self.taskfile = d.getVar("FILE", True)
|
self.taskfile = d.getVar("FILE", True)
|
||||||
self.taskname = self._task
|
self.taskname = self._task
|
||||||
self.logfile = logfile
|
self.logfile = logfile
|
||||||
@@ -139,25 +133,6 @@ class TaskInvalid(TaskBase):
|
|||||||
super(TaskInvalid, self).__init__(task, None, metadata)
|
super(TaskInvalid, self).__init__(task, None, metadata)
|
||||||
self._message = "No such task '%s'" % task
|
self._message = "No such task '%s'" % task
|
||||||
|
|
||||||
class TaskProgress(event.Event):
|
|
||||||
"""
|
|
||||||
Task made some progress that could be reported to the user, usually in
|
|
||||||
the form of a progress bar or similar.
|
|
||||||
NOTE: this class does not inherit from TaskBase since it doesn't need
|
|
||||||
to - it's fired within the task context itself, so we don't have any of
|
|
||||||
the context information that you do in the case of the other events.
|
|
||||||
The event PID can be used to determine which task it came from.
|
|
||||||
The progress value is normally 0-100, but can also be negative
|
|
||||||
indicating that progress has been made but we aren't able to determine
|
|
||||||
how much.
|
|
||||||
The rate is optional, this is simply an extra string to display to the
|
|
||||||
user if specified.
|
|
||||||
"""
|
|
||||||
def __init__(self, progress, rate=None):
|
|
||||||
self.progress = progress
|
|
||||||
self.rate = rate
|
|
||||||
event.Event.__init__(self)
|
|
||||||
|
|
||||||
|
|
||||||
class LogTee(object):
|
class LogTee(object):
|
||||||
def __init__(self, logger, outfile):
|
def __init__(self, logger, outfile):
|
||||||
@@ -181,18 +156,14 @@ class LogTee(object):
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
self.outfile.flush()
|
self.outfile.flush()
|
||||||
|
|
||||||
#
|
def exec_func(func, d, dirs = None):
|
||||||
# pythonexception allows the python exceptions generated to be raised
|
|
||||||
# as the real exceptions (not FuncFailed) and without a backtrace at the
|
|
||||||
# origin of the failure.
|
|
||||||
#
|
|
||||||
def exec_func(func, d, dirs = None, pythonexception=False):
|
|
||||||
"""Execute a BB 'function'"""
|
"""Execute a BB 'function'"""
|
||||||
|
|
||||||
try:
|
body = d.getVar(func, False)
|
||||||
oldcwd = os.getcwd()
|
if not body:
|
||||||
except:
|
if body is None:
|
||||||
oldcwd = None
|
logger.warn("Function %s doesn't exist", func)
|
||||||
|
return
|
||||||
|
|
||||||
flags = d.getVarFlags(func)
|
flags = d.getVarFlags(func)
|
||||||
cleandirs = flags.get('cleandirs')
|
cleandirs = flags.get('cleandirs')
|
||||||
@@ -211,13 +182,8 @@ def exec_func(func, d, dirs = None, pythonexception=False):
|
|||||||
bb.utils.mkdirhier(adir)
|
bb.utils.mkdirhier(adir)
|
||||||
adir = dirs[-1]
|
adir = dirs[-1]
|
||||||
else:
|
else:
|
||||||
adir = None
|
adir = d.getVar('B', True)
|
||||||
|
bb.utils.mkdirhier(adir)
|
||||||
body = d.getVar(func, False)
|
|
||||||
if not body:
|
|
||||||
if body is None:
|
|
||||||
logger.warning("Function %s doesn't exist", func)
|
|
||||||
return
|
|
||||||
|
|
||||||
ispython = flags.get('python')
|
ispython = flags.get('python')
|
||||||
|
|
||||||
@@ -258,30 +224,22 @@ def exec_func(func, d, dirs = None, pythonexception=False):
|
|||||||
|
|
||||||
with bb.utils.fileslocked(lockfiles):
|
with bb.utils.fileslocked(lockfiles):
|
||||||
if ispython:
|
if ispython:
|
||||||
exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
|
exec_func_python(func, d, runfile, cwd=adir)
|
||||||
else:
|
else:
|
||||||
exec_func_shell(func, d, runfile, cwd=adir)
|
exec_func_shell(func, d, runfile, cwd=adir)
|
||||||
|
|
||||||
try:
|
|
||||||
curcwd = os.getcwd()
|
|
||||||
except:
|
|
||||||
curcwd = None
|
|
||||||
|
|
||||||
if oldcwd and curcwd != oldcwd:
|
|
||||||
try:
|
|
||||||
bb.warn("Task %s changed cwd to %s" % (func, curcwd))
|
|
||||||
os.chdir(oldcwd)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
_functionfmt = """
|
_functionfmt = """
|
||||||
|
def {function}(d):
|
||||||
|
{body}
|
||||||
|
|
||||||
{function}(d)
|
{function}(d)
|
||||||
"""
|
"""
|
||||||
logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||||
def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
|
def exec_func_python(func, d, runfile, cwd=None):
|
||||||
"""Execute a python BB 'function'"""
|
"""Execute a python BB 'function'"""
|
||||||
|
|
||||||
code = _functionfmt.format(function=func)
|
bbfile = d.getVar('FILE', True)
|
||||||
|
code = _functionfmt.format(function=func, body=d.getVar(func, True))
|
||||||
bb.utils.mkdirhier(os.path.dirname(runfile))
|
bb.utils.mkdirhier(os.path.dirname(runfile))
|
||||||
with open(runfile, 'w') as script:
|
with open(runfile, 'w') as script:
|
||||||
bb.data.emit_func_python(func, script, d)
|
bb.data.emit_func_python(func, script, d)
|
||||||
@@ -289,26 +247,18 @@ def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
|
|||||||
if cwd:
|
if cwd:
|
||||||
try:
|
try:
|
||||||
olddir = os.getcwd()
|
olddir = os.getcwd()
|
||||||
except OSError as e:
|
except OSError:
|
||||||
bb.warn("%s: Cannot get cwd: %s" % (func, e))
|
|
||||||
olddir = None
|
olddir = None
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
|
|
||||||
bb.debug(2, "Executing python function %s" % func)
|
bb.debug(2, "Executing python function %s" % func)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = "def %s(d):\n%s" % (func, d.getVar(func, False))
|
comp = utils.better_compile(code, func, bbfile)
|
||||||
fn = d.getVarFlag(func, "filename", False)
|
utils.better_exec(comp, {"d": d}, code, bbfile)
|
||||||
lineno = int(d.getVarFlag(func, "lineno", False))
|
|
||||||
bb.methodpool.insert_method(func, text, fn, lineno - 1)
|
|
||||||
|
|
||||||
comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
|
|
||||||
utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
|
|
||||||
except (bb.parse.SkipRecipe, bb.build.FuncFailed):
|
except (bb.parse.SkipRecipe, bb.build.FuncFailed):
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
if pythonexception:
|
|
||||||
raise
|
|
||||||
raise FuncFailed(func, None)
|
raise FuncFailed(func, None)
|
||||||
finally:
|
finally:
|
||||||
bb.debug(2, "Python function %s finished" % func)
|
bb.debug(2, "Python function %s finished" % func)
|
||||||
@@ -316,8 +266,8 @@ def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
|
|||||||
if cwd and olddir:
|
if cwd and olddir:
|
||||||
try:
|
try:
|
||||||
os.chdir(olddir)
|
os.chdir(olddir)
|
||||||
except OSError as e:
|
except OSError:
|
||||||
bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
|
pass
|
||||||
|
|
||||||
def shell_trap_code():
|
def shell_trap_code():
|
||||||
return '''#!/bin/sh\n
|
return '''#!/bin/sh\n
|
||||||
@@ -327,8 +277,9 @@ bb_exit_handler() {
|
|||||||
case $ret in
|
case $ret in
|
||||||
0) ;;
|
0) ;;
|
||||||
*) case $BASH_VERSION in
|
*) case $BASH_VERSION in
|
||||||
"") echo "WARNING: exit code $ret from a shell command.";;
|
"") echo "WARNING: exit code $ret from a shell command.";;
|
||||||
*) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
|
*) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from
|
||||||
|
\"$BASH_COMMAND\"";;
|
||||||
esac
|
esac
|
||||||
exit $ret
|
exit $ret
|
||||||
esac
|
esac
|
||||||
@@ -365,10 +316,10 @@ trap '' 0
|
|||||||
exit $ret
|
exit $ret
|
||||||
''')
|
''')
|
||||||
|
|
||||||
os.chmod(runfile, 0o775)
|
os.chmod(runfile, 0775)
|
||||||
|
|
||||||
cmd = runfile
|
cmd = runfile
|
||||||
if d.getVarFlag(func, 'fakeroot', False):
|
if d.getVarFlag(func, 'fakeroot'):
|
||||||
fakerootcmd = d.getVar('FAKEROOT', True)
|
fakerootcmd = d.getVar('FAKEROOT', True)
|
||||||
if fakerootcmd:
|
if fakerootcmd:
|
||||||
cmd = [fakerootcmd, runfile]
|
cmd = [fakerootcmd, runfile]
|
||||||
@@ -378,64 +329,41 @@ exit $ret
|
|||||||
else:
|
else:
|
||||||
logfile = sys.stdout
|
logfile = sys.stdout
|
||||||
|
|
||||||
progress = d.getVarFlag(func, 'progress', True)
|
|
||||||
if progress:
|
|
||||||
if progress == 'percent':
|
|
||||||
# Use default regex
|
|
||||||
logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
|
|
||||||
elif progress.startswith('percent:'):
|
|
||||||
# Use specified regex
|
|
||||||
logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
|
|
||||||
elif progress.startswith('outof:'):
|
|
||||||
# Use specified regex
|
|
||||||
logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
|
|
||||||
else:
|
|
||||||
bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
|
|
||||||
|
|
||||||
fifobuffer = bytearray()
|
|
||||||
def readfifo(data):
|
def readfifo(data):
|
||||||
nonlocal fifobuffer
|
lines = data.split('\0')
|
||||||
fifobuffer.extend(data)
|
for line in lines:
|
||||||
while fifobuffer:
|
splitval = line.split(' ', 1)
|
||||||
message, token, nextmsg = fifobuffer.partition(b"\00")
|
cmd = splitval[0]
|
||||||
if token:
|
if len(splitval) > 1:
|
||||||
splitval = message.split(b' ', 1)
|
value = splitval[1]
|
||||||
cmd = splitval[0].decode("utf-8")
|
|
||||||
if len(splitval) > 1:
|
|
||||||
value = splitval[1].decode("utf-8")
|
|
||||||
else:
|
|
||||||
value = ''
|
|
||||||
if cmd == 'bbplain':
|
|
||||||
bb.plain(value)
|
|
||||||
elif cmd == 'bbnote':
|
|
||||||
bb.note(value)
|
|
||||||
elif cmd == 'bbwarn':
|
|
||||||
bb.warn(value)
|
|
||||||
elif cmd == 'bberror':
|
|
||||||
bb.error(value)
|
|
||||||
elif cmd == 'bbfatal':
|
|
||||||
# The caller will call exit themselves, so bb.error() is
|
|
||||||
# what we want here rather than bb.fatal()
|
|
||||||
bb.error(value)
|
|
||||||
elif cmd == 'bbfatal_log':
|
|
||||||
bb.error(value, forcelog=True)
|
|
||||||
elif cmd == 'bbdebug':
|
|
||||||
splitval = value.split(' ', 1)
|
|
||||||
level = int(splitval[0])
|
|
||||||
value = splitval[1]
|
|
||||||
bb.debug(level, value)
|
|
||||||
else:
|
|
||||||
bb.warn("Unrecognised command '%s' on FIFO" % cmd)
|
|
||||||
fifobuffer = nextmsg
|
|
||||||
else:
|
else:
|
||||||
break
|
value = ''
|
||||||
|
if cmd == 'bbplain':
|
||||||
|
bb.plain(value)
|
||||||
|
elif cmd == 'bbnote':
|
||||||
|
bb.note(value)
|
||||||
|
elif cmd == 'bbwarn':
|
||||||
|
bb.warn(value)
|
||||||
|
elif cmd == 'bberror':
|
||||||
|
bb.error(value)
|
||||||
|
elif cmd == 'bbfatal':
|
||||||
|
# The caller will call exit themselves, so bb.error() is
|
||||||
|
# what we want here rather than bb.fatal()
|
||||||
|
bb.error(value)
|
||||||
|
elif cmd == 'bbfatal_log':
|
||||||
|
bb.error(value, forcelog=True)
|
||||||
|
elif cmd == 'bbdebug':
|
||||||
|
splitval = value.split(' ', 1)
|
||||||
|
level = int(splitval[0])
|
||||||
|
value = splitval[1]
|
||||||
|
bb.debug(level, value)
|
||||||
|
|
||||||
tempdir = d.getVar('T', True)
|
tempdir = d.getVar('T', True)
|
||||||
fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
|
fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
|
||||||
if os.path.exists(fifopath):
|
if os.path.exists(fifopath):
|
||||||
os.unlink(fifopath)
|
os.unlink(fifopath)
|
||||||
os.mkfifo(fifopath)
|
os.mkfifo(fifopath)
|
||||||
with open(fifopath, 'r+b', buffering=0) as fifo:
|
with open(fifopath, 'r+') as fifo:
|
||||||
try:
|
try:
|
||||||
bb.debug(2, "Executing shell function %s" % func)
|
bb.debug(2, "Executing shell function %s" % func)
|
||||||
|
|
||||||
@@ -466,7 +394,7 @@ def _exec_task(fn, task, d, quieterr):
|
|||||||
Execution of a task involves a bit more setup than executing a function,
|
Execution of a task involves a bit more setup than executing a function,
|
||||||
running it with its own local metadata, and with some useful variables set.
|
running it with its own local metadata, and with some useful variables set.
|
||||||
"""
|
"""
|
||||||
if not d.getVarFlag(task, 'task', False):
|
if not d.getVarFlag(task, 'task'):
|
||||||
event.fire(TaskInvalid(task, d), d)
|
event.fire(TaskInvalid(task, d), d)
|
||||||
logger.error("No such task: %s" % task)
|
logger.error("No such task: %s" % task)
|
||||||
return 1
|
return 1
|
||||||
@@ -566,32 +494,21 @@ def _exec_task(fn, task, d, quieterr):
|
|||||||
|
|
||||||
flags = localdata.getVarFlags(task)
|
flags = localdata.getVarFlags(task)
|
||||||
|
|
||||||
|
event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
|
||||||
try:
|
try:
|
||||||
try:
|
for func in (prefuncs or '').split():
|
||||||
event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
|
exec_func(func, localdata)
|
||||||
except (bb.BBHandledException, SystemExit):
|
exec_func(task, localdata)
|
||||||
return 1
|
for func in (postfuncs or '').split():
|
||||||
except FuncFailed as exc:
|
exec_func(func, localdata)
|
||||||
|
except FuncFailed as exc:
|
||||||
|
if quieterr:
|
||||||
|
event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
|
||||||
|
else:
|
||||||
|
errprinted = errchk.triggered
|
||||||
logger.error(str(exc))
|
logger.error(str(exc))
|
||||||
return 1
|
event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
|
||||||
|
return 1
|
||||||
try:
|
|
||||||
for func in (prefuncs or '').split():
|
|
||||||
exec_func(func, localdata)
|
|
||||||
exec_func(task, localdata)
|
|
||||||
for func in (postfuncs or '').split():
|
|
||||||
exec_func(func, localdata)
|
|
||||||
except FuncFailed as exc:
|
|
||||||
if quieterr:
|
|
||||||
event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
|
|
||||||
else:
|
|
||||||
errprinted = errchk.triggered
|
|
||||||
logger.error(str(exc))
|
|
||||||
event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
|
|
||||||
return 1
|
|
||||||
except bb.BBHandledException:
|
|
||||||
event.fire(TaskFailed(task, logfn, localdata, True), localdata)
|
|
||||||
return 1
|
|
||||||
finally:
|
finally:
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -616,7 +533,7 @@ def _exec_task(fn, task, d, quieterr):
|
|||||||
bb.utils.remove(loglink)
|
bb.utils.remove(loglink)
|
||||||
event.fire(TaskSucceeded(task, logfn, localdata), localdata)
|
event.fire(TaskSucceeded(task, logfn, localdata), localdata)
|
||||||
|
|
||||||
if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
|
if not localdata.getVarFlag(task, 'nostamp') and not localdata.getVarFlag(task, 'selfstamp'):
|
||||||
make_stamp(task, localdata)
|
make_stamp(task, localdata)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -624,7 +541,7 @@ def _exec_task(fn, task, d, quieterr):
|
|||||||
def exec_task(fn, task, d, profile = False):
|
def exec_task(fn, task, d, profile = False):
|
||||||
try:
|
try:
|
||||||
quieterr = False
|
quieterr = False
|
||||||
if d.getVarFlag(task, "quieterrors", False) is not None:
|
if d.getVarFlag(task, "quieterrors") is not None:
|
||||||
quieterr = True
|
quieterr = True
|
||||||
|
|
||||||
if profile:
|
if profile:
|
||||||
@@ -651,7 +568,7 @@ def exec_task(fn, task, d, profile = False):
|
|||||||
event.fire(failedevent, d)
|
event.fire(failedevent, d)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
|
def stamp_internal(taskname, d, file_name, baseonly=False):
|
||||||
"""
|
"""
|
||||||
Internal stamp helper function
|
Internal stamp helper function
|
||||||
Makes sure the stamp directory exists
|
Makes sure the stamp directory exists
|
||||||
@@ -665,17 +582,15 @@ def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
|
|||||||
taskflagname = taskname.replace("_setscene", "")
|
taskflagname = taskname.replace("_setscene", "")
|
||||||
|
|
||||||
if file_name:
|
if file_name:
|
||||||
stamp = d.stamp[file_name]
|
stamp = d.stamp_base[file_name].get(taskflagname) or d.stamp[file_name]
|
||||||
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
|
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
|
||||||
else:
|
else:
|
||||||
stamp = d.getVar('STAMP', True)
|
stamp = d.getVarFlag(taskflagname, 'stamp-base', True) or d.getVar('STAMP', True)
|
||||||
file_name = d.getVar('BB_FILENAME', True)
|
file_name = d.getVar('BB_FILENAME', True)
|
||||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
||||||
|
|
||||||
if baseonly:
|
if baseonly:
|
||||||
return stamp
|
return stamp
|
||||||
if noextra:
|
|
||||||
extrainfo = ""
|
|
||||||
|
|
||||||
if not stamp:
|
if not stamp:
|
||||||
return
|
return
|
||||||
@@ -701,10 +616,10 @@ def stamp_cleanmask_internal(taskname, d, file_name):
|
|||||||
taskflagname = taskname.replace("_setscene", "")
|
taskflagname = taskname.replace("_setscene", "")
|
||||||
|
|
||||||
if file_name:
|
if file_name:
|
||||||
stamp = d.stampclean[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 ""
|
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
|
||||||
else:
|
else:
|
||||||
stamp = d.getVar('STAMPCLEAN', True)
|
stamp = d.getVarFlag(taskflagname, 'stamp-base-clean', True) or d.getVar('STAMPCLEAN', True)
|
||||||
file_name = d.getVar('BB_FILENAME', True)
|
file_name = d.getVar('BB_FILENAME', True)
|
||||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
||||||
|
|
||||||
@@ -724,7 +639,7 @@ def make_stamp(task, d, file_name = None):
|
|||||||
for mask in cleanmask:
|
for mask in cleanmask:
|
||||||
for name in glob.glob(mask):
|
for name in glob.glob(mask):
|
||||||
# Preserve sigdata files in the stamps directory
|
# Preserve sigdata files in the stamps directory
|
||||||
if "sigdata" in name or "sigbasedata" in name:
|
if "sigdata" in name:
|
||||||
continue
|
continue
|
||||||
# Preserve taint files in the stamps directory
|
# Preserve taint files in the stamps directory
|
||||||
if name.endswith('.taint'):
|
if name.endswith('.taint'):
|
||||||
@@ -771,12 +686,12 @@ def write_taint(task, d, file_name = None):
|
|||||||
with open(taintfn, 'w') as taintf:
|
with open(taintfn, 'w') as taintf:
|
||||||
taintf.write(str(uuid.uuid4()))
|
taintf.write(str(uuid.uuid4()))
|
||||||
|
|
||||||
def stampfile(taskname, d, file_name = None, noextra=False):
|
def stampfile(taskname, d, file_name = None):
|
||||||
"""
|
"""
|
||||||
Return the stamp for a given task
|
Return the stamp for a given task
|
||||||
(d can be a data dict or dataCache)
|
(d can be a data dict or dataCache)
|
||||||
"""
|
"""
|
||||||
return stamp_internal(taskname, d, file_name, noextra=noextra)
|
return stamp_internal(taskname, d, file_name)
|
||||||
|
|
||||||
def add_tasks(tasklist, d):
|
def add_tasks(tasklist, d):
|
||||||
task_deps = d.getVar('_task_deps', False)
|
task_deps = d.getVar('_task_deps', False)
|
||||||
@@ -831,7 +746,7 @@ def addtask(task, before, after, d):
|
|||||||
bbtasks.append(task)
|
bbtasks.append(task)
|
||||||
d.setVar('__BBTASKS', bbtasks)
|
d.setVar('__BBTASKS', bbtasks)
|
||||||
|
|
||||||
existing = d.getVarFlag(task, "deps", False) or []
|
existing = d.getVarFlag(task, "deps") or []
|
||||||
if after is not None:
|
if after is not None:
|
||||||
# set up deps for function
|
# set up deps for function
|
||||||
for entry in after.split():
|
for entry in after.split():
|
||||||
@@ -841,7 +756,7 @@ def addtask(task, before, after, d):
|
|||||||
if before is not None:
|
if before is not None:
|
||||||
# set up things that depend on this func
|
# set up things that depend on this func
|
||||||
for entry in before.split():
|
for entry in before.split():
|
||||||
existing = d.getVarFlag(entry, "deps", False) or []
|
existing = d.getVarFlag(entry, "deps") or []
|
||||||
if task not in existing:
|
if task not in existing:
|
||||||
d.setVarFlag(entry, "deps", [task] + existing)
|
d.setVarFlag(entry, "deps", [task] + existing)
|
||||||
|
|
||||||
@@ -852,12 +767,11 @@ def deltask(task, d):
|
|||||||
bbtasks = d.getVar('__BBTASKS', False) or []
|
bbtasks = d.getVar('__BBTASKS', False) or []
|
||||||
if task in bbtasks:
|
if task in bbtasks:
|
||||||
bbtasks.remove(task)
|
bbtasks.remove(task)
|
||||||
d.delVarFlag(task, 'task')
|
|
||||||
d.setVar('__BBTASKS', bbtasks)
|
d.setVar('__BBTASKS', bbtasks)
|
||||||
|
|
||||||
d.delVarFlag(task, 'deps')
|
d.delVarFlag(task, 'deps')
|
||||||
for bbtask in d.getVar('__BBTASKS', False) or []:
|
for bbtask in d.getVar('__BBTASKS', False) or []:
|
||||||
deps = d.getVarFlag(bbtask, 'deps', False) or []
|
deps = d.getVarFlag(bbtask, 'deps') or []
|
||||||
if task in deps:
|
if task in deps:
|
||||||
deps.remove(task)
|
deps.remove(task)
|
||||||
d.setVarFlag(bbtask, 'deps', deps)
|
d.setVarFlag(bbtask, 'deps', deps)
|
||||||
|
|||||||
@@ -28,16 +28,22 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import bb.utils
|
import bb.utils
|
||||||
|
|
||||||
logger = logging.getLogger("BitBake.Cache")
|
logger = logging.getLogger("BitBake.Cache")
|
||||||
|
|
||||||
__cache_version__ = "150"
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
logger.info("Importing cPickle failed. "
|
||||||
|
"Falling back to a very slow implementation.")
|
||||||
|
|
||||||
|
__cache_version__ = "148"
|
||||||
|
|
||||||
def getCacheFile(path, filename, data_hash):
|
def getCacheFile(path, filename, data_hash):
|
||||||
return os.path.join(path, filename + "." + data_hash)
|
return os.path.join(path, filename + "." + data_hash)
|
||||||
@@ -74,13 +80,13 @@ class RecipeInfoCommon(object):
|
|||||||
out_dict = dict((var, metadata.getVarFlag(var, flag, True))
|
out_dict = dict((var, metadata.getVarFlag(var, flag, True))
|
||||||
for var in varlist)
|
for var in varlist)
|
||||||
if squash:
|
if squash:
|
||||||
return dict((k,v) for (k,v) in out_dict.items() if v)
|
return dict((k,v) for (k,v) in out_dict.iteritems() if v)
|
||||||
else:
|
else:
|
||||||
return out_dict
|
return out_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getvar(cls, var, metadata, expand = True):
|
def getvar(cls, var, metadata):
|
||||||
return metadata.getVar(var, expand) or ''
|
return metadata.getVar(var, True) or ''
|
||||||
|
|
||||||
|
|
||||||
class CoreRecipeInfo(RecipeInfoCommon):
|
class CoreRecipeInfo(RecipeInfoCommon):
|
||||||
@@ -93,7 +99,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
self.timestamp = bb.parse.cached_mtime(filename)
|
self.timestamp = bb.parse.cached_mtime(filename)
|
||||||
self.variants = self.listvar('__VARIANTS', metadata) + ['']
|
self.variants = self.listvar('__VARIANTS', metadata) + ['']
|
||||||
self.appends = self.listvar('__BBAPPEND', metadata)
|
self.appends = self.listvar('__BBAPPEND', metadata)
|
||||||
self.nocache = self.getvar('BB_DONT_CACHE', metadata)
|
self.nocache = self.getvar('__BB_DONT_CACHE', metadata)
|
||||||
|
|
||||||
self.skipreason = self.getvar('__SKIPPED', metadata)
|
self.skipreason = self.getvar('__SKIPPED', metadata)
|
||||||
if self.skipreason:
|
if self.skipreason:
|
||||||
@@ -123,6 +129,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
self.not_world = self.getvar('EXCLUDE_FROM_WORLD', metadata)
|
self.not_world = self.getvar('EXCLUDE_FROM_WORLD', metadata)
|
||||||
self.stamp = self.getvar('STAMP', metadata)
|
self.stamp = self.getvar('STAMP', metadata)
|
||||||
self.stampclean = self.getvar('STAMPCLEAN', 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.stamp_extrainfo = self.flaglist('stamp-extra-info', self.tasks, metadata)
|
||||||
self.file_checksums = self.flaglist('file-checksums', self.tasks, metadata, True)
|
self.file_checksums = self.flaglist('file-checksums', self.tasks, metadata, True)
|
||||||
self.packages_dynamic = self.listvar('PACKAGES_DYNAMIC', metadata)
|
self.packages_dynamic = self.listvar('PACKAGES_DYNAMIC', metadata)
|
||||||
@@ -134,11 +142,10 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
self.rprovides_pkg = self.pkgvar('RPROVIDES', self.packages, metadata)
|
self.rprovides_pkg = self.pkgvar('RPROVIDES', self.packages, metadata)
|
||||||
self.rdepends_pkg = self.pkgvar('RDEPENDS', self.packages, metadata)
|
self.rdepends_pkg = self.pkgvar('RDEPENDS', self.packages, metadata)
|
||||||
self.rrecommends_pkg = self.pkgvar('RRECOMMENDS', self.packages, metadata)
|
self.rrecommends_pkg = self.pkgvar('RRECOMMENDS', self.packages, metadata)
|
||||||
self.inherits = self.getvar('__inherit_cache', metadata, expand=False)
|
self.inherits = self.getvar('__inherit_cache', metadata)
|
||||||
self.fakerootenv = self.getvar('FAKEROOTENV', metadata)
|
self.fakerootenv = self.getvar('FAKEROOTENV', metadata)
|
||||||
self.fakerootdirs = self.getvar('FAKEROOTDIRS', metadata)
|
self.fakerootdirs = self.getvar('FAKEROOTDIRS', metadata)
|
||||||
self.fakerootnoenv = self.getvar('FAKEROOTNOENV', metadata)
|
self.fakerootnoenv = self.getvar('FAKEROOTNOENV', metadata)
|
||||||
self.extradepsfunc = self.getvar('calculate_extra_depends', metadata)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_cacheData(cls, cachedata):
|
def init_cacheData(cls, cachedata):
|
||||||
@@ -151,6 +158,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
|
|
||||||
cachedata.stamp = {}
|
cachedata.stamp = {}
|
||||||
cachedata.stampclean = {}
|
cachedata.stampclean = {}
|
||||||
|
cachedata.stamp_base = {}
|
||||||
|
cachedata.stamp_base_clean = {}
|
||||||
cachedata.stamp_extrainfo = {}
|
cachedata.stamp_extrainfo = {}
|
||||||
cachedata.file_checksums = {}
|
cachedata.file_checksums = {}
|
||||||
cachedata.fn_provides = {}
|
cachedata.fn_provides = {}
|
||||||
@@ -174,7 +183,6 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
cachedata.fakerootenv = {}
|
cachedata.fakerootenv = {}
|
||||||
cachedata.fakerootnoenv = {}
|
cachedata.fakerootnoenv = {}
|
||||||
cachedata.fakerootdirs = {}
|
cachedata.fakerootdirs = {}
|
||||||
cachedata.extradepsfunc = {}
|
|
||||||
|
|
||||||
def add_cacheData(self, cachedata, fn):
|
def add_cacheData(self, cachedata, fn):
|
||||||
cachedata.task_deps[fn] = self.task_deps
|
cachedata.task_deps[fn] = self.task_deps
|
||||||
@@ -184,6 +192,8 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
cachedata.pkg_dp[fn] = self.defaultpref
|
cachedata.pkg_dp[fn] = self.defaultpref
|
||||||
cachedata.stamp[fn] = self.stamp
|
cachedata.stamp[fn] = self.stamp
|
||||||
cachedata.stampclean[fn] = self.stampclean
|
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.stamp_extrainfo[fn] = self.stamp_extrainfo
|
||||||
cachedata.file_checksums[fn] = self.file_checksums
|
cachedata.file_checksums[fn] = self.file_checksums
|
||||||
|
|
||||||
@@ -210,8 +220,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
rprovides += self.rprovides_pkg[package]
|
rprovides += self.rprovides_pkg[package]
|
||||||
|
|
||||||
for rprovide in rprovides:
|
for rprovide in rprovides:
|
||||||
if fn not in cachedata.rproviders[rprovide]:
|
cachedata.rproviders[rprovide].append(fn)
|
||||||
cachedata.rproviders[rprovide].append(fn)
|
|
||||||
|
|
||||||
for package in self.packages_dynamic:
|
for package in self.packages_dynamic:
|
||||||
cachedata.packages_dynamic[package].append(fn)
|
cachedata.packages_dynamic[package].append(fn)
|
||||||
@@ -234,7 +243,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
cachedata.universe_target.append(self.pn)
|
cachedata.universe_target.append(self.pn)
|
||||||
|
|
||||||
cachedata.hashfn[fn] = self.hashfilename
|
cachedata.hashfn[fn] = self.hashfilename
|
||||||
for task, taskhash in self.basetaskhashes.items():
|
for task, taskhash in self.basetaskhashes.iteritems():
|
||||||
identifier = '%s.%s' % (fn, task)
|
identifier = '%s.%s' % (fn, task)
|
||||||
cachedata.basetaskhash[identifier] = taskhash
|
cachedata.basetaskhash[identifier] = taskhash
|
||||||
|
|
||||||
@@ -242,138 +251,15 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
|||||||
cachedata.fakerootenv[fn] = self.fakerootenv
|
cachedata.fakerootenv[fn] = self.fakerootenv
|
||||||
cachedata.fakerootnoenv[fn] = self.fakerootnoenv
|
cachedata.fakerootnoenv[fn] = self.fakerootnoenv
|
||||||
cachedata.fakerootdirs[fn] = self.fakerootdirs
|
cachedata.fakerootdirs[fn] = self.fakerootdirs
|
||||||
cachedata.extradepsfunc[fn] = self.extradepsfunc
|
|
||||||
|
|
||||||
def virtualfn2realfn(virtualfn):
|
|
||||||
"""
|
|
||||||
Convert a virtual file name to a real one + the associated subclass keyword
|
|
||||||
"""
|
|
||||||
mc = ""
|
|
||||||
if virtualfn.startswith('multiconfig:'):
|
|
||||||
elems = virtualfn.split(':')
|
|
||||||
mc = elems[1]
|
|
||||||
virtualfn = ":".join(elems[2:])
|
|
||||||
|
|
||||||
fn = virtualfn
|
|
||||||
cls = ""
|
|
||||||
if virtualfn.startswith('virtual:'):
|
|
||||||
elems = virtualfn.split(':')
|
|
||||||
cls = ":".join(elems[1:-1])
|
|
||||||
fn = elems[-1]
|
|
||||||
|
|
||||||
return (fn, cls, mc)
|
|
||||||
|
|
||||||
def realfn2virtual(realfn, cls, mc):
|
|
||||||
"""
|
|
||||||
Convert a real filename + the associated subclass keyword to a virtual filename
|
|
||||||
"""
|
|
||||||
if cls:
|
|
||||||
realfn = "virtual:" + cls + ":" + realfn
|
|
||||||
if mc:
|
|
||||||
realfn = "multiconfig:" + mc + ":" + realfn
|
|
||||||
return realfn
|
|
||||||
|
|
||||||
def variant2virtual(realfn, variant):
|
|
||||||
"""
|
|
||||||
Convert a real filename + the associated subclass keyword to a virtual filename
|
|
||||||
"""
|
|
||||||
if variant == "":
|
|
||||||
return realfn
|
|
||||||
if variant.startswith("multiconfig:"):
|
|
||||||
elems = variant.split(":")
|
|
||||||
if elems[2]:
|
|
||||||
return "multiconfig:" + elems[1] + ":virtual:" + ":".join(elems[2:]) + ":" + realfn
|
|
||||||
return "multiconfig:" + elems[1] + ":" + realfn
|
|
||||||
return "virtual:" + variant + ":" + realfn
|
|
||||||
|
|
||||||
def parse_recipe(bb_data, bbfile, appends, mc=''):
|
|
||||||
"""
|
|
||||||
Parse a recipe
|
|
||||||
"""
|
|
||||||
|
|
||||||
chdir_back = False
|
|
||||||
|
|
||||||
bb_data.setVar("__BBMULTICONFIG", mc)
|
|
||||||
|
|
||||||
# expand tmpdir to include this topdir
|
|
||||||
bb_data.setVar('TMPDIR', bb_data.getVar('TMPDIR', True) or "")
|
|
||||||
bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
|
|
||||||
oldpath = os.path.abspath(os.getcwd())
|
|
||||||
bb.parse.cached_mtime_noerror(bbfile_loc)
|
|
||||||
|
|
||||||
# The ConfHandler first looks if there is a TOPDIR and if not
|
|
||||||
# then it would call getcwd().
|
|
||||||
# Previously, we chdir()ed to bbfile_loc, called the handler
|
|
||||||
# and finally chdir()ed back, a couple of thousand times. We now
|
|
||||||
# just fill in TOPDIR to point to bbfile_loc if there is no TOPDIR yet.
|
|
||||||
if not bb_data.getVar('TOPDIR', False):
|
|
||||||
chdir_back = True
|
|
||||||
bb_data.setVar('TOPDIR', bbfile_loc)
|
|
||||||
try:
|
|
||||||
if appends:
|
|
||||||
bb_data.setVar('__BBAPPEND', " ".join(appends))
|
|
||||||
bb_data = bb.parse.handle(bbfile, bb_data)
|
|
||||||
if chdir_back:
|
|
||||||
os.chdir(oldpath)
|
|
||||||
return bb_data
|
|
||||||
except:
|
|
||||||
if chdir_back:
|
|
||||||
os.chdir(oldpath)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NoCache(object):
|
class Cache(object):
|
||||||
|
|
||||||
def __init__(self, databuilder):
|
|
||||||
self.databuilder = databuilder
|
|
||||||
self.data = databuilder.data
|
|
||||||
|
|
||||||
def loadDataFull(self, virtualfn, appends):
|
|
||||||
"""
|
|
||||||
Return a complete set of data for fn.
|
|
||||||
To do this, we need to parse the file.
|
|
||||||
"""
|
|
||||||
logger.debug(1, "Parsing %s (full)" % virtualfn)
|
|
||||||
(fn, virtual, mc) = virtualfn2realfn(virtualfn)
|
|
||||||
bb_data = self.load_bbfile(virtualfn, appends, virtonly=True)
|
|
||||||
return bb_data[virtual]
|
|
||||||
|
|
||||||
def load_bbfile(self, bbfile, appends, virtonly = False):
|
|
||||||
"""
|
|
||||||
Load and parse one .bb build file
|
|
||||||
Return the data and whether parsing resulted in the file being skipped
|
|
||||||
"""
|
|
||||||
|
|
||||||
if virtonly:
|
|
||||||
(bbfile, virtual, mc) = virtualfn2realfn(bbfile)
|
|
||||||
bb_data = self.databuilder.mcdata[mc].createCopy()
|
|
||||||
bb_data.setVar("__ONLYFINALISE", virtual or "default")
|
|
||||||
datastores = parse_recipe(bb_data, bbfile, appends, mc)
|
|
||||||
return datastores
|
|
||||||
|
|
||||||
bb_data = self.data.createCopy()
|
|
||||||
datastores = parse_recipe(bb_data, bbfile, appends)
|
|
||||||
|
|
||||||
for mc in self.databuilder.mcdata:
|
|
||||||
if not mc:
|
|
||||||
continue
|
|
||||||
bb_data = self.databuilder.mcdata[mc].createCopy()
|
|
||||||
newstores = parse_recipe(bb_data, bbfile, appends, mc)
|
|
||||||
for ns in newstores:
|
|
||||||
datastores["multiconfig:%s:%s" % (mc, ns)] = newstores[ns]
|
|
||||||
|
|
||||||
return datastores
|
|
||||||
|
|
||||||
class Cache(NoCache):
|
|
||||||
"""
|
"""
|
||||||
BitBake Cache implementation
|
BitBake Cache implementation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, databuilder, data_hash, caches_array):
|
def __init__(self, data, data_hash, caches_array):
|
||||||
super().__init__(databuilder)
|
|
||||||
data = databuilder.data
|
|
||||||
|
|
||||||
# Pass caches_array information into Cache Constructor
|
# Pass caches_array information into Cache Constructor
|
||||||
# It will be used later for deciding whether we
|
# It will be used later for deciding whether we
|
||||||
# need extra cache file dump/load support
|
# need extra cache file dump/load support
|
||||||
@@ -382,6 +268,7 @@ class Cache(NoCache):
|
|||||||
self.clean = set()
|
self.clean = set()
|
||||||
self.checked = set()
|
self.checked = set()
|
||||||
self.depends_cache = {}
|
self.depends_cache = {}
|
||||||
|
self.data = None
|
||||||
self.data_fn = None
|
self.data_fn = None
|
||||||
self.cacheclean = True
|
self.cacheclean = True
|
||||||
self.data_hash = data_hash
|
self.data_hash = data_hash
|
||||||
@@ -401,74 +288,72 @@ class Cache(NoCache):
|
|||||||
cache_ok = True
|
cache_ok = True
|
||||||
if self.caches_array:
|
if self.caches_array:
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
cache_ok = cache_ok and os.path.exists(cachefile)
|
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||||
cache_class.init_cacheData(self)
|
cache_ok = cache_ok and os.path.exists(cachefile)
|
||||||
|
cache_class.init_cacheData(self)
|
||||||
if cache_ok:
|
if cache_ok:
|
||||||
self.load_cachefile()
|
self.load_cachefile()
|
||||||
elif os.path.isfile(self.cachefile):
|
elif os.path.isfile(self.cachefile):
|
||||||
logger.info("Out of date cache found, rebuilding...")
|
logger.info("Out of date cache found, rebuilding...")
|
||||||
|
|
||||||
def load_cachefile(self):
|
def load_cachefile(self):
|
||||||
|
# Firstly, using core cache file information for
|
||||||
|
# valid checking
|
||||||
|
with open(self.cachefile, "rb") as cachefile:
|
||||||
|
pickled = pickle.Unpickler(cachefile)
|
||||||
|
try:
|
||||||
|
cache_ver = pickled.load()
|
||||||
|
bitbake_ver = pickled.load()
|
||||||
|
except Exception:
|
||||||
|
logger.info('Invalid cache, rebuilding...')
|
||||||
|
return
|
||||||
|
|
||||||
|
if cache_ver != __cache_version__:
|
||||||
|
logger.info('Cache version mismatch, rebuilding...')
|
||||||
|
return
|
||||||
|
elif bitbake_ver != bb.__version__:
|
||||||
|
logger.info('Bitbake version mismatch, rebuilding...')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
cachesize = 0
|
cachesize = 0
|
||||||
previous_progress = 0
|
previous_progress = 0
|
||||||
previous_percent = 0
|
previous_percent = 0
|
||||||
|
|
||||||
# Calculate the correct cachesize of all those cache files
|
# Calculate the correct cachesize of all those cache files
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
with open(cachefile, "rb") as cachefile:
|
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||||
cachesize += os.fstat(cachefile.fileno()).st_size
|
with open(cachefile, "rb") as cachefile:
|
||||||
|
cachesize += os.fstat(cachefile.fileno()).st_size
|
||||||
|
|
||||||
bb.event.fire(bb.event.CacheLoadStarted(cachesize), self.data)
|
bb.event.fire(bb.event.CacheLoadStarted(cachesize), self.data)
|
||||||
|
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
with open(cachefile, "rb") as cachefile:
|
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||||
pickled = pickle.Unpickler(cachefile)
|
with open(cachefile, "rb") as cachefile:
|
||||||
# Check cache version information
|
pickled = pickle.Unpickler(cachefile)
|
||||||
try:
|
while cachefile:
|
||||||
cache_ver = pickled.load()
|
try:
|
||||||
bitbake_ver = pickled.load()
|
key = pickled.load()
|
||||||
except Exception:
|
value = pickled.load()
|
||||||
logger.info('Invalid cache, rebuilding...')
|
except Exception:
|
||||||
return
|
break
|
||||||
|
if self.depends_cache.has_key(key):
|
||||||
|
self.depends_cache[key].append(value)
|
||||||
|
else:
|
||||||
|
self.depends_cache[key] = [value]
|
||||||
|
# only fire events on even percentage boundaries
|
||||||
|
current_progress = cachefile.tell() + previous_progress
|
||||||
|
current_percent = 100 * current_progress / cachesize
|
||||||
|
if current_percent > previous_percent:
|
||||||
|
previous_percent = current_percent
|
||||||
|
bb.event.fire(bb.event.CacheLoadProgress(current_progress, cachesize),
|
||||||
|
self.data)
|
||||||
|
|
||||||
if cache_ver != __cache_version__:
|
previous_progress += current_progress
|
||||||
logger.info('Cache version mismatch, rebuilding...')
|
|
||||||
return
|
|
||||||
elif bitbake_ver != bb.__version__:
|
|
||||||
logger.info('Bitbake version mismatch, rebuilding...')
|
|
||||||
return
|
|
||||||
|
|
||||||
# Load the rest of the cache file
|
|
||||||
current_progress = 0
|
|
||||||
while cachefile:
|
|
||||||
try:
|
|
||||||
key = pickled.load()
|
|
||||||
value = pickled.load()
|
|
||||||
except Exception:
|
|
||||||
break
|
|
||||||
if not isinstance(key, str):
|
|
||||||
bb.warn("%s from extras cache is not a string?" % key)
|
|
||||||
break
|
|
||||||
if not isinstance(value, RecipeInfoCommon):
|
|
||||||
bb.warn("%s from extras cache is not a RecipeInfoCommon class?" % value)
|
|
||||||
break
|
|
||||||
|
|
||||||
if key in self.depends_cache:
|
|
||||||
self.depends_cache[key].append(value)
|
|
||||||
else:
|
|
||||||
self.depends_cache[key] = [value]
|
|
||||||
# only fire events on even percentage boundaries
|
|
||||||
current_progress = cachefile.tell() + previous_progress
|
|
||||||
current_percent = 100 * current_progress / cachesize
|
|
||||||
if current_percent > previous_percent:
|
|
||||||
previous_percent = current_percent
|
|
||||||
bb.event.fire(bb.event.CacheLoadProgress(current_progress, cachesize),
|
|
||||||
self.data)
|
|
||||||
|
|
||||||
previous_progress += current_progress
|
|
||||||
|
|
||||||
# Note: depends cache number is corresponding to the parsing file numbers.
|
# Note: depends cache number is corresponding to the parsing file numbers.
|
||||||
# The same file has several caches, still regarded as one item in the cache
|
# The same file has several caches, still regarded as one item in the cache
|
||||||
@@ -476,33 +361,69 @@ class Cache(NoCache):
|
|||||||
len(self.depends_cache)),
|
len(self.depends_cache)),
|
||||||
self.data)
|
self.data)
|
||||||
|
|
||||||
def parse(self, filename, appends):
|
|
||||||
|
@staticmethod
|
||||||
|
def virtualfn2realfn(virtualfn):
|
||||||
|
"""
|
||||||
|
Convert a virtual file name to a real one + the associated subclass keyword
|
||||||
|
"""
|
||||||
|
|
||||||
|
fn = virtualfn
|
||||||
|
cls = ""
|
||||||
|
if virtualfn.startswith('virtual:'):
|
||||||
|
elems = virtualfn.split(':')
|
||||||
|
cls = ":".join(elems[1:-1])
|
||||||
|
fn = elems[-1]
|
||||||
|
return (fn, cls)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def realfn2virtual(realfn, cls):
|
||||||
|
"""
|
||||||
|
Convert a real filename + the associated subclass keyword to a virtual filename
|
||||||
|
"""
|
||||||
|
if cls == "":
|
||||||
|
return realfn
|
||||||
|
return "virtual:" + cls + ":" + realfn
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def loadDataFull(cls, virtualfn, appends, cfgData):
|
||||||
|
"""
|
||||||
|
Return a complete set of data for fn.
|
||||||
|
To do this, we need to parse the file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
(fn, virtual) = cls.virtualfn2realfn(virtualfn)
|
||||||
|
|
||||||
|
logger.debug(1, "Parsing %s (full)", fn)
|
||||||
|
|
||||||
|
cfgData.setVar("__ONLYFINALISE", virtual or "default")
|
||||||
|
bb_data = cls.load_bbfile(fn, appends, cfgData)
|
||||||
|
return bb_data[virtual]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, filename, appends, configdata, caches_array):
|
||||||
"""Parse the specified filename, returning the recipe information"""
|
"""Parse the specified filename, returning the recipe information"""
|
||||||
logger.debug(1, "Parsing %s", filename)
|
|
||||||
infos = []
|
infos = []
|
||||||
datastores = self.load_bbfile(filename, appends)
|
datastores = cls.load_bbfile(filename, appends, configdata)
|
||||||
depends = []
|
depends = []
|
||||||
variants = []
|
for variant, data in sorted(datastores.iteritems(),
|
||||||
# Process the "real" fn last so we can store variants list
|
|
||||||
for variant, data in sorted(datastores.items(),
|
|
||||||
key=lambda i: i[0],
|
key=lambda i: i[0],
|
||||||
reverse=True):
|
reverse=True):
|
||||||
virtualfn = variant2virtual(filename, variant)
|
virtualfn = cls.realfn2virtual(filename, variant)
|
||||||
variants.append(variant)
|
|
||||||
depends = depends + (data.getVar("__depends", False) or [])
|
depends = depends + (data.getVar("__depends", False) or [])
|
||||||
if depends and not variant:
|
if depends and not variant:
|
||||||
data.setVar("__depends", depends)
|
data.setVar("__depends", depends)
|
||||||
if virtualfn == filename:
|
|
||||||
data.setVar("__VARIANTS", " ".join(variants))
|
|
||||||
info_array = []
|
info_array = []
|
||||||
for cache_class in self.caches_array:
|
for cache_class in caches_array:
|
||||||
info = cache_class(filename, data)
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
info_array.append(info)
|
info = cache_class(filename, data)
|
||||||
|
info_array.append(info)
|
||||||
infos.append((virtualfn, info_array))
|
infos.append((virtualfn, info_array))
|
||||||
|
|
||||||
return infos
|
return infos
|
||||||
|
|
||||||
def load(self, filename, appends):
|
def load(self, filename, appends, configdata):
|
||||||
"""Obtain the recipe information for the specified filename,
|
"""Obtain the recipe information for the specified filename,
|
||||||
using cached values if available, otherwise parsing.
|
using cached values if available, otherwise parsing.
|
||||||
|
|
||||||
@@ -516,20 +437,21 @@ class Cache(NoCache):
|
|||||||
# info_array item is a list of [CoreRecipeInfo, XXXRecipeInfo]
|
# info_array item is a list of [CoreRecipeInfo, XXXRecipeInfo]
|
||||||
info_array = self.depends_cache[filename]
|
info_array = self.depends_cache[filename]
|
||||||
for variant in info_array[0].variants:
|
for variant in info_array[0].variants:
|
||||||
virtualfn = variant2virtual(filename, variant)
|
virtualfn = self.realfn2virtual(filename, variant)
|
||||||
infos.append((virtualfn, self.depends_cache[virtualfn]))
|
infos.append((virtualfn, self.depends_cache[virtualfn]))
|
||||||
else:
|
else:
|
||||||
|
logger.debug(1, "Parsing %s", filename)
|
||||||
return self.parse(filename, appends, configdata, self.caches_array)
|
return self.parse(filename, appends, configdata, self.caches_array)
|
||||||
|
|
||||||
return cached, infos
|
return cached, infos
|
||||||
|
|
||||||
def loadData(self, fn, appends, cacheData):
|
def loadData(self, fn, appends, cfgData, cacheData):
|
||||||
"""Load the recipe info for the specified filename,
|
"""Load the recipe info for the specified filename,
|
||||||
parsing and adding to the cache if necessary, and adding
|
parsing and adding to the cache if necessary, and adding
|
||||||
the recipe information to the supplied CacheData instance."""
|
the recipe information to the supplied CacheData instance."""
|
||||||
skipped, virtuals = 0, 0
|
skipped, virtuals = 0, 0
|
||||||
|
|
||||||
cached, infos = self.load(fn, appends)
|
cached, infos = self.load(fn, appends, cfgData)
|
||||||
for virtualfn, info_array in infos:
|
for virtualfn, info_array in infos:
|
||||||
if info_array[0].skipped:
|
if info_array[0].skipped:
|
||||||
logger.debug(1, "Skipping %s: %s", virtualfn, info_array[0].skipreason)
|
logger.debug(1, "Skipping %s: %s", virtualfn, info_array[0].skipreason)
|
||||||
@@ -637,19 +559,16 @@ class Cache(NoCache):
|
|||||||
|
|
||||||
invalid = False
|
invalid = False
|
||||||
for cls in info_array[0].variants:
|
for cls in info_array[0].variants:
|
||||||
virtualfn = variant2virtual(fn, cls)
|
virtualfn = self.realfn2virtual(fn, cls)
|
||||||
self.clean.add(virtualfn)
|
self.clean.add(virtualfn)
|
||||||
if virtualfn not in self.depends_cache:
|
if virtualfn not in self.depends_cache:
|
||||||
logger.debug(2, "Cache: %s is not cached", virtualfn)
|
logger.debug(2, "Cache: %s is not cached", virtualfn)
|
||||||
invalid = True
|
invalid = True
|
||||||
elif len(self.depends_cache[virtualfn]) != len(self.caches_array):
|
|
||||||
logger.debug(2, "Cache: Extra caches missing for %s?" % virtualfn)
|
|
||||||
invalid = True
|
|
||||||
|
|
||||||
# If any one of the variants is not present, mark as invalid for all
|
# If any one of the variants is not present, mark as invalid for all
|
||||||
if invalid:
|
if invalid:
|
||||||
for cls in info_array[0].variants:
|
for cls in info_array[0].variants:
|
||||||
virtualfn = variant2virtual(fn, cls)
|
virtualfn = self.realfn2virtual(fn, cls)
|
||||||
if virtualfn in self.clean:
|
if virtualfn in self.clean:
|
||||||
logger.debug(2, "Cache: Removing %s from cache", virtualfn)
|
logger.debug(2, "Cache: Removing %s from cache", virtualfn)
|
||||||
self.clean.remove(virtualfn)
|
self.clean.remove(virtualfn)
|
||||||
@@ -686,19 +605,30 @@ class Cache(NoCache):
|
|||||||
logger.debug(2, "Cache is clean, not saving.")
|
logger.debug(2, "Cache is clean, not saving.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
file_dict = {}
|
||||||
|
pickler_dict = {}
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
cache_class_name = cache_class.__name__
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
cache_class_name = cache_class.__name__
|
||||||
with open(cachefile, "wb") as f:
|
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||||
p = pickle.Pickler(f, pickle.HIGHEST_PROTOCOL)
|
file_dict[cache_class_name] = open(cachefile, "wb")
|
||||||
p.dump(__cache_version__)
|
pickler_dict[cache_class_name] = pickle.Pickler(file_dict[cache_class_name], pickle.HIGHEST_PROTOCOL)
|
||||||
p.dump(bb.__version__)
|
|
||||||
|
pickler_dict['CoreRecipeInfo'].dump(__cache_version__)
|
||||||
|
pickler_dict['CoreRecipeInfo'].dump(bb.__version__)
|
||||||
|
|
||||||
for key, info_array in self.depends_cache.items():
|
try:
|
||||||
for info in info_array:
|
for key, info_array in self.depends_cache.iteritems():
|
||||||
if isinstance(info, RecipeInfoCommon) and info.__class__.__name__ == cache_class_name:
|
for info in info_array:
|
||||||
p.dump(key)
|
if isinstance(info, RecipeInfoCommon):
|
||||||
p.dump(info)
|
cache_class_name = info.__class__.__name__
|
||||||
|
pickler_dict[cache_class_name].dump(key)
|
||||||
|
pickler_dict[cache_class_name].dump(info)
|
||||||
|
finally:
|
||||||
|
for cache_class in self.caches_array:
|
||||||
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
|
cache_class_name = cache_class.__name__
|
||||||
|
file_dict[cache_class_name].close()
|
||||||
|
|
||||||
del self.depends_cache
|
del self.depends_cache
|
||||||
|
|
||||||
@@ -726,13 +656,50 @@ class Cache(NoCache):
|
|||||||
Save data we need into the cache
|
Save data we need into the cache
|
||||||
"""
|
"""
|
||||||
|
|
||||||
realfn = virtualfn2realfn(file_name)[0]
|
realfn = self.virtualfn2realfn(file_name)[0]
|
||||||
|
|
||||||
info_array = []
|
info_array = []
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
info_array.append(cache_class(realfn, data))
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
|
info_array.append(cache_class(realfn, data))
|
||||||
self.add_info(file_name, info_array, cacheData, parsed)
|
self.add_info(file_name, info_array, cacheData, parsed)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_bbfile(bbfile, appends, config):
|
||||||
|
"""
|
||||||
|
Load and parse one .bb build file
|
||||||
|
Return the data and whether parsing resulted in the file being skipped
|
||||||
|
"""
|
||||||
|
chdir_back = False
|
||||||
|
|
||||||
|
from bb import parse
|
||||||
|
|
||||||
|
# expand tmpdir to include this topdir
|
||||||
|
config.setVar('TMPDIR', config.getVar('TMPDIR', True) or "")
|
||||||
|
bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
|
||||||
|
oldpath = os.path.abspath(os.getcwd())
|
||||||
|
parse.cached_mtime_noerror(bbfile_loc)
|
||||||
|
bb_data = config.createCopy()
|
||||||
|
# The ConfHandler first looks if there is a TOPDIR and if not
|
||||||
|
# then it would call getcwd().
|
||||||
|
# Previously, we chdir()ed to bbfile_loc, called the handler
|
||||||
|
# and finally chdir()ed back, a couple of thousand times. We now
|
||||||
|
# just fill in TOPDIR to point to bbfile_loc if there is no TOPDIR yet.
|
||||||
|
if not bb_data.getVar('TOPDIR', False):
|
||||||
|
chdir_back = True
|
||||||
|
bb_data.setVar('TOPDIR', bbfile_loc)
|
||||||
|
try:
|
||||||
|
if appends:
|
||||||
|
bb_data.setVar('__BBAPPEND', " ".join(appends))
|
||||||
|
bb_data = parse.handle(bbfile, bb_data)
|
||||||
|
if chdir_back:
|
||||||
|
os.chdir(oldpath)
|
||||||
|
return bb_data
|
||||||
|
except:
|
||||||
|
if chdir_back:
|
||||||
|
os.chdir(oldpath)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def init(cooker):
|
def init(cooker):
|
||||||
"""
|
"""
|
||||||
@@ -762,9 +729,8 @@ class CacheData(object):
|
|||||||
def __init__(self, caches_array):
|
def __init__(self, caches_array):
|
||||||
self.caches_array = caches_array
|
self.caches_array = caches_array
|
||||||
for cache_class in self.caches_array:
|
for cache_class in self.caches_array:
|
||||||
if not issubclass(cache_class, RecipeInfoCommon):
|
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||||
bb.error("Extra cache data class %s should subclass RecipeInfoCommon class" % cache_class)
|
cache_class.init_cacheData(self)
|
||||||
cache_class.init_cacheData(self)
|
|
||||||
|
|
||||||
# Direct cache variables
|
# Direct cache variables
|
||||||
self.task_queues = {}
|
self.task_queues = {}
|
||||||
@@ -791,14 +757,13 @@ class MultiProcessCache(object):
|
|||||||
self.cachedata = self.create_cachedata()
|
self.cachedata = self.create_cachedata()
|
||||||
self.cachedata_extras = self.create_cachedata()
|
self.cachedata_extras = self.create_cachedata()
|
||||||
|
|
||||||
def init_cache(self, d, cache_file_name=None):
|
def init_cache(self, d):
|
||||||
cachedir = (d.getVar("PERSISTENT_DIR", True) or
|
cachedir = (d.getVar("PERSISTENT_DIR", True) or
|
||||||
d.getVar("CACHE", True))
|
d.getVar("CACHE", True))
|
||||||
if cachedir in [None, '']:
|
if cachedir in [None, '']:
|
||||||
return
|
return
|
||||||
bb.utils.mkdirhier(cachedir)
|
bb.utils.mkdirhier(cachedir)
|
||||||
self.cachefile = os.path.join(cachedir,
|
self.cachefile = os.path.join(cachedir, self.__class__.cache_file_name)
|
||||||
cache_file_name or self.__class__.cache_file_name)
|
|
||||||
logger.debug(1, "Using cache in '%s'", self.cachefile)
|
logger.debug(1, "Using cache in '%s'", self.cachefile)
|
||||||
|
|
||||||
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
||||||
@@ -822,7 +787,7 @@ class MultiProcessCache(object):
|
|||||||
data = [{}]
|
data = [{}]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def save_extras(self):
|
def save_extras(self, d):
|
||||||
if not self.cachefile:
|
if not self.cachefile:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -852,7 +817,7 @@ class MultiProcessCache(object):
|
|||||||
if h not in dest[j]:
|
if h not in dest[j]:
|
||||||
dest[j][h] = source[j][h]
|
dest[j][h] = source[j][h]
|
||||||
|
|
||||||
def save_merge(self):
|
def save_merge(self, d):
|
||||||
if not self.cachefile:
|
if not self.cachefile:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,22 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import glob
|
|
||||||
import operator
|
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import pickle
|
|
||||||
import bb.utils
|
import bb.utils
|
||||||
import logging
|
import logging
|
||||||
from bb.cache import MultiProcessCache
|
from bb.cache import MultiProcessCache
|
||||||
|
|
||||||
logger = logging.getLogger("BitBake.Cache")
|
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)
|
# mtime cache (non-persistent)
|
||||||
# based upon the assumption that files do not change during bitbake run
|
# based upon the assumption that files do not change during bitbake run
|
||||||
class FileMtimeCache(object):
|
class FileMtimeCache(object):
|
||||||
@@ -83,52 +88,3 @@ class FileChecksumCache(MultiProcessCache):
|
|||||||
dest[0][h] = source[0][h]
|
dest[0][h] = source[0][h]
|
||||||
else:
|
else:
|
||||||
dest[0][h] = source[0][h]
|
dest[0][h] = source[0][h]
|
||||||
|
|
||||||
def get_checksums(self, filelist, pn):
|
|
||||||
"""Get checksums for a list of files"""
|
|
||||||
|
|
||||||
def checksum_file(f):
|
|
||||||
try:
|
|
||||||
checksum = self.get_checksum(f)
|
|
||||||
except OSError as e:
|
|
||||||
bb.warn("Unable to get checksum for %s SRC_URI entry %s: %s" % (pn, os.path.basename(f), e))
|
|
||||||
return None
|
|
||||||
return checksum
|
|
||||||
|
|
||||||
def checksum_dir(pth):
|
|
||||||
# Handle directories recursively
|
|
||||||
dirchecksums = []
|
|
||||||
for root, dirs, files in os.walk(pth):
|
|
||||||
for name in files:
|
|
||||||
fullpth = os.path.join(root, name)
|
|
||||||
checksum = checksum_file(fullpth)
|
|
||||||
if checksum:
|
|
||||||
dirchecksums.append((fullpth, checksum))
|
|
||||||
return dirchecksums
|
|
||||||
|
|
||||||
checksums = []
|
|
||||||
for pth in filelist.split():
|
|
||||||
exist = pth.split(":")[1]
|
|
||||||
if exist == "False":
|
|
||||||
continue
|
|
||||||
pth = pth.split(":")[0]
|
|
||||||
if '*' in pth:
|
|
||||||
# Handle globs
|
|
||||||
for f in glob.glob(pth):
|
|
||||||
if os.path.isdir(f):
|
|
||||||
if not os.path.islink(f):
|
|
||||||
checksums.extend(checksum_dir(f))
|
|
||||||
else:
|
|
||||||
checksum = checksum_file(f)
|
|
||||||
if checksum:
|
|
||||||
checksums.append((f, checksum))
|
|
||||||
elif os.path.isdir(pth):
|
|
||||||
if not os.path.islink(pth):
|
|
||||||
checksums.extend(checksum_dir(pth))
|
|
||||||
else:
|
|
||||||
checksum = checksum_file(pth)
|
|
||||||
if checksum:
|
|
||||||
checksums.append((pth, checksum))
|
|
||||||
|
|
||||||
checksums.sort(key=operator.itemgetter(1))
|
|
||||||
return checksums
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import ast
|
import ast
|
||||||
import sys
|
|
||||||
import codegen
|
import codegen
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
import bb.pysh as pysh
|
|
||||||
import os.path
|
import os.path
|
||||||
import bb.utils, bb.data
|
import bb.utils, bb.data
|
||||||
import hashlib
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from bb.pysh import pyshyacc, pyshlex, sherrors
|
from pysh import pyshyacc, pyshlex, sherrors
|
||||||
from bb.cache import MultiProcessCache
|
from bb.cache import MultiProcessCache
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('BitBake.CodeParser')
|
logger = logging.getLogger('BitBake.CodeParser')
|
||||||
|
|
||||||
def bbhash(s):
|
try:
|
||||||
return hashlib.md5(s.encode("utf-8")).hexdigest()
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
logger.info('Importing cPickle failed. Falling back to a very slow implementation.')
|
||||||
|
|
||||||
|
|
||||||
def check_indent(codestr):
|
def check_indent(codestr):
|
||||||
"""If the code is indented, add a top level piece of code to 'remove' the indentation"""
|
"""If the code is indented, add a top level piece of code to 'remove' the indentation"""
|
||||||
@@ -27,10 +28,6 @@ def check_indent(codestr):
|
|||||||
return codestr
|
return codestr
|
||||||
|
|
||||||
if codestr[i-1] == "\t" or codestr[i-1] == " ":
|
if codestr[i-1] == "\t" or codestr[i-1] == " ":
|
||||||
if codestr[0] == "\n":
|
|
||||||
# Since we're adding a line, we need to remove one line of any empty padding
|
|
||||||
# to ensure line numbers are correct
|
|
||||||
codestr = codestr[1:]
|
|
||||||
return "if 1:\n" + codestr
|
return "if 1:\n" + codestr
|
||||||
|
|
||||||
return codestr
|
return codestr
|
||||||
@@ -67,12 +64,11 @@ class SetCache(object):
|
|||||||
|
|
||||||
new = []
|
new = []
|
||||||
for i in items:
|
for i in items:
|
||||||
new.append(sys.intern(i))
|
new.append(intern(i))
|
||||||
s = frozenset(new)
|
s = frozenset(new)
|
||||||
h = hash(s)
|
if hash(s) in self.setcache:
|
||||||
if h in self.setcache:
|
return self.setcache[hash(s)]
|
||||||
return self.setcache[h]
|
self.setcache[hash(s)] = s
|
||||||
self.setcache[h] = s
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
codecache = SetCache()
|
codecache = SetCache()
|
||||||
@@ -117,7 +113,7 @@ class shellCacheLine(object):
|
|||||||
|
|
||||||
class CodeParserCache(MultiProcessCache):
|
class CodeParserCache(MultiProcessCache):
|
||||||
cache_file_name = "bb_codeparser.dat"
|
cache_file_name = "bb_codeparser.dat"
|
||||||
CACHE_VERSION = 8
|
CACHE_VERSION = 7
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
MultiProcessCache.__init__(self)
|
MultiProcessCache.__init__(self)
|
||||||
@@ -148,10 +144,6 @@ class CodeParserCache(MultiProcessCache):
|
|||||||
return cacheline
|
return cacheline
|
||||||
|
|
||||||
def init_cache(self, d):
|
def init_cache(self, d):
|
||||||
# Check if we already have the caches
|
|
||||||
if self.pythoncache:
|
|
||||||
return
|
|
||||||
|
|
||||||
MultiProcessCache.init_cache(self, d)
|
MultiProcessCache.init_cache(self, d)
|
||||||
|
|
||||||
# cachedata gets re-assigned in the parent
|
# cachedata gets re-assigned in the parent
|
||||||
@@ -167,11 +159,11 @@ codeparsercache = CodeParserCache()
|
|||||||
def parser_cache_init(d):
|
def parser_cache_init(d):
|
||||||
codeparsercache.init_cache(d)
|
codeparsercache.init_cache(d)
|
||||||
|
|
||||||
def parser_cache_save():
|
def parser_cache_save(d):
|
||||||
codeparsercache.save_extras()
|
codeparsercache.save_extras(d)
|
||||||
|
|
||||||
def parser_cache_savemerge():
|
def parser_cache_savemerge(d):
|
||||||
codeparsercache.save_merge()
|
codeparsercache.save_merge(d)
|
||||||
|
|
||||||
Logger = logging.getLoggerClass()
|
Logger = logging.getLoggerClass()
|
||||||
class BufferedLogger(Logger):
|
class BufferedLogger(Logger):
|
||||||
@@ -191,7 +183,6 @@ class BufferedLogger(Logger):
|
|||||||
|
|
||||||
class PythonParser():
|
class PythonParser():
|
||||||
getvars = (".getVar", ".appendVar", ".prependVar")
|
getvars = (".getVar", ".appendVar", ".prependVar")
|
||||||
getvarflags = (".getVarFlag", ".appendVarFlag", ".prependVarFlag")
|
|
||||||
containsfuncs = ("bb.utils.contains", "base_contains", "bb.utils.contains_any")
|
containsfuncs = ("bb.utils.contains", "base_contains", "bb.utils.contains_any")
|
||||||
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
|
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
|
||||||
|
|
||||||
@@ -211,33 +202,17 @@ class PythonParser():
|
|||||||
|
|
||||||
def visit_Call(self, node):
|
def visit_Call(self, node):
|
||||||
name = self.called_node_name(node.func)
|
name = self.called_node_name(node.func)
|
||||||
if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs):
|
if name and name.endswith(self.getvars) or name in self.containsfuncs:
|
||||||
if isinstance(node.args[0], ast.Str):
|
if isinstance(node.args[0], ast.Str):
|
||||||
varname = node.args[0].s
|
varname = node.args[0].s
|
||||||
if name in self.containsfuncs and isinstance(node.args[1], ast.Str):
|
if name in self.containsfuncs and isinstance(node.args[1], ast.Str):
|
||||||
if varname not in self.contains:
|
if varname not in self.contains:
|
||||||
self.contains[varname] = set()
|
self.contains[varname] = set()
|
||||||
self.contains[varname].add(node.args[1].s)
|
self.contains[varname].add(node.args[1].s)
|
||||||
elif name.endswith(self.getvarflags):
|
else:
|
||||||
if isinstance(node.args[1], ast.Str):
|
self.references.add(node.args[0].s)
|
||||||
self.references.add('%s[%s]' % (varname, node.args[1].s))
|
|
||||||
else:
|
|
||||||
self.warn(node.func, node.args[1])
|
|
||||||
else:
|
|
||||||
self.references.add(varname)
|
|
||||||
else:
|
else:
|
||||||
self.warn(node.func, node.args[0])
|
self.warn(node.func, node.args[0])
|
||||||
elif name and name.endswith(".expand"):
|
|
||||||
if isinstance(node.args[0], ast.Str):
|
|
||||||
value = node.args[0].s
|
|
||||||
d = bb.data.init()
|
|
||||||
parser = d.expandWithRefs(value, self.name)
|
|
||||||
self.references |= parser.references
|
|
||||||
self.execs |= parser.execs
|
|
||||||
for varname in parser.contains:
|
|
||||||
if varname not in self.contains:
|
|
||||||
self.contains[varname] = set()
|
|
||||||
self.contains[varname] |= parser.contains[varname]
|
|
||||||
elif name in self.execfuncs:
|
elif name in self.execfuncs:
|
||||||
if isinstance(node.args[0], ast.Str):
|
if isinstance(node.args[0], ast.Str):
|
||||||
self.var_execs.add(node.args[0].s)
|
self.var_execs.add(node.args[0].s)
|
||||||
@@ -260,7 +235,6 @@ class PythonParser():
|
|||||||
break
|
break
|
||||||
|
|
||||||
def __init__(self, name, log):
|
def __init__(self, name, log):
|
||||||
self.name = name
|
|
||||||
self.var_execs = set()
|
self.var_execs = set()
|
||||||
self.contains = {}
|
self.contains = {}
|
||||||
self.execs = set()
|
self.execs = set()
|
||||||
@@ -270,11 +244,11 @@ class PythonParser():
|
|||||||
self.unhandled_message = "in call of %s, argument '%s' is not a string literal"
|
self.unhandled_message = "in call of %s, argument '%s' is not a string literal"
|
||||||
self.unhandled_message = "while parsing %s, %s" % (name, self.unhandled_message)
|
self.unhandled_message = "while parsing %s, %s" % (name, self.unhandled_message)
|
||||||
|
|
||||||
def parse_python(self, node, lineno=0, filename="<string>"):
|
def parse_python(self, node):
|
||||||
if not node or not node.strip():
|
if not node or not node.strip():
|
||||||
return
|
return
|
||||||
|
|
||||||
h = bbhash(str(node))
|
h = hash(str(node))
|
||||||
|
|
||||||
if h in codeparsercache.pythoncache:
|
if h in codeparsercache.pythoncache:
|
||||||
self.references = set(codeparsercache.pythoncache[h].refs)
|
self.references = set(codeparsercache.pythoncache[h].refs)
|
||||||
@@ -292,9 +266,7 @@ class PythonParser():
|
|||||||
self.contains[i] = set(codeparsercache.pythoncacheextras[h].contains[i])
|
self.contains[i] = set(codeparsercache.pythoncacheextras[h].contains[i])
|
||||||
return
|
return
|
||||||
|
|
||||||
# We can't add to the linenumbers for compile, we can pad to the correct number of blank lines though
|
code = compile(check_indent(str(node)), "<string>", "exec",
|
||||||
node = "\n" * int(lineno) + node
|
|
||||||
code = compile(check_indent(str(node)), filename, "exec",
|
|
||||||
ast.PyCF_ONLY_AST)
|
ast.PyCF_ONLY_AST)
|
||||||
|
|
||||||
for n in ast.walk(code):
|
for n in ast.walk(code):
|
||||||
@@ -319,7 +291,7 @@ class ShellParser():
|
|||||||
commands it executes.
|
commands it executes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
h = bbhash(str(value))
|
h = hash(str(value))
|
||||||
|
|
||||||
if h in codeparsercache.shellcache:
|
if h in codeparsercache.shellcache:
|
||||||
self.execs = set(codeparsercache.shellcache[h].execs)
|
self.execs = set(codeparsercache.shellcache[h].execs)
|
||||||
@@ -342,7 +314,8 @@ class ShellParser():
|
|||||||
except pyshlex.NeedMore:
|
except pyshlex.NeedMore:
|
||||||
raise sherrors.ShellSyntaxError("Unexpected EOF")
|
raise sherrors.ShellSyntaxError("Unexpected EOF")
|
||||||
|
|
||||||
self.process_tokens(tokens)
|
for token in tokens:
|
||||||
|
self.process_tokens(token)
|
||||||
|
|
||||||
def process_tokens(self, tokens):
|
def process_tokens(self, tokens):
|
||||||
"""Process a supplied portion of the syntax tree as returned by
|
"""Process a supplied portion of the syntax tree as returned by
|
||||||
@@ -388,24 +361,18 @@ class ShellParser():
|
|||||||
"case_clause": case_clause,
|
"case_clause": case_clause,
|
||||||
}
|
}
|
||||||
|
|
||||||
def process_token_list(tokens):
|
for token in tokens:
|
||||||
for token in tokens:
|
name, value = token
|
||||||
if isinstance(token, list):
|
try:
|
||||||
process_token_list(token)
|
more_tokens, words = token_handlers[name](value)
|
||||||
continue
|
except KeyError:
|
||||||
name, value = token
|
raise NotImplementedError("Unsupported token type " + name)
|
||||||
try:
|
|
||||||
more_tokens, words = token_handlers[name](value)
|
|
||||||
except KeyError:
|
|
||||||
raise NotImplementedError("Unsupported token type " + name)
|
|
||||||
|
|
||||||
if more_tokens:
|
if more_tokens:
|
||||||
self.process_tokens(more_tokens)
|
self.process_tokens(more_tokens)
|
||||||
|
|
||||||
if words:
|
if words:
|
||||||
self.process_words(words)
|
self.process_words(words)
|
||||||
|
|
||||||
process_token_list(tokens)
|
|
||||||
|
|
||||||
def process_words(self, words):
|
def process_words(self, words):
|
||||||
"""Process a set of 'words' in pyshyacc parlance, which includes
|
"""Process a set of 'words' in pyshyacc parlance, which includes
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class Command:
|
|||||||
return False
|
return False
|
||||||
except SystemExit as exc:
|
except SystemExit as exc:
|
||||||
arg = exc.args[0]
|
arg = exc.args[0]
|
||||||
if isinstance(arg, str):
|
if isinstance(arg, basestring):
|
||||||
self.finishAsyncCommand(arg)
|
self.finishAsyncCommand(arg)
|
||||||
else:
|
else:
|
||||||
self.finishAsyncCommand("Exited with %s" % arg)
|
self.finishAsyncCommand("Exited with %s" % arg)
|
||||||
@@ -279,7 +279,6 @@ class CommandsSync:
|
|||||||
mask = params[3]
|
mask = params[3]
|
||||||
return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
|
return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
|
||||||
setEventMask.needconfig = False
|
setEventMask.needconfig = False
|
||||||
setEventMask.readonly = True
|
|
||||||
|
|
||||||
def setFeatures(self, command, params):
|
def setFeatures(self, command, params):
|
||||||
"""
|
"""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,11 +22,9 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import logging
|
import os, sys
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
import logging
|
||||||
import bb
|
import bb
|
||||||
from bb import data
|
from bb import data
|
||||||
import bb.parse
|
import bb.parse
|
||||||
@@ -139,7 +137,6 @@ class CookerConfiguration(object):
|
|||||||
self.force = False
|
self.force = False
|
||||||
self.profile = False
|
self.profile = False
|
||||||
self.nosetscene = False
|
self.nosetscene = False
|
||||||
self.setsceneonly = False
|
|
||||||
self.invalidate_stamp = False
|
self.invalidate_stamp = False
|
||||||
self.dump_signatures = []
|
self.dump_signatures = []
|
||||||
self.dry_run = False
|
self.dry_run = False
|
||||||
@@ -184,7 +181,7 @@ def catch_parse_error(func):
|
|||||||
parselog.critical(traceback.format_exc())
|
parselog.critical(traceback.format_exc())
|
||||||
parselog.critical("Unable to parse %s: %s" % (fn, exc))
|
parselog.critical("Unable to parse %s: %s" % (fn, exc))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except bb.data_smart.ExpansionError as exc:
|
except (bb.parse.ParseError, bb.data_smart.ExpansionError) as exc:
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
bbdir = os.path.dirname(__file__) + os.sep
|
bbdir = os.path.dirname(__file__) + os.sep
|
||||||
@@ -194,10 +191,7 @@ def catch_parse_error(func):
|
|||||||
fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
|
fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
|
||||||
if not fn.startswith(bbdir):
|
if not fn.startswith(bbdir):
|
||||||
break
|
break
|
||||||
parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb))
|
parselog.critical("Unable to parse %s", fn, exc_info=(exc_class, exc, tb))
|
||||||
sys.exit(1)
|
|
||||||
except bb.parse.ParseError as exc:
|
|
||||||
parselog.critical(str(exc))
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
@@ -237,9 +231,9 @@ class CookerDataBuilder(object):
|
|||||||
|
|
||||||
bb.utils.set_context(bb.utils.clean_context())
|
bb.utils.set_context(bb.utils.clean_context())
|
||||||
bb.event.set_class_handlers(bb.event.clean_class_handlers())
|
bb.event.set_class_handlers(bb.event.clean_class_handlers())
|
||||||
self.basedata = bb.data.init()
|
self.data = bb.data.init()
|
||||||
if self.tracking:
|
if self.tracking:
|
||||||
self.basedata.enableTracking()
|
self.data.enableTracking()
|
||||||
|
|
||||||
# Keep a datastore of the initial environment variables and their
|
# Keep a datastore of the initial environment variables and their
|
||||||
# values from when BitBake was launched to enable child processes
|
# values from when BitBake was launched to enable child processes
|
||||||
@@ -250,49 +244,16 @@ class CookerDataBuilder(object):
|
|||||||
self.savedenv.setVar(k, cookercfg.env[k])
|
self.savedenv.setVar(k, cookercfg.env[k])
|
||||||
|
|
||||||
filtered_keys = bb.utils.approved_variables()
|
filtered_keys = bb.utils.approved_variables()
|
||||||
bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys)
|
bb.data.inheritFromOS(self.data, self.savedenv, filtered_keys)
|
||||||
self.basedata.setVar("BB_ORIGENV", self.savedenv)
|
self.data.setVar("BB_ORIGENV", self.savedenv)
|
||||||
|
|
||||||
if worker:
|
if worker:
|
||||||
self.basedata.setVar("BB_WORKERCONTEXT", "1")
|
self.data.setVar("BB_WORKERCONTEXT", "1")
|
||||||
|
|
||||||
self.data = self.basedata
|
|
||||||
self.mcdata = {}
|
|
||||||
|
|
||||||
def parseBaseConfiguration(self):
|
def parseBaseConfiguration(self):
|
||||||
try:
|
try:
|
||||||
bb.parse.init_parser(self.basedata)
|
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||||
self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
except SyntaxError:
|
||||||
|
|
||||||
if self.data.getVar("BB_WORKERCONTEXT", False) is None:
|
|
||||||
bb.fetch.fetcher_init(self.data)
|
|
||||||
bb.codeparser.parser_cache_init(self.data)
|
|
||||||
|
|
||||||
bb.event.fire(bb.event.ConfigParsed(), self.data)
|
|
||||||
|
|
||||||
reparse_cnt = 0
|
|
||||||
while self.data.getVar("BB_INVALIDCONF", False) is True:
|
|
||||||
if reparse_cnt > 20:
|
|
||||||
logger.error("Configuration has been re-parsed over 20 times, "
|
|
||||||
"breaking out of the loop...")
|
|
||||||
raise Exception("Too deep config re-parse loop. Check locations where "
|
|
||||||
"BB_INVALIDCONF is being set (ConfigParsed event handlers)")
|
|
||||||
self.data.setVar("BB_INVALIDCONF", False)
|
|
||||||
self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
|
||||||
reparse_cnt += 1
|
|
||||||
bb.event.fire(bb.event.ConfigParsed(), self.data)
|
|
||||||
|
|
||||||
bb.parse.init_parser(self.data)
|
|
||||||
self.data_hash = self.data.get_hash()
|
|
||||||
self.mcdata[''] = self.data
|
|
||||||
|
|
||||||
multiconfig = (self.data.getVar("BBMULTICONFIG", True) or "").split()
|
|
||||||
for config in multiconfig:
|
|
||||||
mcdata = self.parseConfigurationFiles(self.prefiles, self.postfiles, config)
|
|
||||||
bb.event.fire(bb.event.ConfigParsed(), mcdata)
|
|
||||||
self.mcdata[config] = mcdata
|
|
||||||
|
|
||||||
except (SyntaxError, bb.BBHandledException):
|
|
||||||
raise bb.BBHandledException
|
raise bb.BBHandledException
|
||||||
except bb.data_smart.ExpansionError as e:
|
except bb.data_smart.ExpansionError as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
@@ -304,9 +265,9 @@ class CookerDataBuilder(object):
|
|||||||
def _findLayerConf(self, data):
|
def _findLayerConf(self, data):
|
||||||
return findConfigFile("bblayers.conf", data)
|
return findConfigFile("bblayers.conf", data)
|
||||||
|
|
||||||
def parseConfigurationFiles(self, prefiles, postfiles, mc = "default"):
|
def parseConfigurationFiles(self, prefiles, postfiles):
|
||||||
data = bb.data.createCopy(self.basedata)
|
data = self.data
|
||||||
data.setVar("BB_CURRENT_MC", mc)
|
bb.parse.init_parser(data)
|
||||||
|
|
||||||
# Parse files for loading *before* bitbake.conf and any includes
|
# Parse files for loading *before* bitbake.conf and any includes
|
||||||
for f in prefiles:
|
for f in prefiles:
|
||||||
@@ -325,22 +286,13 @@ class CookerDataBuilder(object):
|
|||||||
data = bb.data.createCopy(data)
|
data = bb.data.createCopy(data)
|
||||||
approved = bb.utils.approved_variables()
|
approved = bb.utils.approved_variables()
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
if not os.path.isdir(layer):
|
|
||||||
parselog.critical("Layer directory '%s' does not exist! "
|
|
||||||
"Please check BBLAYERS in %s" % (layer, layerconf))
|
|
||||||
sys.exit(1)
|
|
||||||
parselog.debug(2, "Adding layer %s", layer)
|
parselog.debug(2, "Adding layer %s", layer)
|
||||||
if 'HOME' in approved and '~' in layer:
|
if 'HOME' in approved and '~' in layer:
|
||||||
layer = os.path.expanduser(layer)
|
layer = os.path.expanduser(layer)
|
||||||
if layer.endswith('/'):
|
|
||||||
layer = layer.rstrip('/')
|
|
||||||
data.setVar('LAYERDIR', layer)
|
data.setVar('LAYERDIR', layer)
|
||||||
data.setVar('LAYERDIR_RE', re.escape(layer))
|
|
||||||
data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
|
data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
|
||||||
data.expandVarref('LAYERDIR')
|
data.expandVarref('LAYERDIR')
|
||||||
data.expandVarref('LAYERDIR_RE')
|
|
||||||
|
|
||||||
data.delVar('LAYERDIR_RE')
|
|
||||||
data.delVar('LAYERDIR')
|
data.delVar('LAYERDIR')
|
||||||
|
|
||||||
if not data.getVar("BBPATH", True):
|
if not data.getVar("BBPATH", True):
|
||||||
@@ -365,14 +317,22 @@ class CookerDataBuilder(object):
|
|||||||
# Nomally we only register event handlers at the end of parsing .bb files
|
# Nomally we only register event handlers at the end of parsing .bb files
|
||||||
# We register any handlers we've found so far here...
|
# We register any handlers we've found so far here...
|
||||||
for var in data.getVar('__BBHANDLERS', False) or []:
|
for var in data.getVar('__BBHANDLERS', False) or []:
|
||||||
handlerfn = data.getVarFlag(var, "filename", False)
|
bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask", True) or "").split())
|
||||||
if not handlerfn:
|
|
||||||
parselog.critical("Undefined event handler function '%s'" % var)
|
|
||||||
sys.exit(1)
|
|
||||||
handlerln = int(data.getVarFlag(var, "lineno", False))
|
|
||||||
bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
|
|
||||||
|
|
||||||
|
if data.getVar("BB_WORKERCONTEXT", False) is None:
|
||||||
|
bb.fetch.fetcher_init(data)
|
||||||
|
bb.codeparser.parser_cache_init(data)
|
||||||
|
bb.event.fire(bb.event.ConfigParsed(), data)
|
||||||
|
|
||||||
|
if data.getVar("BB_INVALIDCONF", False) is True:
|
||||||
|
data.setVar("BB_INVALIDCONF", False)
|
||||||
|
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||||
|
return
|
||||||
|
|
||||||
|
bb.parse.init_parser(data)
|
||||||
data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
|
data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
|
||||||
|
self.data = data
|
||||||
|
self.data_hash = data.get_hash()
|
||||||
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|||||||
@@ -178,8 +178,8 @@ def createDaemon(function, logfile):
|
|||||||
# os.dup2(0, 2) # standard error (2)
|
# os.dup2(0, 2) # standard error (2)
|
||||||
|
|
||||||
|
|
||||||
si = open('/dev/null', 'r')
|
si = file('/dev/null', 'r')
|
||||||
so = open(logfile, 'w')
|
so = file(logfile, 'w')
|
||||||
se = so
|
se = so
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ def setVarFlag(var, flag, flagvalue, d):
|
|||||||
|
|
||||||
def getVarFlag(var, flag, d):
|
def getVarFlag(var, flag, d):
|
||||||
"""Gets given flag from given var"""
|
"""Gets given flag from given var"""
|
||||||
return d.getVarFlag(var, flag, False)
|
return d.getVarFlag(var, flag)
|
||||||
|
|
||||||
def delVarFlag(var, flag, d):
|
def delVarFlag(var, flag, d):
|
||||||
"""Removes a given flag from the variable's flags"""
|
"""Removes a given flag from the variable's flags"""
|
||||||
@@ -182,12 +182,12 @@ def inheritFromOS(d, savedenv, permitted):
|
|||||||
|
|
||||||
def emit_var(var, o=sys.__stdout__, d = init(), all=False):
|
def emit_var(var, o=sys.__stdout__, d = init(), all=False):
|
||||||
"""Emit a variable to be sourced by a shell."""
|
"""Emit a variable to be sourced by a shell."""
|
||||||
func = d.getVarFlag(var, "func", False)
|
if d.getVarFlag(var, "python"):
|
||||||
if d.getVarFlag(var, 'python', False) and func:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
export = d.getVarFlag(var, "export", False)
|
export = d.getVarFlag(var, "export")
|
||||||
unexport = d.getVarFlag(var, "unexport", False)
|
unexport = d.getVarFlag(var, "unexport")
|
||||||
|
func = d.getVarFlag(var, "func")
|
||||||
if not all and not export and not unexport and not func:
|
if not all and not export and not unexport and not func:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -227,7 +227,6 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
|
|||||||
|
|
||||||
if func:
|
if func:
|
||||||
# NOTE: should probably check for unbalanced {} within the var
|
# NOTE: should probably check for unbalanced {} within the var
|
||||||
val = val.rstrip('\n')
|
|
||||||
o.write("%s() {\n%s\n}\n" % (varExpanded, val))
|
o.write("%s() {\n%s\n}\n" % (varExpanded, val))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -245,7 +244,7 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
|
|||||||
def emit_env(o=sys.__stdout__, d = init(), all=False):
|
def emit_env(o=sys.__stdout__, d = init(), all=False):
|
||||||
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
||||||
|
|
||||||
isfunc = lambda key: bool(d.getVarFlag(key, "func", False))
|
isfunc = lambda key: bool(d.getVarFlag(key, "func"))
|
||||||
keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
|
keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
|
||||||
grouped = groupby(keys, isfunc)
|
grouped = groupby(keys, isfunc)
|
||||||
for isfunc, keys in grouped:
|
for isfunc, keys in grouped:
|
||||||
@@ -254,17 +253,15 @@ def emit_env(o=sys.__stdout__, d = init(), all=False):
|
|||||||
|
|
||||||
def exported_keys(d):
|
def exported_keys(d):
|
||||||
return (key for key in d.keys() if not key.startswith('__') and
|
return (key for key in d.keys() if not key.startswith('__') and
|
||||||
d.getVarFlag(key, 'export', False) and
|
d.getVarFlag(key, 'export') and
|
||||||
not d.getVarFlag(key, 'unexport', False))
|
not d.getVarFlag(key, 'unexport'))
|
||||||
|
|
||||||
def exported_vars(d):
|
def exported_vars(d):
|
||||||
k = list(exported_keys(d))
|
for key in exported_keys(d):
|
||||||
for key in k:
|
|
||||||
try:
|
try:
|
||||||
value = d.getVar(key, True)
|
value = d.getVar(key, True)
|
||||||
except Exception as err:
|
except Exception:
|
||||||
bb.warn("%s: Unable to export ${%s}: %s" % (d.getVar("FILE", True), key, err))
|
pass
|
||||||
continue
|
|
||||||
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
yield key, str(value)
|
yield key, str(value)
|
||||||
@@ -272,7 +269,7 @@ def exported_vars(d):
|
|||||||
def emit_func(func, o=sys.__stdout__, d = init()):
|
def emit_func(func, o=sys.__stdout__, d = init()):
|
||||||
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
||||||
|
|
||||||
keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func", False))
|
keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func"))
|
||||||
for key in keys:
|
for key in keys:
|
||||||
emit_var(key, o, d, False)
|
emit_var(key, o, d, False)
|
||||||
|
|
||||||
@@ -286,7 +283,7 @@ def emit_func(func, o=sys.__stdout__, d = init()):
|
|||||||
seen |= deps
|
seen |= deps
|
||||||
newdeps = set()
|
newdeps = set()
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
if d.getVarFlag(dep, "func", False) and not d.getVarFlag(dep, "python", False):
|
if d.getVarFlag(dep, "func") and not d.getVarFlag(dep, "python"):
|
||||||
emit_var(dep, o, d, False) and o.write('\n')
|
emit_var(dep, o, d, False) and o.write('\n')
|
||||||
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
|
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
|
||||||
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
||||||
@@ -300,7 +297,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
|
|||||||
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
||||||
|
|
||||||
def write_func(func, o, call = False):
|
def write_func(func, o, call = False):
|
||||||
body = d.getVar(func, False)
|
body = d.getVar(func, True)
|
||||||
if not body.startswith("def"):
|
if not body.startswith("def"):
|
||||||
body = _functionfmt.format(function=func, body=body)
|
body = _functionfmt.format(function=func, body=body)
|
||||||
|
|
||||||
@@ -310,7 +307,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
|
|||||||
|
|
||||||
write_func(func, o, True)
|
write_func(func, o, True)
|
||||||
pp = bb.codeparser.PythonParser(func, logger)
|
pp = bb.codeparser.PythonParser(func, logger)
|
||||||
pp.parse_python(d.getVar(func, False))
|
pp.parse_python(d.getVar(func, True))
|
||||||
newdeps = pp.execs
|
newdeps = pp.execs
|
||||||
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
|
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
|
||||||
seen = set()
|
seen = set()
|
||||||
@@ -319,10 +316,10 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
|
|||||||
seen |= deps
|
seen |= deps
|
||||||
newdeps = set()
|
newdeps = set()
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
if d.getVarFlag(dep, "func", False) and d.getVarFlag(dep, "python", False):
|
if d.getVarFlag(dep, "func") and d.getVarFlag(dep, "python"):
|
||||||
write_func(dep, o)
|
write_func(dep, o)
|
||||||
pp = bb.codeparser.PythonParser(dep, logger)
|
pp = bb.codeparser.PythonParser(dep, logger)
|
||||||
pp.parse_python(d.getVar(dep, False))
|
pp.parse_python(d.getVar(dep, True))
|
||||||
newdeps |= pp.execs
|
newdeps |= pp.execs
|
||||||
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
||||||
newdeps -= seen
|
newdeps -= seen
|
||||||
@@ -341,7 +338,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
|||||||
deps |= parser.references
|
deps |= parser.references
|
||||||
deps = deps | (keys & parser.execs)
|
deps = deps | (keys & parser.execs)
|
||||||
return deps, value
|
return deps, value
|
||||||
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "exports", "postfuncs", "prefuncs", "lineno", "filename"]) or {}
|
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "vardepvalueexclude", "postfuncs", "prefuncs"]) or {}
|
||||||
vardeps = varflags.get("vardeps")
|
vardeps = varflags.get("vardeps")
|
||||||
value = d.getVar(key, False)
|
value = d.getVar(key, False)
|
||||||
|
|
||||||
@@ -364,29 +361,27 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
|||||||
value = varflags.get("vardepvalue")
|
value = varflags.get("vardepvalue")
|
||||||
elif varflags.get("func"):
|
elif varflags.get("func"):
|
||||||
if varflags.get("python"):
|
if varflags.get("python"):
|
||||||
|
parsedvar = d.expandWithRefs(value, key)
|
||||||
parser = bb.codeparser.PythonParser(key, logger)
|
parser = bb.codeparser.PythonParser(key, logger)
|
||||||
if value and "\t" in value:
|
if parsedvar.value and "\t" in parsedvar.value:
|
||||||
logger.warning("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE", True)))
|
logger.warn("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE", True)))
|
||||||
parser.parse_python(value, filename=varflags.get("filename"), lineno=varflags.get("lineno"))
|
parser.parse_python(parsedvar.value)
|
||||||
deps = deps | parser.references
|
deps = deps | parser.references
|
||||||
deps = deps | (keys & parser.execs)
|
|
||||||
value = handle_contains(value, parser.contains, d)
|
value = handle_contains(value, parser.contains, d)
|
||||||
else:
|
else:
|
||||||
parsedvar = d.expandWithRefs(value, key)
|
parsedvar = d.expandWithRefs(value, key)
|
||||||
parser = bb.codeparser.ShellParser(key, logger)
|
parser = bb.codeparser.ShellParser(key, logger)
|
||||||
parser.parse_shell(parsedvar.value)
|
parser.parse_shell(parsedvar.value)
|
||||||
deps = deps | shelldeps
|
deps = deps | shelldeps
|
||||||
deps = deps | parsedvar.references
|
|
||||||
deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
|
|
||||||
value = handle_contains(value, parsedvar.contains, d)
|
|
||||||
if vardeps is None:
|
if vardeps is None:
|
||||||
parser.log.flush()
|
parser.log.flush()
|
||||||
if "prefuncs" in varflags:
|
if "prefuncs" in varflags:
|
||||||
deps = deps | set(varflags["prefuncs"].split())
|
deps = deps | set(varflags["prefuncs"].split())
|
||||||
if "postfuncs" in varflags:
|
if "postfuncs" in varflags:
|
||||||
deps = deps | set(varflags["postfuncs"].split())
|
deps = deps | set(varflags["postfuncs"].split())
|
||||||
if "exports" in varflags:
|
deps = deps | parsedvar.references
|
||||||
deps = deps | set(varflags["exports"].split())
|
deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
|
||||||
|
value = handle_contains(value, parsedvar.contains, d)
|
||||||
else:
|
else:
|
||||||
parser = d.expandWithRefs(value, key)
|
parser = d.expandWithRefs(value, key)
|
||||||
deps |= parser.references
|
deps |= parser.references
|
||||||
@@ -411,8 +406,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
|||||||
deps |= set((vardeps or "").split())
|
deps |= set((vardeps or "").split())
|
||||||
deps -= set(varflags.get("vardepsexclude", "").split())
|
deps -= set(varflags.get("vardepsexclude", "").split())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
bb.warn("Exception during build_dependencies for %s" % key)
|
raise bb.data_smart.ExpansionError(key, None, e)
|
||||||
raise
|
|
||||||
return deps, value
|
return deps, value
|
||||||
#bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
|
#bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
|
||||||
#d.setVarFlag(key, "vardeps", deps)
|
#d.setVarFlag(key, "vardeps", deps)
|
||||||
@@ -420,7 +414,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
|||||||
def generate_dependencies(d):
|
def generate_dependencies(d):
|
||||||
|
|
||||||
keys = set(key for key in d if not key.startswith("__"))
|
keys = set(key for key in d if not key.startswith("__"))
|
||||||
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export", False) and not d.getVarFlag(key, "unexport", False))
|
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||||
|
|
||||||
deps = {}
|
deps = {}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ logger = logging.getLogger("BitBake.Data")
|
|||||||
|
|
||||||
__setvar_keyword__ = ["_append", "_prepend", "_remove"]
|
__setvar_keyword__ = ["_append", "_prepend", "_remove"]
|
||||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>.*))?$')
|
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>.*))?$')
|
||||||
__expand_var_regexp__ = re.compile(r"\${[^{}@\n\t :]+}")
|
__expand_var_regexp__ = re.compile(r"\${[^{}@\n\t ]+}")
|
||||||
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
||||||
|
|
||||||
def infer_caller_details(loginfo, parent = False, varval = True):
|
def infer_caller_details(loginfo, parent = False, varval = True):
|
||||||
@@ -135,7 +135,7 @@ class VariableParse:
|
|||||||
self.contains[k] = parser.contains[k].copy()
|
self.contains[k] = parser.contains[k].copy()
|
||||||
else:
|
else:
|
||||||
self.contains[k].update(parser.contains[k])
|
self.contains[k].update(parser.contains[k])
|
||||||
value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d})
|
value = utils.better_eval(codeobj, DataContext(self.d))
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ class DataContext(dict):
|
|||||||
|
|
||||||
def __missing__(self, key):
|
def __missing__(self, key):
|
||||||
value = self.metadata.getVar(key, True)
|
value = self.metadata.getVar(key, True)
|
||||||
if value is None or self.metadata.getVarFlag(key, 'func', False):
|
if value is None or self.metadata.getVarFlag(key, 'func'):
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
@@ -372,7 +372,7 @@ class DataSmart(MutableMapping):
|
|||||||
|
|
||||||
def expandWithRefs(self, s, varname):
|
def expandWithRefs(self, s, varname):
|
||||||
|
|
||||||
if not isinstance(s, str): # sanity check
|
if not isinstance(s, basestring): # sanity check
|
||||||
return VariableParse(varname, self, s)
|
return VariableParse(varname, self, s)
|
||||||
|
|
||||||
if varname and varname in self.expand_cache:
|
if varname and varname in self.expand_cache:
|
||||||
@@ -384,12 +384,7 @@ class DataSmart(MutableMapping):
|
|||||||
olds = s
|
olds = s
|
||||||
try:
|
try:
|
||||||
s = __expand_var_regexp__.sub(varparse.var_sub, s)
|
s = __expand_var_regexp__.sub(varparse.var_sub, s)
|
||||||
try:
|
s = __expand_python_regexp__.sub(varparse.python_sub, s)
|
||||||
s = __expand_python_regexp__.sub(varparse.python_sub, s)
|
|
||||||
except SyntaxError as e:
|
|
||||||
# Likely unmatched brackets, just don't expand the expression
|
|
||||||
if e.msg != "EOL while scanning string literal":
|
|
||||||
raise
|
|
||||||
if s == olds:
|
if s == olds:
|
||||||
break
|
break
|
||||||
except ExpansionError:
|
except ExpansionError:
|
||||||
@@ -397,7 +392,8 @@ class DataSmart(MutableMapping):
|
|||||||
except bb.parse.SkipRecipe:
|
except bb.parse.SkipRecipe:
|
||||||
raise
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ExpansionError(varname, s, exc) from exc
|
exc_class, exc, tb = sys.exc_info()
|
||||||
|
raise ExpansionError, ExpansionError(varname, s, exc), tb
|
||||||
|
|
||||||
varparse.value = s
|
varparse.value = s
|
||||||
|
|
||||||
@@ -479,7 +475,7 @@ class DataSmart(MutableMapping):
|
|||||||
base = match.group('base')
|
base = match.group('base')
|
||||||
keyword = match.group("keyword")
|
keyword = match.group("keyword")
|
||||||
override = match.group('add')
|
override = match.group('add')
|
||||||
l = self.getVarFlag(base, keyword, False) or []
|
l = self.getVarFlag(base, keyword) or []
|
||||||
l.append([value, override])
|
l.append([value, override])
|
||||||
self.setVarFlag(base, keyword, l, ignore=True)
|
self.setVarFlag(base, keyword, l, ignore=True)
|
||||||
# And cause that to be recorded:
|
# And cause that to be recorded:
|
||||||
@@ -551,7 +547,7 @@ class DataSmart(MutableMapping):
|
|||||||
# aka pay the cookie monster
|
# aka pay the cookie monster
|
||||||
override = var[var.rfind('_')+1:]
|
override = var[var.rfind('_')+1:]
|
||||||
shortvar = var[:var.rfind('_')]
|
shortvar = var[:var.rfind('_')]
|
||||||
while override and override.islower():
|
while override:
|
||||||
if shortvar not in self.overridedata:
|
if shortvar not in self.overridedata:
|
||||||
self.overridedata[shortvar] = []
|
self.overridedata[shortvar] = []
|
||||||
if [var, override] not in self.overridedata[shortvar]:
|
if [var, override] not in self.overridedata[shortvar]:
|
||||||
@@ -565,7 +561,7 @@ class DataSmart(MutableMapping):
|
|||||||
if len(shortvar) == 0:
|
if len(shortvar) == 0:
|
||||||
override = None
|
override = None
|
||||||
|
|
||||||
def getVar(self, var, expand, noweakdefault=False, parsing=False):
|
def getVar(self, var, expand=False, noweakdefault=False, parsing=False):
|
||||||
return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
|
return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
|
||||||
|
|
||||||
def renameVar(self, key, newkey, **loginfo):
|
def renameVar(self, key, newkey, **loginfo):
|
||||||
@@ -581,11 +577,11 @@ class DataSmart(MutableMapping):
|
|||||||
self.setVar(newkey, val, ignore=True, parsing=True)
|
self.setVar(newkey, val, ignore=True, parsing=True)
|
||||||
|
|
||||||
for i in (__setvar_keyword__):
|
for i in (__setvar_keyword__):
|
||||||
src = self.getVarFlag(key, i, False)
|
src = self.getVarFlag(key, i)
|
||||||
if src is None:
|
if src is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
dest = self.getVarFlag(newkey, i, False) or []
|
dest = self.getVarFlag(newkey, i) or []
|
||||||
dest.extend(src)
|
dest.extend(src)
|
||||||
self.setVarFlag(newkey, i, dest, ignore=True)
|
self.setVarFlag(newkey, i, dest, ignore=True)
|
||||||
|
|
||||||
@@ -625,7 +621,7 @@ class DataSmart(MutableMapping):
|
|||||||
if '_' in var:
|
if '_' in var:
|
||||||
override = var[var.rfind('_')+1:]
|
override = var[var.rfind('_')+1:]
|
||||||
shortvar = var[:var.rfind('_')]
|
shortvar = var[:var.rfind('_')]
|
||||||
while override and override.islower():
|
while override:
|
||||||
try:
|
try:
|
||||||
if shortvar in self.overridedata:
|
if shortvar in self.overridedata:
|
||||||
# Force CoW by recreating the list first
|
# Force CoW by recreating the list first
|
||||||
@@ -662,7 +658,7 @@ class DataSmart(MutableMapping):
|
|||||||
self.dict["__exportlist"]["_content"] = set()
|
self.dict["__exportlist"]["_content"] = set()
|
||||||
self.dict["__exportlist"]["_content"].add(var)
|
self.dict["__exportlist"]["_content"].add(var)
|
||||||
|
|
||||||
def getVarFlag(self, var, flag, expand, noweakdefault=False, parsing=False):
|
def getVarFlag(self, var, flag, expand=False, noweakdefault=False, parsing=False):
|
||||||
local_var = self._findVar(var)
|
local_var = self._findVar(var)
|
||||||
value = None
|
value = None
|
||||||
if flag == "_content" and var in self.overridedata and not parsing:
|
if flag == "_content" and var in self.overridedata and not parsing:
|
||||||
@@ -691,7 +687,7 @@ class DataSmart(MutableMapping):
|
|||||||
match = active[a]
|
match = active[a]
|
||||||
del active[a]
|
del active[a]
|
||||||
if match:
|
if match:
|
||||||
value = self.getVar(match, False)
|
value = self.getVar(match)
|
||||||
|
|
||||||
if local_var is not None and value is None:
|
if local_var is not None and value is None:
|
||||||
if flag in local_var:
|
if flag in local_var:
|
||||||
@@ -748,14 +744,13 @@ class DataSmart(MutableMapping):
|
|||||||
if match:
|
if match:
|
||||||
removes.extend(self.expand(r).split())
|
removes.extend(self.expand(r).split())
|
||||||
|
|
||||||
if removes:
|
filtered = filter(lambda v: v not in removes,
|
||||||
filtered = filter(lambda v: v not in removes,
|
value.split())
|
||||||
value.split())
|
value = " ".join(filtered)
|
||||||
value = " ".join(filtered)
|
if expand and var in self.expand_cache:
|
||||||
if expand and var in self.expand_cache:
|
# We need to ensure the expand cache has the correct value
|
||||||
# We need to ensure the expand cache has the correct value
|
# flag == "_content" here
|
||||||
# flag == "_content" here
|
self.expand_cache[var].value = value
|
||||||
self.expand_cache[var].value = value
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def delVarFlag(self, var, flag, **loginfo):
|
def delVarFlag(self, var, flag, **loginfo):
|
||||||
@@ -917,7 +912,7 @@ class DataSmart(MutableMapping):
|
|||||||
yield k
|
yield k
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(frozenset(iter(self)))
|
return len(frozenset(self))
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
value = self.getVar(item, False)
|
value = self.getVar(item, False)
|
||||||
@@ -962,8 +957,8 @@ class DataSmart(MutableMapping):
|
|||||||
|
|
||||||
if key == "__BBANONFUNCS":
|
if key == "__BBANONFUNCS":
|
||||||
for i in bb_list:
|
for i in bb_list:
|
||||||
value = d.getVar(i, False) or ""
|
value = d.getVar(i, True) or ""
|
||||||
data.update({i:value})
|
data.update({i:value})
|
||||||
|
|
||||||
data_str = str([(k, data[k]) for k in sorted(data.keys())])
|
data_str = str([(k, data[k]) for k in sorted(data.keys())])
|
||||||
return hashlib.md5(data_str.encode("utf-8")).hexdigest()
|
return hashlib.md5(data_str).hexdigest()
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ BitBake build tools.
|
|||||||
|
|
||||||
import os, sys
|
import os, sys
|
||||||
import warnings
|
import warnings
|
||||||
import pickle
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
import logging
|
import logging
|
||||||
import atexit
|
import atexit
|
||||||
import traceback
|
import traceback
|
||||||
import ast
|
|
||||||
import threading
|
|
||||||
|
|
||||||
import bb.utils
|
import bb.utils
|
||||||
import bb.compat
|
import bb.compat
|
||||||
import bb.exceptions
|
import bb.exceptions
|
||||||
@@ -70,27 +70,12 @@ _event_handler_map = {}
|
|||||||
_catchall_handlers = {}
|
_catchall_handlers = {}
|
||||||
_eventfilter = None
|
_eventfilter = None
|
||||||
_uiready = False
|
_uiready = False
|
||||||
_thread_lock = threading.Lock()
|
|
||||||
_thread_lock_enabled = False
|
|
||||||
|
|
||||||
if hasattr(__builtins__, '__setitem__'):
|
|
||||||
builtins = __builtins__
|
|
||||||
else:
|
|
||||||
builtins = __builtins__.__dict__
|
|
||||||
|
|
||||||
def enable_threadlock():
|
|
||||||
global _thread_lock_enabled
|
|
||||||
_thread_lock_enabled = True
|
|
||||||
|
|
||||||
def disable_threadlock():
|
|
||||||
global _thread_lock_enabled
|
|
||||||
_thread_lock_enabled = False
|
|
||||||
|
|
||||||
def execute_handler(name, handler, event, d):
|
def execute_handler(name, handler, event, d):
|
||||||
event.data = d
|
event.data = d
|
||||||
addedd = False
|
addedd = False
|
||||||
if 'd' not in builtins:
|
if 'd' not in __builtins__:
|
||||||
builtins['d'] = d
|
__builtins__['d'] = d
|
||||||
addedd = True
|
addedd = True
|
||||||
try:
|
try:
|
||||||
ret = handler(event)
|
ret = handler(event)
|
||||||
@@ -108,7 +93,7 @@ def execute_handler(name, handler, event, d):
|
|||||||
finally:
|
finally:
|
||||||
del event.data
|
del event.data
|
||||||
if addedd:
|
if addedd:
|
||||||
del builtins['d']
|
del __builtins__['d']
|
||||||
|
|
||||||
def fire_class_handlers(event, d):
|
def fire_class_handlers(event, d):
|
||||||
if isinstance(event, logging.LogRecord):
|
if isinstance(event, logging.LogRecord):
|
||||||
@@ -116,7 +101,7 @@ def fire_class_handlers(event, d):
|
|||||||
|
|
||||||
eid = str(event.__class__)[8:-2]
|
eid = str(event.__class__)[8:-2]
|
||||||
evt_hmap = _event_handler_map.get(eid, {})
|
evt_hmap = _event_handler_map.get(eid, {})
|
||||||
for name, handler in list(_handlers.items()):
|
for name, handler in _handlers.iteritems():
|
||||||
if name in _catchall_handlers or name in evt_hmap:
|
if name in _catchall_handlers or name in evt_hmap:
|
||||||
if _eventfilter:
|
if _eventfilter:
|
||||||
if not _eventfilter(name, handler, event, d):
|
if not _eventfilter(name, handler, event, d):
|
||||||
@@ -131,44 +116,31 @@ def print_ui_queue():
|
|||||||
logger = logging.getLogger("BitBake")
|
logger = logging.getLogger("BitBake")
|
||||||
if not _uiready:
|
if not _uiready:
|
||||||
from bb.msg import BBLogFormatter
|
from bb.msg import BBLogFormatter
|
||||||
stdout = logging.StreamHandler(sys.stdout)
|
console = logging.StreamHandler(sys.stdout)
|
||||||
stderr = logging.StreamHandler(sys.stderr)
|
console.setFormatter(BBLogFormatter("%(levelname)s: %(message)s"))
|
||||||
formatter = BBLogFormatter("%(levelname)s: %(message)s")
|
logger.handlers = [console]
|
||||||
stdout.setFormatter(formatter)
|
|
||||||
stderr.setFormatter(formatter)
|
|
||||||
|
|
||||||
# First check to see if we have any proper messages
|
# First check to see if we have any proper messages
|
||||||
msgprint = False
|
msgprint = False
|
||||||
for event in ui_queue[:]:
|
for event in ui_queue:
|
||||||
if isinstance(event, logging.LogRecord):
|
if isinstance(event, logging.LogRecord):
|
||||||
if event.levelno > logging.DEBUG:
|
if event.levelno > logging.DEBUG:
|
||||||
if event.levelno >= logging.WARNING:
|
|
||||||
logger.addHandler(stderr)
|
|
||||||
else:
|
|
||||||
logger.addHandler(stdout)
|
|
||||||
logger.handle(event)
|
logger.handle(event)
|
||||||
msgprint = True
|
msgprint = True
|
||||||
if msgprint:
|
if msgprint:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Nope, so just print all of the messages we have (including debug messages)
|
# Nope, so just print all of the messages we have (including debug messages)
|
||||||
logger.addHandler(stdout)
|
for event in ui_queue:
|
||||||
for event in ui_queue[:]:
|
|
||||||
if isinstance(event, logging.LogRecord):
|
if isinstance(event, logging.LogRecord):
|
||||||
logger.handle(event)
|
logger.handle(event)
|
||||||
|
|
||||||
def fire_ui_handlers(event, d):
|
def fire_ui_handlers(event, d):
|
||||||
global _thread_lock
|
|
||||||
global _thread_lock_enabled
|
|
||||||
|
|
||||||
if not _uiready:
|
if not _uiready:
|
||||||
# No UI handlers registered yet, queue up the messages
|
# No UI handlers registered yet, queue up the messages
|
||||||
ui_queue.append(event)
|
ui_queue.append(event)
|
||||||
return
|
return
|
||||||
|
|
||||||
if _thread_lock_enabled:
|
|
||||||
_thread_lock.acquire()
|
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
for h in _ui_handlers:
|
for h in _ui_handlers:
|
||||||
#print "Sending event %s" % event
|
#print "Sending event %s" % event
|
||||||
@@ -187,9 +159,6 @@ def fire_ui_handlers(event, d):
|
|||||||
for h in errors:
|
for h in errors:
|
||||||
del _ui_handlers[h]
|
del _ui_handlers[h]
|
||||||
|
|
||||||
if _thread_lock_enabled:
|
|
||||||
_thread_lock.release()
|
|
||||||
|
|
||||||
def fire(event, d):
|
def fire(event, d):
|
||||||
"""Fire off an Event"""
|
"""Fire off an Event"""
|
||||||
|
|
||||||
@@ -208,7 +177,7 @@ def fire_from_worker(event, d):
|
|||||||
fire_ui_handlers(event, d)
|
fire_ui_handlers(event, d)
|
||||||
|
|
||||||
noop = lambda _: None
|
noop = lambda _: None
|
||||||
def register(name, handler, mask=None, filename=None, lineno=None):
|
def register(name, handler, mask=None):
|
||||||
"""Register an Event handler"""
|
"""Register an Event handler"""
|
||||||
|
|
||||||
# already registered
|
# already registered
|
||||||
@@ -217,18 +186,10 @@ def register(name, handler, mask=None, filename=None, lineno=None):
|
|||||||
|
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
# handle string containing python code
|
# handle string containing python code
|
||||||
if isinstance(handler, str):
|
if isinstance(handler, basestring):
|
||||||
tmp = "def %s(e):\n%s" % (name, handler)
|
tmp = "def %s(e):\n%s" % (name, handler)
|
||||||
try:
|
try:
|
||||||
code = bb.methodpool.compile_cache(tmp)
|
code = compile(tmp, "%s(e)" % name, "exec")
|
||||||
if not code:
|
|
||||||
if filename is None:
|
|
||||||
filename = "%s(e)" % name
|
|
||||||
code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST)
|
|
||||||
if lineno is not None:
|
|
||||||
ast.increment_lineno(code, lineno-1)
|
|
||||||
code = compile(code, filename, "exec")
|
|
||||||
bb.methodpool.compile_cache_add(tmp, code)
|
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
logger.error("Unable to register event handler '%s':\n%s", name,
|
logger.error("Unable to register event handler '%s':\n%s", name,
|
||||||
''.join(traceback.format_exc(limit=0)))
|
''.join(traceback.format_exc(limit=0)))
|
||||||
@@ -255,13 +216,6 @@ def remove(name, handler):
|
|||||||
"""Remove an Event handler"""
|
"""Remove an Event handler"""
|
||||||
_handlers.pop(name)
|
_handlers.pop(name)
|
||||||
|
|
||||||
def get_handlers():
|
|
||||||
return _handlers
|
|
||||||
|
|
||||||
def set_handlers(handlers):
|
|
||||||
global _handlers
|
|
||||||
_handlers = handlers
|
|
||||||
|
|
||||||
def set_eventfilter(func):
|
def set_eventfilter(func):
|
||||||
global _eventfilter
|
global _eventfilter
|
||||||
_eventfilter = func
|
_eventfilter = func
|
||||||
@@ -410,11 +364,7 @@ class BuildBase(Event):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BuildInit(BuildBase):
|
|
||||||
"""buildFile or buildTargets was invoked"""
|
|
||||||
def __init__(self, p=[]):
|
|
||||||
name = None
|
|
||||||
BuildBase.__init__(self, name, p)
|
|
||||||
|
|
||||||
class BuildStarted(BuildBase, OperationStarted):
|
class BuildStarted(BuildBase, OperationStarted):
|
||||||
"""bbmake build run started"""
|
"""bbmake build run started"""
|
||||||
@@ -645,10 +595,7 @@ class LogHandler(logging.Handler):
|
|||||||
etype, value, tb = record.exc_info
|
etype, value, tb = record.exc_info
|
||||||
if hasattr(tb, 'tb_next'):
|
if hasattr(tb, 'tb_next'):
|
||||||
tb = list(bb.exceptions.extract_traceback(tb, context=3))
|
tb = list(bb.exceptions.extract_traceback(tb, context=3))
|
||||||
# Need to turn the value into something the logging system can pickle
|
|
||||||
record.bb_exc_info = (etype, value, tb)
|
record.bb_exc_info = (etype, value, tb)
|
||||||
record.bb_exc_formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
|
|
||||||
value = str(value)
|
|
||||||
record.exc_info = None
|
record.exc_info = None
|
||||||
fire(record, None)
|
fire(record, None)
|
||||||
|
|
||||||
@@ -679,33 +626,6 @@ class MetadataEvent(Event):
|
|||||||
self.type = eventtype
|
self.type = eventtype
|
||||||
self._localdata = eventdata
|
self._localdata = eventdata
|
||||||
|
|
||||||
class ProcessStarted(Event):
|
|
||||||
"""
|
|
||||||
Generic process started event (usually part of the initial startup)
|
|
||||||
where further progress events will be delivered
|
|
||||||
"""
|
|
||||||
def __init__(self, processname, total):
|
|
||||||
Event.__init__(self)
|
|
||||||
self.processname = processname
|
|
||||||
self.total = total
|
|
||||||
|
|
||||||
class ProcessProgress(Event):
|
|
||||||
"""
|
|
||||||
Generic process progress event (usually part of the initial startup)
|
|
||||||
"""
|
|
||||||
def __init__(self, processname, progress):
|
|
||||||
Event.__init__(self)
|
|
||||||
self.processname = processname
|
|
||||||
self.progress = progress
|
|
||||||
|
|
||||||
class ProcessFinished(Event):
|
|
||||||
"""
|
|
||||||
Generic process finished event (usually part of the initial startup)
|
|
||||||
"""
|
|
||||||
def __init__(self, processname):
|
|
||||||
Event.__init__(self)
|
|
||||||
self.processname = processname
|
|
||||||
|
|
||||||
class SanityCheck(Event):
|
class SanityCheck(Event):
|
||||||
"""
|
"""
|
||||||
Event to run sanity checks, either raise errors or generate events as return status.
|
Event to run sanity checks, either raise errors or generate events as return status.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
import inspect
|
import inspect
|
||||||
import traceback
|
import traceback
|
||||||
import bb.namedtuple_with_abc
|
import bb.namedtuple_with_abc
|
||||||
@@ -86,6 +86,6 @@ def format_exception(etype, value, tb, context=1, limit=None, formatter=None):
|
|||||||
|
|
||||||
def to_string(exc):
|
def to_string(exc):
|
||||||
if isinstance(exc, SystemExit):
|
if isinstance(exc, SystemExit):
|
||||||
if not isinstance(exc.code, str):
|
if not isinstance(exc.code, basestring):
|
||||||
return 'Exited with "%d"' % exc.code
|
return 'Exited with "%d"' % exc.code
|
||||||
return str(exc)
|
return str(exc)
|
||||||
|
|||||||
@@ -25,26 +25,33 @@ BitBake build tools.
|
|||||||
#
|
#
|
||||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import print_function
|
||||||
import os, re
|
import os, re
|
||||||
import signal
|
import signal
|
||||||
|
import glob
|
||||||
import logging
|
import logging
|
||||||
import urllib.request, urllib.parse, urllib.error
|
import urllib
|
||||||
if 'git' not in urllib.parse.uses_netloc:
|
import urlparse
|
||||||
urllib.parse.uses_netloc.append('git')
|
|
||||||
import operator
|
import operator
|
||||||
import collections
|
|
||||||
import subprocess
|
|
||||||
import pickle
|
|
||||||
import bb.persist_data, bb.utils
|
import bb.persist_data, bb.utils
|
||||||
import bb.checksum
|
import bb.checksum
|
||||||
from bb import data
|
from bb import data
|
||||||
import bb.process
|
import bb.process
|
||||||
|
import subprocess
|
||||||
|
|
||||||
__version__ = "2"
|
__version__ = "2"
|
||||||
_checksum_cache = bb.checksum.FileChecksumCache()
|
_checksum_cache = bb.checksum.FileChecksumCache()
|
||||||
|
|
||||||
logger = logging.getLogger("BitBake.Fetcher")
|
logger = logging.getLogger("BitBake.Fetcher")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
logger.info("Importing cPickle failed. "
|
||||||
|
"Falling back to a very slow implementation.")
|
||||||
|
|
||||||
class BBFetchException(Exception):
|
class BBFetchException(Exception):
|
||||||
"""Class all fetch exceptions inherit from"""
|
"""Class all fetch exceptions inherit from"""
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
@@ -226,14 +233,14 @@ class URI(object):
|
|||||||
# them are not quite RFC compliant.
|
# them are not quite RFC compliant.
|
||||||
uri, param_str = (uri.split(";", 1) + [None])[:2]
|
uri, param_str = (uri.split(";", 1) + [None])[:2]
|
||||||
|
|
||||||
urlp = urllib.parse.urlparse(uri)
|
urlp = urlparse.urlparse(uri)
|
||||||
self.scheme = urlp.scheme
|
self.scheme = urlp.scheme
|
||||||
|
|
||||||
reparse = 0
|
reparse = 0
|
||||||
|
|
||||||
# Coerce urlparse to make URI scheme use netloc
|
# Coerce urlparse to make URI scheme use netloc
|
||||||
if not self.scheme in urllib.parse.uses_netloc:
|
if not self.scheme in urlparse.uses_netloc:
|
||||||
urllib.parse.uses_params.append(self.scheme)
|
urlparse.uses_params.append(self.scheme)
|
||||||
reparse = 1
|
reparse = 1
|
||||||
|
|
||||||
# Make urlparse happy(/ier) by converting local resources
|
# Make urlparse happy(/ier) by converting local resources
|
||||||
@@ -244,7 +251,7 @@ class URI(object):
|
|||||||
reparse = 1
|
reparse = 1
|
||||||
|
|
||||||
if reparse:
|
if reparse:
|
||||||
urlp = urllib.parse.urlparse(uri)
|
urlp = urlparse.urlparse(uri)
|
||||||
|
|
||||||
# Identify if the URI is relative or not
|
# Identify if the URI is relative or not
|
||||||
if urlp.scheme in self._relative_schemes and \
|
if urlp.scheme in self._relative_schemes and \
|
||||||
@@ -260,7 +267,7 @@ class URI(object):
|
|||||||
if urlp.password:
|
if urlp.password:
|
||||||
self.userinfo += ':%s' % urlp.password
|
self.userinfo += ':%s' % urlp.password
|
||||||
|
|
||||||
self.path = urllib.parse.unquote(urlp.path)
|
self.path = urllib.unquote(urlp.path)
|
||||||
|
|
||||||
if param_str:
|
if param_str:
|
||||||
self.params = self._param_str_split(param_str, ";")
|
self.params = self._param_str_split(param_str, ";")
|
||||||
@@ -292,7 +299,7 @@ class URI(object):
|
|||||||
if self.query else '')
|
if self.query else '')
|
||||||
|
|
||||||
def _param_str_split(self, string, elmdelim, kvdelim="="):
|
def _param_str_split(self, string, elmdelim, kvdelim="="):
|
||||||
ret = collections.OrderedDict()
|
ret = {}
|
||||||
for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
|
for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
|
||||||
ret[k] = v
|
ret[k] = v
|
||||||
return ret
|
return ret
|
||||||
@@ -308,11 +315,11 @@ class URI(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_quoted(self):
|
def path_quoted(self):
|
||||||
return urllib.parse.quote(self.path)
|
return urllib.quote(self.path)
|
||||||
|
|
||||||
@path_quoted.setter
|
@path_quoted.setter
|
||||||
def path_quoted(self, path):
|
def path_quoted(self, path):
|
||||||
self.path = urllib.parse.unquote(path)
|
self.path = urllib.unquote(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
@@ -322,7 +329,7 @@ class URI(object):
|
|||||||
def path(self, path):
|
def path(self, path):
|
||||||
self._path = path
|
self._path = path
|
||||||
|
|
||||||
if not path or re.compile("^/").match(path):
|
if re.compile("^/").match(path):
|
||||||
self.relative = False
|
self.relative = False
|
||||||
else:
|
else:
|
||||||
self.relative = True
|
self.relative = True
|
||||||
@@ -370,12 +377,9 @@ def decodeurl(url):
|
|||||||
if locidx != -1 and type.lower() != 'file':
|
if locidx != -1 and type.lower() != 'file':
|
||||||
host = location[:locidx]
|
host = location[:locidx]
|
||||||
path = location[locidx:]
|
path = location[locidx:]
|
||||||
elif type.lower() == 'file':
|
else:
|
||||||
host = ""
|
host = ""
|
||||||
path = location
|
path = location
|
||||||
else:
|
|
||||||
host = location
|
|
||||||
path = ""
|
|
||||||
if user:
|
if user:
|
||||||
m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
|
m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
|
||||||
if m:
|
if m:
|
||||||
@@ -385,7 +389,7 @@ def decodeurl(url):
|
|||||||
user = ''
|
user = ''
|
||||||
pswd = ''
|
pswd = ''
|
||||||
|
|
||||||
p = collections.OrderedDict()
|
p = {}
|
||||||
if parm:
|
if parm:
|
||||||
for s in parm.split(';'):
|
for s in parm.split(';'):
|
||||||
if s:
|
if s:
|
||||||
@@ -394,7 +398,7 @@ def decodeurl(url):
|
|||||||
s1, s2 = s.split('=')
|
s1, s2 = s.split('=')
|
||||||
p[s1] = s2
|
p[s1] = s2
|
||||||
|
|
||||||
return type, host, urllib.parse.unquote(path), user, pswd, p
|
return type, host, urllib.unquote(path), user, pswd, p
|
||||||
|
|
||||||
def encodeurl(decoded):
|
def encodeurl(decoded):
|
||||||
"""Encodes a URL from tokens (scheme, network location, path,
|
"""Encodes a URL from tokens (scheme, network location, path,
|
||||||
@@ -418,7 +422,7 @@ def encodeurl(decoded):
|
|||||||
# Standardise path to ensure comparisons work
|
# Standardise path to ensure comparisons work
|
||||||
while '//' in path:
|
while '//' in path:
|
||||||
path = path.replace("//", "/")
|
path = path.replace("//", "/")
|
||||||
url += "%s" % urllib.parse.quote(path)
|
url += "%s" % urllib.quote(path)
|
||||||
if p:
|
if p:
|
||||||
for parm in p:
|
for parm in p:
|
||||||
url += ";%s=%s" % (parm, p[parm])
|
url += ";%s=%s" % (parm, p[parm])
|
||||||
@@ -511,13 +515,13 @@ def fetcher_init(d):
|
|||||||
if hasattr(m, "init"):
|
if hasattr(m, "init"):
|
||||||
m.init(d)
|
m.init(d)
|
||||||
|
|
||||||
def fetcher_parse_save():
|
def fetcher_parse_save(d):
|
||||||
_checksum_cache.save_extras()
|
_checksum_cache.save_extras(d)
|
||||||
|
|
||||||
def fetcher_parse_done():
|
def fetcher_parse_done(d):
|
||||||
_checksum_cache.save_merge()
|
_checksum_cache.save_merge(d)
|
||||||
|
|
||||||
def fetcher_compare_revisions():
|
def fetcher_compare_revisions(d):
|
||||||
"""
|
"""
|
||||||
Compare the revisions in the persistant cache with current values and
|
Compare the revisions in the persistant cache with current values and
|
||||||
return true/false on whether they've changed.
|
return true/false on whether they've changed.
|
||||||
@@ -570,10 +574,10 @@ def verify_checksum(ud, d, precomputed={}):
|
|||||||
else:
|
else:
|
||||||
sha256data = bb.utils.sha256_file(ud.localpath)
|
sha256data = bb.utils.sha256_file(ud.localpath)
|
||||||
|
|
||||||
if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
|
if ud.method.recommends_checksum(ud):
|
||||||
# If strict checking enabled and neither sum defined, raise error
|
# If strict checking enabled and neither sum defined, raise error
|
||||||
strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
|
strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
|
||||||
if strict == "1":
|
if (strict == "1") and not (ud.md5_expected or ud.sha256_expected):
|
||||||
logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
|
logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
|
||||||
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
|
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
|
||||||
(ud.localpath, ud.md5_name, md5data,
|
(ud.localpath, ud.md5_name, md5data,
|
||||||
@@ -581,22 +585,34 @@ def verify_checksum(ud, d, precomputed={}):
|
|||||||
raise NoChecksumError('Missing SRC_URI checksum', ud.url)
|
raise NoChecksumError('Missing SRC_URI checksum', ud.url)
|
||||||
|
|
||||||
# Log missing sums so user can more easily add them
|
# Log missing sums so user can more easily add them
|
||||||
logger.warning('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
|
if not ud.md5_expected:
|
||||||
'SRC_URI[%s] = "%s"',
|
logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
|
||||||
ud.localpath, ud.md5_name, md5data)
|
'SRC_URI[%s] = "%s"',
|
||||||
logger.warning('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
|
ud.localpath, ud.md5_name, md5data)
|
||||||
'SRC_URI[%s] = "%s"',
|
|
||||||
ud.localpath, ud.sha256_name, sha256data)
|
if not ud.sha256_expected:
|
||||||
|
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
|
||||||
|
|
||||||
|
if ud.md5_expected != md5data:
|
||||||
|
md5mismatch = True
|
||||||
|
|
||||||
|
if ud.sha256_expected != sha256data:
|
||||||
|
sha256mismatch = True
|
||||||
|
|
||||||
# We want to alert the user if a checksum is defined in the recipe but
|
# We want to alert the user if a checksum is defined in the recipe but
|
||||||
# it does not match.
|
# it does not match.
|
||||||
msg = ""
|
msg = ""
|
||||||
mismatch = False
|
mismatch = False
|
||||||
if ud.md5_expected and ud.md5_expected != md5data:
|
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)
|
msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'md5', md5data, ud.md5_expected)
|
||||||
mismatch = True;
|
mismatch = True;
|
||||||
|
|
||||||
if ud.sha256_expected and ud.sha256_expected != sha256data:
|
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)
|
msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'sha256', sha256data, ud.sha256_expected)
|
||||||
mismatch = True;
|
mismatch = True;
|
||||||
|
|
||||||
@@ -621,9 +637,6 @@ def verify_donestamp(ud, d, origud=None):
|
|||||||
Returns True, if the donestamp exists and is valid, False otherwise. When
|
Returns True, if the donestamp exists and is valid, False otherwise. When
|
||||||
returning False, any existing done stamps are removed.
|
returning False, any existing done stamps are removed.
|
||||||
"""
|
"""
|
||||||
if not ud.needdonestamp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if not os.path.exists(ud.donestamp):
|
if not os.path.exists(ud.donestamp):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -654,9 +667,9 @@ def verify_donestamp(ud, d, origud=None):
|
|||||||
# files to those containing the checksums.
|
# files to those containing the checksums.
|
||||||
if not isinstance(e, EOFError):
|
if not isinstance(e, EOFError):
|
||||||
# Ignore errors, they aren't fatal
|
# Ignore errors, they aren't fatal
|
||||||
logger.warning("Couldn't load checksums from donestamp %s: %s "
|
logger.warn("Couldn't load checksums from donestamp %s: %s "
|
||||||
"(msg: %s)" % (ud.donestamp, type(e).__name__,
|
"(msg: %s)" % (ud.donestamp, type(e).__name__,
|
||||||
str(e)))
|
str(e)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
checksums = verify_checksum(ud, d, precomputed_checksums)
|
checksums = verify_checksum(ud, d, precomputed_checksums)
|
||||||
@@ -664,16 +677,15 @@ def verify_donestamp(ud, d, origud=None):
|
|||||||
# as an upgrade path from the previous done stamp file format.
|
# as an upgrade path from the previous done stamp file format.
|
||||||
if checksums != precomputed_checksums:
|
if checksums != precomputed_checksums:
|
||||||
with open(ud.donestamp, "wb") as cachefile:
|
with open(ud.donestamp, "wb") as cachefile:
|
||||||
p = pickle.Pickler(cachefile, 2)
|
p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
|
||||||
p.dump(checksums)
|
p.dump(checksums)
|
||||||
return True
|
return True
|
||||||
except ChecksumError as e:
|
except ChecksumError as e:
|
||||||
# Checksums failed to verify, trigger re-download and remove the
|
# Checksums failed to verify, trigger re-download and remove the
|
||||||
# incorrect stamp file.
|
# incorrect stamp file.
|
||||||
logger.warning("Checksum mismatch for local file %s\n"
|
logger.warn("Checksum mismatch for local file %s\n"
|
||||||
"Cleaning and trying again." % ud.localpath)
|
"Cleaning and trying again." % ud.localpath)
|
||||||
if os.path.exists(ud.localpath):
|
rename_bad_checksum(ud, e.checksum)
|
||||||
rename_bad_checksum(ud, e.checksum)
|
|
||||||
bb.utils.remove(ud.donestamp)
|
bb.utils.remove(ud.donestamp)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -683,9 +695,6 @@ def update_stamp(ud, d):
|
|||||||
donestamp is file stamp indicating the whole fetching is done
|
donestamp is file stamp indicating the whole fetching is done
|
||||||
this function update the stamp after verifying the checksum
|
this function update the stamp after verifying the checksum
|
||||||
"""
|
"""
|
||||||
if not ud.needdonestamp:
|
|
||||||
return
|
|
||||||
|
|
||||||
if os.path.exists(ud.donestamp):
|
if os.path.exists(ud.donestamp):
|
||||||
# Touch the done stamp file to show active use of the download
|
# Touch the done stamp file to show active use of the download
|
||||||
try:
|
try:
|
||||||
@@ -694,21 +703,11 @@ def update_stamp(ud, d):
|
|||||||
# Errors aren't fatal here
|
# Errors aren't fatal here
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
checksums = verify_checksum(ud, d)
|
||||||
checksums = verify_checksum(ud, d)
|
# Store the checksums for later re-verification against the recipe
|
||||||
# Store the checksums for later re-verification against the recipe
|
with open(ud.donestamp, "wb") as cachefile:
|
||||||
with open(ud.donestamp, "wb") as cachefile:
|
p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
|
||||||
p = pickle.Pickler(cachefile, 2)
|
p.dump(checksums)
|
||||||
p.dump(checksums)
|
|
||||||
except ChecksumError as e:
|
|
||||||
# Checksums failed to verify, trigger re-download and remove the
|
|
||||||
# incorrect stamp file.
|
|
||||||
logger.warning("Checksum mismatch for local file %s\n"
|
|
||||||
"Cleaning and trying again." % ud.localpath)
|
|
||||||
if os.path.exists(ud.localpath):
|
|
||||||
rename_bad_checksum(ud, e.checksum)
|
|
||||||
bb.utils.remove(ud.donestamp)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def subprocess_setup():
|
def subprocess_setup():
|
||||||
# Python installs a SIGPIPE handler by default. This is usually not what
|
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||||
@@ -719,7 +718,7 @@ def subprocess_setup():
|
|||||||
def get_autorev(d):
|
def get_autorev(d):
|
||||||
# only not cache src rev in autorev case
|
# only not cache src rev in autorev case
|
||||||
if d.getVar('BB_SRCREV_POLICY', True) != "cache":
|
if d.getVar('BB_SRCREV_POLICY', True) != "cache":
|
||||||
d.setVar('BB_DONT_CACHE', '1')
|
d.setVar('__BB_DONT_CACHE', '1')
|
||||||
return "AUTOINC"
|
return "AUTOINC"
|
||||||
|
|
||||||
def get_srcrev(d, method_name='sortable_revision'):
|
def get_srcrev(d, method_name='sortable_revision'):
|
||||||
@@ -761,7 +760,6 @@ def get_srcrev(d, method_name='sortable_revision'):
|
|||||||
if not format:
|
if not format:
|
||||||
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
|
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
|
||||||
|
|
||||||
name_to_rev = {}
|
|
||||||
seenautoinc = False
|
seenautoinc = False
|
||||||
for scm in scms:
|
for scm in scms:
|
||||||
ud = urldata[scm]
|
ud = urldata[scm]
|
||||||
@@ -770,16 +768,7 @@ def get_srcrev(d, method_name='sortable_revision'):
|
|||||||
seenautoinc = seenautoinc or autoinc
|
seenautoinc = seenautoinc or autoinc
|
||||||
if len(rev) > 10:
|
if len(rev) > 10:
|
||||||
rev = rev[:10]
|
rev = rev[:10]
|
||||||
name_to_rev[name] = rev
|
format = format.replace(name, rev)
|
||||||
# Replace names by revisions in the SRCREV_FORMAT string. The approach used
|
|
||||||
# here can handle names being prefixes of other names and names appearing
|
|
||||||
# as substrings in revisions (in which case the name should not be
|
|
||||||
# expanded). The '|' regular expression operator tries matches from left to
|
|
||||||
# right, so we need to sort the names with the longest ones first.
|
|
||||||
names_descending_len = sorted(name_to_rev, key=len, reverse=True)
|
|
||||||
name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len)
|
|
||||||
format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format)
|
|
||||||
|
|
||||||
if seenautoinc:
|
if seenautoinc:
|
||||||
format = "AUTOINC+" + format
|
format = "AUTOINC+" + format
|
||||||
|
|
||||||
@@ -789,7 +778,7 @@ def localpath(url, d):
|
|||||||
fetcher = bb.fetch2.Fetch([url], d)
|
fetcher = bb.fetch2.Fetch([url], d)
|
||||||
return fetcher.localpath(url)
|
return fetcher.localpath(url)
|
||||||
|
|
||||||
def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
def runfetchcmd(cmd, d, quiet=False, cleanup=None):
|
||||||
"""
|
"""
|
||||||
Run cmd returning the command output
|
Run cmd returning the command output
|
||||||
Raise an error if interrupted or cmd fails
|
Raise an error if interrupted or cmd fails
|
||||||
@@ -812,16 +801,13 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
|||||||
'GIT_SSL_CAINFO',
|
'GIT_SSL_CAINFO',
|
||||||
'GIT_SMART_HTTP',
|
'GIT_SMART_HTTP',
|
||||||
'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
|
'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
|
||||||
'SOCKS5_USER', 'SOCKS5_PASSWD',
|
'SOCKS5_USER', 'SOCKS5_PASSWD']
|
||||||
'DBUS_SESSION_BUS_ADDRESS',
|
|
||||||
'P4CONFIG']
|
|
||||||
|
|
||||||
if not cleanup:
|
if not cleanup:
|
||||||
cleanup = []
|
cleanup = []
|
||||||
|
|
||||||
origenv = d.getVar("BB_ORIGENV", False)
|
|
||||||
for var in exportvars:
|
for var in exportvars:
|
||||||
val = d.getVar(var, True) or (origenv and origenv.getVar(var, True))
|
val = d.getVar(var, True)
|
||||||
if val:
|
if val:
|
||||||
cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
|
cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
|
||||||
|
|
||||||
@@ -831,7 +817,7 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
|||||||
error_message = ""
|
error_message = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir)
|
(output, errors) = bb.process.run(cmd, shell=True, stderr=subprocess.PIPE)
|
||||||
success = True
|
success = True
|
||||||
except bb.process.NotFoundError as e:
|
except bb.process.NotFoundError as e:
|
||||||
error_message = "Fetch command %s" % (e.command)
|
error_message = "Fetch command %s" % (e.command)
|
||||||
@@ -842,7 +828,7 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
|||||||
output = "output:\n%s" % e.stderr
|
output = "output:\n%s" % e.stderr
|
||||||
else:
|
else:
|
||||||
output = "no output"
|
output = "no output"
|
||||||
error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output)
|
error_message = "Fetch command failed with exit code %s, %s" % (e.exitcode, output)
|
||||||
except bb.process.CmdError as e:
|
except bb.process.CmdError as e:
|
||||||
error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
|
error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
|
||||||
if not success:
|
if not success:
|
||||||
@@ -934,10 +920,6 @@ def rename_bad_checksum(ud, suffix):
|
|||||||
def try_mirror_url(fetch, origud, ud, ld, check = False):
|
def try_mirror_url(fetch, origud, ud, ld, check = False):
|
||||||
# Return of None or a value means we're finished
|
# Return of None or a value means we're finished
|
||||||
# False means try another url
|
# False means try another url
|
||||||
|
|
||||||
if ud.lockfile and ud.lockfile != origud.lockfile:
|
|
||||||
lf = bb.utils.lockfile(ud.lockfile)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if check:
|
if check:
|
||||||
found = ud.method.checkstatus(fetch, ud, ld)
|
found = ud.method.checkstatus(fetch, ud, ld)
|
||||||
@@ -945,6 +927,8 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
|||||||
return found
|
return found
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
os.chdir(ld.getVar("DL_DIR", True))
|
||||||
|
|
||||||
if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
|
if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
|
||||||
ud.method.download(ud, ld)
|
ud.method.download(ud, ld)
|
||||||
if hasattr(ud.method,"build_mirror_data"):
|
if hasattr(ud.method,"build_mirror_data"):
|
||||||
@@ -962,9 +946,8 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
|||||||
if origud.mirrortarball and os.path.basename(ud.localpath) == os.path.basename(origud.mirrortarball) \
|
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):
|
and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
|
||||||
# Create donestamp in old format to avoid triggering a re-download
|
# Create donestamp in old format to avoid triggering a re-download
|
||||||
if ud.donestamp:
|
bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
|
||||||
bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
|
open(ud.donestamp, 'w').close()
|
||||||
open(ud.donestamp, 'w').close()
|
|
||||||
dest = os.path.join(dldir, os.path.basename(ud.localpath))
|
dest = os.path.join(dldir, os.path.basename(ud.localpath))
|
||||||
if not os.path.exists(dest):
|
if not os.path.exists(dest):
|
||||||
os.symlink(ud.localpath, dest)
|
os.symlink(ud.localpath, dest)
|
||||||
@@ -988,10 +971,9 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
|||||||
|
|
||||||
except bb.fetch2.BBFetchException as e:
|
except bb.fetch2.BBFetchException as e:
|
||||||
if isinstance(e, ChecksumError):
|
if isinstance(e, ChecksumError):
|
||||||
logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
|
logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
|
||||||
logger.warning(str(e))
|
logger.warn(str(e))
|
||||||
if os.path.exists(ud.localpath):
|
rename_bad_checksum(ud, e.checksum)
|
||||||
rename_bad_checksum(ud, e.checksum)
|
|
||||||
elif isinstance(e, NoChecksumError):
|
elif isinstance(e, NoChecksumError):
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
@@ -1002,10 +984,6 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
|||||||
except UnboundLocalError:
|
except UnboundLocalError:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
finally:
|
|
||||||
if ud.lockfile and ud.lockfile != origud.lockfile:
|
|
||||||
bb.utils.unlockfile(lf)
|
|
||||||
|
|
||||||
|
|
||||||
def try_mirrors(fetch, d, origud, mirrors, check = False):
|
def try_mirrors(fetch, d, origud, mirrors, check = False):
|
||||||
"""
|
"""
|
||||||
@@ -1036,7 +1014,7 @@ def trusted_network(d, url):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
pkgname = d.expand(d.getVar('PN', False))
|
pkgname = d.expand(d.getVar('PN', False))
|
||||||
trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
|
trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname)
|
||||||
|
|
||||||
if not trusted_hosts:
|
if not trusted_hosts:
|
||||||
trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS', True)
|
trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS', True)
|
||||||
@@ -1050,7 +1028,6 @@ def trusted_network(d, url):
|
|||||||
if not network:
|
if not network:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
network = network.split(':')[0]
|
|
||||||
network = network.lower()
|
network = network.lower()
|
||||||
|
|
||||||
for host in trusted_hosts.split(" "):
|
for host in trusted_hosts.split(" "):
|
||||||
@@ -1143,7 +1120,48 @@ def get_file_checksums(filelist, pn):
|
|||||||
it proceeds
|
it proceeds
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return _checksum_cache.get_checksums(filelist, pn)
|
|
||||||
|
def checksum_file(f):
|
||||||
|
try:
|
||||||
|
checksum = _checksum_cache.get_checksum(f)
|
||||||
|
except OSError as e:
|
||||||
|
bb.warn("Unable to get checksum for %s SRC_URI entry %s: %s" % (pn, os.path.basename(f), e))
|
||||||
|
return None
|
||||||
|
return checksum
|
||||||
|
|
||||||
|
def checksum_dir(pth):
|
||||||
|
# Handle directories recursively
|
||||||
|
dirchecksums = []
|
||||||
|
for root, dirs, files in os.walk(pth):
|
||||||
|
for name in files:
|
||||||
|
fullpth = os.path.join(root, name)
|
||||||
|
checksum = checksum_file(fullpth)
|
||||||
|
if checksum:
|
||||||
|
dirchecksums.append((fullpth, checksum))
|
||||||
|
return dirchecksums
|
||||||
|
|
||||||
|
checksums = []
|
||||||
|
for pth in filelist.split():
|
||||||
|
exist = pth.split(":")[1]
|
||||||
|
if exist == "False":
|
||||||
|
continue
|
||||||
|
pth = pth.split(":")[0]
|
||||||
|
if '*' in pth:
|
||||||
|
# Handle globs
|
||||||
|
for f in glob.glob(pth):
|
||||||
|
if os.path.isdir(f):
|
||||||
|
checksums.extend(checksum_dir(f))
|
||||||
|
else:
|
||||||
|
checksum = checksum_file(f)
|
||||||
|
checksums.append((f, checksum))
|
||||||
|
elif os.path.isdir(pth):
|
||||||
|
checksums.extend(checksum_dir(pth))
|
||||||
|
else:
|
||||||
|
checksum = checksum_file(pth)
|
||||||
|
checksums.append((pth, checksum))
|
||||||
|
|
||||||
|
checksums.sort(key=operator.itemgetter(1))
|
||||||
|
return checksums
|
||||||
|
|
||||||
|
|
||||||
class FetchData(object):
|
class FetchData(object):
|
||||||
@@ -1153,7 +1171,6 @@ class FetchData(object):
|
|||||||
def __init__(self, url, d, localonly = False):
|
def __init__(self, url, d, localonly = False):
|
||||||
# localpath is the location of a downloaded result. If not set, the file is local.
|
# localpath is the location of a downloaded result. If not set, the file is local.
|
||||||
self.donestamp = None
|
self.donestamp = None
|
||||||
self.needdonestamp = True
|
|
||||||
self.localfile = ""
|
self.localfile = ""
|
||||||
self.localpath = None
|
self.localpath = None
|
||||||
self.lockfile = None
|
self.lockfile = None
|
||||||
@@ -1180,13 +1197,13 @@ class FetchData(object):
|
|||||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||||
self.md5_expected = None
|
self.md5_expected = None
|
||||||
else:
|
else:
|
||||||
self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name, True)
|
self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
|
||||||
if self.sha256_name in self.parm:
|
if self.sha256_name in self.parm:
|
||||||
self.sha256_expected = self.parm[self.sha256_name]
|
self.sha256_expected = self.parm[self.sha256_name]
|
||||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||||
self.sha256_expected = None
|
self.sha256_expected = None
|
||||||
else:
|
else:
|
||||||
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name, True)
|
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
|
||||||
self.ignore_checksums = False
|
self.ignore_checksums = False
|
||||||
|
|
||||||
self.names = self.parm.get("name",'default').split(',')
|
self.names = self.parm.get("name",'default').split(',')
|
||||||
@@ -1204,7 +1221,7 @@ class FetchData(object):
|
|||||||
raise NonLocalMethod()
|
raise NonLocalMethod()
|
||||||
|
|
||||||
if self.parm.get("proto", None) and "protocol" not in self.parm:
|
if self.parm.get("proto", None) and "protocol" not in self.parm:
|
||||||
logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN', True))
|
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)
|
self.parm["protocol"] = self.parm.get("proto", None)
|
||||||
|
|
||||||
if hasattr(self.method, "urldata_init"):
|
if hasattr(self.method, "urldata_init"):
|
||||||
@@ -1218,20 +1235,13 @@ class FetchData(object):
|
|||||||
self.localpath = self.method.localpath(self, d)
|
self.localpath = self.method.localpath(self, d)
|
||||||
|
|
||||||
dldir = d.getVar("DL_DIR", True)
|
dldir = d.getVar("DL_DIR", True)
|
||||||
|
|
||||||
if not self.needdonestamp:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
|
# Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
|
||||||
if self.localpath and self.localpath.startswith(dldir):
|
if self.localpath and self.localpath.startswith(dldir):
|
||||||
basepath = self.localpath
|
basepath = self.localpath
|
||||||
elif self.localpath:
|
elif self.localpath:
|
||||||
basepath = dldir + os.sep + os.path.basename(self.localpath)
|
basepath = dldir + os.sep + os.path.basename(self.localpath)
|
||||||
elif self.basepath or self.basename:
|
|
||||||
basepath = dldir + os.sep + (self.basepath or self.basename)
|
|
||||||
else:
|
else:
|
||||||
bb.fatal("Can't determine lock path for url %s" % url)
|
basepath = dldir + os.sep + (self.basepath or self.basename)
|
||||||
|
|
||||||
self.donestamp = basepath + '.done'
|
self.donestamp = basepath + '.done'
|
||||||
self.lockfile = basepath + '.lock'
|
self.lockfile = basepath + '.lock'
|
||||||
|
|
||||||
@@ -1345,11 +1355,6 @@ class FetchMethod(object):
|
|||||||
iterate = False
|
iterate = False
|
||||||
file = urldata.localpath
|
file = urldata.localpath
|
||||||
|
|
||||||
# Localpath can't deal with 'dir/*' entries, so it converts them to '.',
|
|
||||||
# but it must be corrected back for local files copying
|
|
||||||
if urldata.basename == '*' and file.endswith('/.'):
|
|
||||||
file = '%s/%s' % (file.rstrip('/.'), urldata.path)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
|
unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
@@ -1401,60 +1406,60 @@ class FetchMethod(object):
|
|||||||
else:
|
else:
|
||||||
cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
|
cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
|
||||||
elif file.endswith('.deb') or file.endswith('.ipk'):
|
elif file.endswith('.deb') or file.endswith('.ipk'):
|
||||||
output = subprocess.check_output('ar -t %s' % file, preexec_fn=subprocess_setup, shell=True)
|
cmd = 'ar -p %s data.tar.gz | zcat | tar --no-same-owner -xpf -' % file
|
||||||
datafile = None
|
|
||||||
if output:
|
|
||||||
for line in output.decode().splitlines():
|
|
||||||
if line.startswith('data.tar.'):
|
|
||||||
datafile = line
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url)
|
|
||||||
else:
|
|
||||||
raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
|
|
||||||
cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
|
|
||||||
elif file.endswith('.tar.7z'):
|
|
||||||
cmd = '7z x -so %s | tar xf - ' % file
|
|
||||||
elif file.endswith('.7z'):
|
|
||||||
cmd = '7za x -y %s 1>/dev/null' % file
|
|
||||||
|
|
||||||
# If 'subdir' param exists, create a dir and use it as destination for unpack cmd
|
|
||||||
if 'subdir' in urldata.parm:
|
|
||||||
subdir = urldata.parm.get('subdir')
|
|
||||||
if os.path.isabs(subdir):
|
|
||||||
if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)):
|
|
||||||
raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url)
|
|
||||||
unpackdir = subdir
|
|
||||||
else:
|
|
||||||
unpackdir = os.path.join(rootdir, subdir)
|
|
||||||
bb.utils.mkdirhier(unpackdir)
|
|
||||||
else:
|
|
||||||
unpackdir = rootdir
|
|
||||||
|
|
||||||
if not unpack or not cmd:
|
if not unpack or not cmd:
|
||||||
# If file == dest, then avoid any copies, as we already put the file into dest!
|
# If file == dest, then avoid any copies, as we already put the file into dest!
|
||||||
dest = os.path.join(unpackdir, os.path.basename(file))
|
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 (file != dest) and not (os.path.exists(dest) and os.path.samefile(file, dest)):
|
||||||
destdir = '.'
|
if os.path.isdir(file):
|
||||||
# For file:// entries all intermediate dirs in path must be created at destination
|
# If for example we're asked to copy file://foo/bar, we need to unpack the result into foo/bar
|
||||||
if urldata.type == "file":
|
basepath = getattr(urldata, "basepath", None)
|
||||||
# Trailing '/' does a copying to wrong place
|
destdir = "."
|
||||||
urlpath = urldata.path.rstrip('/')
|
if basepath and basepath.endswith("/"):
|
||||||
# Want files places relative to cwd so no leading '/'
|
basepath = basepath.rstrip("/")
|
||||||
urlpath = urlpath.lstrip('/')
|
elif basepath:
|
||||||
if urlpath.find("/") != -1:
|
basepath = os.path.dirname(basepath)
|
||||||
destdir = urlpath.rsplit("/", 1)[0] + '/'
|
if basepath and basepath.find("/") != -1:
|
||||||
bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
|
destdir = basepath[:basepath.rfind('/')]
|
||||||
cmd = 'cp -fpPRH %s %s' % (file, destdir)
|
destdir = destdir.strip('/')
|
||||||
|
if destdir != "." and not os.access("%s/%s" % (rootdir, destdir), os.F_OK):
|
||||||
|
os.makedirs("%s/%s" % (rootdir, destdir))
|
||||||
|
cmd = 'cp -fpPR %s %s/%s/' % (file, rootdir, destdir)
|
||||||
|
#cmd = 'tar -cf - -C "%d" -ps . | tar -xf - -C "%s/%s/"' % (file, rootdir, destdir)
|
||||||
|
else:
|
||||||
|
# The "destdir" handling was specifically done for FILESPATH
|
||||||
|
# items. So, only do so for file:// entries.
|
||||||
|
if urldata.type == "file" and urldata.path.find("/") != -1:
|
||||||
|
destdir = urldata.path.rsplit("/", 1)[0]
|
||||||
|
if urldata.parm.get('subdir') != None:
|
||||||
|
destdir = urldata.parm.get('subdir') + "/" + destdir
|
||||||
|
else:
|
||||||
|
if urldata.parm.get('subdir') != None:
|
||||||
|
destdir = urldata.parm.get('subdir')
|
||||||
|
else:
|
||||||
|
destdir = "."
|
||||||
|
bb.utils.mkdirhier("%s/%s" % (rootdir, destdir))
|
||||||
|
cmd = 'cp -f %s %s/%s/' % (file, rootdir, destdir)
|
||||||
|
|
||||||
if not cmd:
|
if not cmd:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Change to subdir before executing command
|
||||||
|
save_cwd = os.getcwd();
|
||||||
|
os.chdir(rootdir)
|
||||||
|
if 'subdir' in urldata.parm:
|
||||||
|
newdir = ("%s/%s" % (rootdir, urldata.parm.get('subdir')))
|
||||||
|
bb.utils.mkdirhier(newdir)
|
||||||
|
os.chdir(newdir)
|
||||||
|
|
||||||
path = data.getVar('PATH', True)
|
path = data.getVar('PATH', True)
|
||||||
if path:
|
if path:
|
||||||
cmd = "PATH=\"%s\" %s" % (path, cmd)
|
cmd = "PATH=\"%s\" %s" % (path, cmd)
|
||||||
bb.note("Unpacking %s to %s/" % (file, unpackdir))
|
bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
|
||||||
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
|
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True)
|
||||||
|
|
||||||
|
os.chdir(save_cwd)
|
||||||
|
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
|
raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
|
||||||
@@ -1522,9 +1527,8 @@ class Fetch(object):
|
|||||||
self.connection_cache = connection_cache
|
self.connection_cache = connection_cache
|
||||||
|
|
||||||
fn = d.getVar('FILE', True)
|
fn = d.getVar('FILE', True)
|
||||||
mc = d.getVar('__BBMULTICONFIG', True) or ""
|
if cache and fn and fn in urldata_cache:
|
||||||
if cache and fn and mc + fn in urldata_cache:
|
self.ud = urldata_cache[fn]
|
||||||
self.ud = urldata_cache[mc + fn]
|
|
||||||
|
|
||||||
for url in urls:
|
for url in urls:
|
||||||
if url not in self.ud:
|
if url not in self.ud:
|
||||||
@@ -1536,7 +1540,7 @@ class Fetch(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if fn and cache:
|
if fn and cache:
|
||||||
urldata_cache[mc + fn] = self.ud
|
urldata_cache[fn] = self.ud
|
||||||
|
|
||||||
def localpath(self, url):
|
def localpath(self, url):
|
||||||
if url not in self.urls:
|
if url not in self.urls:
|
||||||
@@ -1574,8 +1578,7 @@ class Fetch(object):
|
|||||||
m = ud.method
|
m = ud.method
|
||||||
localpath = ""
|
localpath = ""
|
||||||
|
|
||||||
if ud.lockfile:
|
lf = bb.utils.lockfile(ud.lockfile)
|
||||||
lf = bb.utils.lockfile(ud.lockfile)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.d.setVar("BB_NO_NETWORK", network)
|
self.d.setVar("BB_NO_NETWORK", network)
|
||||||
@@ -1590,6 +1593,8 @@ class Fetch(object):
|
|||||||
if premirroronly:
|
if premirroronly:
|
||||||
self.d.setVar("BB_NO_NETWORK", "1")
|
self.d.setVar("BB_NO_NETWORK", "1")
|
||||||
|
|
||||||
|
os.chdir(self.d.getVar("DL_DIR", True))
|
||||||
|
|
||||||
firsterr = None
|
firsterr = None
|
||||||
verified_stamp = verify_donestamp(ud, self.d)
|
verified_stamp = verify_donestamp(ud, self.d)
|
||||||
if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
|
if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
|
||||||
@@ -1610,14 +1615,13 @@ class Fetch(object):
|
|||||||
|
|
||||||
except BBFetchException as e:
|
except BBFetchException as e:
|
||||||
if isinstance(e, ChecksumError):
|
if isinstance(e, ChecksumError):
|
||||||
logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
|
logger.warn("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
|
||||||
logger.debug(1, str(e))
|
logger.debug(1, str(e))
|
||||||
if os.path.exists(ud.localpath):
|
rename_bad_checksum(ud, e.checksum)
|
||||||
rename_bad_checksum(ud, e.checksum)
|
|
||||||
elif isinstance(e, NoChecksumError):
|
elif isinstance(e, NoChecksumError):
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u)
|
logger.warn('Failed to fetch URL %s, attempting MIRRORS if available' % u)
|
||||||
logger.debug(1, str(e))
|
logger.debug(1, str(e))
|
||||||
firsterr = e
|
firsterr = e
|
||||||
# Remove any incomplete fetch
|
# Remove any incomplete fetch
|
||||||
@@ -1640,8 +1644,7 @@ class Fetch(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if ud.lockfile:
|
bb.utils.unlockfile(lf)
|
||||||
bb.utils.unlockfile(lf)
|
|
||||||
|
|
||||||
def checkstatus(self, urls=None):
|
def checkstatus(self, urls=None):
|
||||||
"""
|
"""
|
||||||
@@ -1683,6 +1686,9 @@ class Fetch(object):
|
|||||||
ud = self.ud[u]
|
ud = self.ud[u]
|
||||||
ud.setup_localpath(self.d)
|
ud.setup_localpath(self.d)
|
||||||
|
|
||||||
|
if self.d.expand(self.localpath) is None:
|
||||||
|
continue
|
||||||
|
|
||||||
if ud.lockfile:
|
if ud.lockfile:
|
||||||
lf = bb.utils.lockfile(ud.lockfile)
|
lf = bb.utils.lockfile(ud.lockfile)
|
||||||
|
|
||||||
@@ -1750,7 +1756,7 @@ class FetchConnectionCache(object):
|
|||||||
del self.cache[cn]
|
del self.cache[cn]
|
||||||
|
|
||||||
def close_connections(self):
|
def close_connections(self):
|
||||||
for cn in list(self.cache.keys()):
|
for cn in self.cache.keys():
|
||||||
self.cache[cn].close()
|
self.cache[cn].close()
|
||||||
del self.cache[cn]
|
del self.cache[cn]
|
||||||
|
|
||||||
@@ -1769,7 +1775,6 @@ from . import hg
|
|||||||
from . import osc
|
from . import osc
|
||||||
from . import repo
|
from . import repo
|
||||||
from . import clearcase
|
from . import clearcase
|
||||||
from . import npm
|
|
||||||
|
|
||||||
methods.append(local.Local())
|
methods.append(local.Local())
|
||||||
methods.append(wget.Wget())
|
methods.append(wget.Wget())
|
||||||
@@ -1786,4 +1791,3 @@ methods.append(hg.Hg())
|
|||||||
methods.append(osc.Osc())
|
methods.append(osc.Osc())
|
||||||
methods.append(repo.Repo())
|
methods.append(repo.Repo())
|
||||||
methods.append(clearcase.ClearCase())
|
methods.append(clearcase.ClearCase())
|
||||||
methods.append(npm.Npm())
|
|
||||||
|
|||||||
@@ -88,25 +88,28 @@ class Bzr(FetchMethod):
|
|||||||
bzrcmd = self._buildbzrcommand(ud, d, "update")
|
bzrcmd = self._buildbzrcommand(ud, d, "update")
|
||||||
logger.debug(1, "BZR Update %s", ud.url)
|
logger.debug(1, "BZR Update %s", ud.url)
|
||||||
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
|
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
|
||||||
runfetchcmd(bzrcmd, d, workdir=os.path.join(ud.pkgdir, os.path.basename(ud.path)))
|
os.chdir(os.path.join (ud.pkgdir, os.path.basename(ud.path)))
|
||||||
|
runfetchcmd(bzrcmd, d)
|
||||||
else:
|
else:
|
||||||
bb.utils.remove(os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir)), True)
|
bb.utils.remove(os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir)), True)
|
||||||
bzrcmd = self._buildbzrcommand(ud, d, "fetch")
|
bzrcmd = self._buildbzrcommand(ud, d, "fetch")
|
||||||
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
|
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
|
||||||
logger.debug(1, "BZR Checkout %s", ud.url)
|
logger.debug(1, "BZR Checkout %s", ud.url)
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
bb.utils.mkdirhier(ud.pkgdir)
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
logger.debug(1, "Running %s", bzrcmd)
|
logger.debug(1, "Running %s", bzrcmd)
|
||||||
runfetchcmd(bzrcmd, d, workdir=ud.pkgdir)
|
runfetchcmd(bzrcmd, d)
|
||||||
|
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
|
|
||||||
scmdata = ud.parm.get("scmdata", "")
|
scmdata = ud.parm.get("scmdata", "")
|
||||||
if scmdata == "keep":
|
if scmdata == "keep":
|
||||||
tar_flags = ""
|
tar_flags = ""
|
||||||
else:
|
else:
|
||||||
tar_flags = "--exclude='.bzr' --exclude='.bzrtags'"
|
tar_flags = "--exclude '.bzr' --exclude '.bzrtags'"
|
||||||
|
|
||||||
# tar them up to a defined filename
|
# tar them up to a defined filename
|
||||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(ud.pkgdir)),
|
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(ud.pkgdir)), d, cleanup = [ud.localpath])
|
||||||
d, cleanup=[ud.localpath], workdir=ud.pkgdir)
|
|
||||||
|
|
||||||
def supports_srcrev(self):
|
def supports_srcrev(self):
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -202,10 +202,11 @@ class ClearCase(FetchMethod):
|
|||||||
|
|
||||||
def _remove_view(self, ud, d):
|
def _remove_view(self, ud, d):
|
||||||
if os.path.exists(ud.viewdir):
|
if os.path.exists(ud.viewdir):
|
||||||
|
os.chdir(ud.ccasedir)
|
||||||
cmd = self._build_ccase_command(ud, 'rmview');
|
cmd = self._build_ccase_command(ud, 'rmview');
|
||||||
logger.info("cleaning up [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
logger.info("cleaning up [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
||||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||||
output = runfetchcmd(cmd, d, workdir=ud.ccasedir)
|
output = runfetchcmd(cmd, d)
|
||||||
logger.info("rmview output: %s", output)
|
logger.info("rmview output: %s", output)
|
||||||
|
|
||||||
def need_update(self, ud, d):
|
def need_update(self, ud, d):
|
||||||
@@ -240,10 +241,11 @@ class ClearCase(FetchMethod):
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
# Set configspec: Setting the configspec effectively fetches the files as defined in the configspec
|
# Set configspec: Setting the configspec effectively fetches the files as defined in the configspec
|
||||||
|
os.chdir(ud.viewdir)
|
||||||
cmd = self._build_ccase_command(ud, 'setcs');
|
cmd = self._build_ccase_command(ud, 'setcs');
|
||||||
logger.info("fetching data [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
logger.info("fetching data [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
||||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||||
output = runfetchcmd(cmd, d, workdir=ud.viewdir)
|
output = runfetchcmd(cmd, d)
|
||||||
logger.info("%s", output)
|
logger.info("%s", output)
|
||||||
|
|
||||||
# Copy the configspec to the viewdir so we have it in our source tarball later
|
# Copy the configspec to the viewdir so we have it in our source tarball later
|
||||||
|
|||||||
@@ -123,23 +123,22 @@ class Cvs(FetchMethod):
|
|||||||
pkg = d.getVar('PN', True)
|
pkg = d.getVar('PN', True)
|
||||||
pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
|
pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
|
||||||
moddir = os.path.join(pkgdir, localdir)
|
moddir = os.path.join(pkgdir, localdir)
|
||||||
workdir = None
|
|
||||||
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
|
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
|
||||||
logger.info("Update " + ud.url)
|
logger.info("Update " + ud.url)
|
||||||
bb.fetch2.check_network_access(d, cvsupdatecmd, ud.url)
|
bb.fetch2.check_network_access(d, cvsupdatecmd, ud.url)
|
||||||
# update sources there
|
# update sources there
|
||||||
workdir = moddir
|
os.chdir(moddir)
|
||||||
cmd = cvsupdatecmd
|
cmd = cvsupdatecmd
|
||||||
else:
|
else:
|
||||||
logger.info("Fetch " + ud.url)
|
logger.info("Fetch " + ud.url)
|
||||||
# check out sources there
|
# check out sources there
|
||||||
bb.utils.mkdirhier(pkgdir)
|
bb.utils.mkdirhier(pkgdir)
|
||||||
workdir = pkgdir
|
os.chdir(pkgdir)
|
||||||
logger.debug(1, "Running %s", cvscmd)
|
logger.debug(1, "Running %s", cvscmd)
|
||||||
bb.fetch2.check_network_access(d, cvscmd, ud.url)
|
bb.fetch2.check_network_access(d, cvscmd, ud.url)
|
||||||
cmd = cvscmd
|
cmd = cvscmd
|
||||||
|
|
||||||
runfetchcmd(cmd, d, cleanup=[moddir], workdir=workdir)
|
runfetchcmd(cmd, d, cleanup = [moddir])
|
||||||
|
|
||||||
if not os.access(moddir, os.R_OK):
|
if not os.access(moddir, os.R_OK):
|
||||||
raise FetchError("Directory %s was not readable despite sucessful fetch?!" % moddir, ud.url)
|
raise FetchError("Directory %s was not readable despite sucessful fetch?!" % moddir, ud.url)
|
||||||
@@ -148,18 +147,18 @@ class Cvs(FetchMethod):
|
|||||||
if scmdata == "keep":
|
if scmdata == "keep":
|
||||||
tar_flags = ""
|
tar_flags = ""
|
||||||
else:
|
else:
|
||||||
tar_flags = "--exclude='CVS'"
|
tar_flags = "--exclude 'CVS'"
|
||||||
|
|
||||||
# tar them up to a defined filename
|
# tar them up to a defined filename
|
||||||
workdir = None
|
|
||||||
if 'fullpath' in ud.parm:
|
if 'fullpath' in ud.parm:
|
||||||
workdir = pkgdir
|
os.chdir(pkgdir)
|
||||||
cmd = "tar %s -czf %s %s" % (tar_flags, ud.localpath, localdir)
|
cmd = "tar %s -czf %s %s" % (tar_flags, ud.localpath, localdir)
|
||||||
else:
|
else:
|
||||||
workdir = os.path.dirname(os.path.realpath(moddir))
|
os.chdir(moddir)
|
||||||
|
os.chdir('..')
|
||||||
cmd = "tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(moddir))
|
cmd = "tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(moddir))
|
||||||
|
|
||||||
runfetchcmd(cmd, d, cleanup=[ud.localpath], workdir=workdir)
|
runfetchcmd(cmd, d, cleanup = [ud.localpath])
|
||||||
|
|
||||||
def clean(self, ud, d):
|
def clean(self, ud, d):
|
||||||
""" Clean CVS Files and tarballs """
|
""" Clean CVS Files and tarballs """
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ Supported SRC_URI options are:
|
|||||||
referring to commit which is valid in tag instead of branch.
|
referring to commit which is valid in tag instead of branch.
|
||||||
The default is "0", set nobranch=1 if needed.
|
The default is "0", set nobranch=1 if needed.
|
||||||
|
|
||||||
- usehead
|
|
||||||
For local git:// urls to use the current branch HEAD as the revsion for use with
|
|
||||||
AUTOREV. Implies nobranch.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#Copyright (C) 2005 Richard Purdie
|
#Copyright (C) 2005 Richard Purdie
|
||||||
@@ -74,54 +70,11 @@ import errno
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import bb
|
import bb
|
||||||
import errno
|
|
||||||
import bb.progress
|
|
||||||
from bb import data
|
from bb import data
|
||||||
from bb.fetch2 import FetchMethod
|
from bb.fetch2 import FetchMethod
|
||||||
from bb.fetch2 import runfetchcmd
|
from bb.fetch2 import runfetchcmd
|
||||||
from bb.fetch2 import logger
|
from bb.fetch2 import logger
|
||||||
|
|
||||||
|
|
||||||
class GitProgressHandler(bb.progress.LineFilterProgressHandler):
|
|
||||||
"""Extract progress information from git output"""
|
|
||||||
def __init__(self, d):
|
|
||||||
self._buffer = ''
|
|
||||||
self._count = 0
|
|
||||||
super(GitProgressHandler, self).__init__(d)
|
|
||||||
# Send an initial progress event so the bar gets shown
|
|
||||||
self._fire_progress(-1)
|
|
||||||
|
|
||||||
def write(self, string):
|
|
||||||
self._buffer += string
|
|
||||||
stages = ['Counting objects', 'Compressing objects', 'Receiving objects', 'Resolving deltas']
|
|
||||||
stage_weights = [0.2, 0.05, 0.5, 0.25]
|
|
||||||
stagenum = 0
|
|
||||||
for i, stage in reversed(list(enumerate(stages))):
|
|
||||||
if stage in self._buffer:
|
|
||||||
stagenum = i
|
|
||||||
self._buffer = ''
|
|
||||||
break
|
|
||||||
self._status = stages[stagenum]
|
|
||||||
percs = re.findall(r'(\d+)%', string)
|
|
||||||
if percs:
|
|
||||||
progress = int(round((int(percs[-1]) * stage_weights[stagenum]) + (sum(stage_weights[:stagenum]) * 100)))
|
|
||||||
rates = re.findall(r'([\d.]+ [a-zA-Z]*/s+)', string)
|
|
||||||
if rates:
|
|
||||||
rate = rates[-1]
|
|
||||||
else:
|
|
||||||
rate = None
|
|
||||||
self.update(progress, rate)
|
|
||||||
else:
|
|
||||||
if stagenum == 0:
|
|
||||||
percs = re.findall(r': (\d+)', string)
|
|
||||||
if percs:
|
|
||||||
count = int(percs[-1])
|
|
||||||
if count > self._count:
|
|
||||||
self._count = count
|
|
||||||
self._fire_progress(-count)
|
|
||||||
super(GitProgressHandler, self).write(string)
|
|
||||||
|
|
||||||
|
|
||||||
class Git(FetchMethod):
|
class Git(FetchMethod):
|
||||||
"""Class to fetch a module or modules from git repositories"""
|
"""Class to fetch a module or modules from git repositories"""
|
||||||
def init(self, d):
|
def init(self, d):
|
||||||
@@ -157,13 +110,6 @@ class Git(FetchMethod):
|
|||||||
|
|
||||||
ud.nobranch = ud.parm.get("nobranch","0") == "1"
|
ud.nobranch = ud.parm.get("nobranch","0") == "1"
|
||||||
|
|
||||||
# usehead implies nobranch
|
|
||||||
ud.usehead = ud.parm.get("usehead","0") == "1"
|
|
||||||
if ud.usehead:
|
|
||||||
if ud.proto != "file":
|
|
||||||
raise bb.fetch2.ParameterError("The usehead option is only for use with local ('protocol=file') git repositories", ud.url)
|
|
||||||
ud.nobranch = 1
|
|
||||||
|
|
||||||
# bareclone implies nocheckout
|
# bareclone implies nocheckout
|
||||||
ud.bareclone = ud.parm.get("bareclone","0") == "1"
|
ud.bareclone = ud.parm.get("bareclone","0") == "1"
|
||||||
if ud.bareclone:
|
if ud.bareclone:
|
||||||
@@ -179,9 +125,6 @@ class Git(FetchMethod):
|
|||||||
ud.branches[name] = branch
|
ud.branches[name] = branch
|
||||||
ud.unresolvedrev[name] = branch
|
ud.unresolvedrev[name] = branch
|
||||||
|
|
||||||
if ud.usehead:
|
|
||||||
ud.unresolvedrev['default'] = 'HEAD'
|
|
||||||
|
|
||||||
ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git -c core.fsyncobjectfiles=0"
|
ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git -c core.fsyncobjectfiles=0"
|
||||||
|
|
||||||
ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0") or ud.rebaseable
|
ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0") or ud.rebaseable
|
||||||
@@ -219,8 +162,9 @@ class Git(FetchMethod):
|
|||||||
def need_update(self, ud, d):
|
def need_update(self, ud, d):
|
||||||
if not os.path.exists(ud.clonedir):
|
if not os.path.exists(ud.clonedir):
|
||||||
return True
|
return True
|
||||||
|
os.chdir(ud.clonedir)
|
||||||
for name in ud.names:
|
for name in ud.names:
|
||||||
if not self._contains_ref(ud, d, name, ud.clonedir):
|
if not self._contains_ref(ud, d, name):
|
||||||
return True
|
return True
|
||||||
if ud.write_tarballs and not os.path.exists(ud.fullmirror):
|
if ud.write_tarballs and not os.path.exists(ud.fullmirror):
|
||||||
return True
|
return True
|
||||||
@@ -241,7 +185,8 @@ class Git(FetchMethod):
|
|||||||
# If the checkout doesn't exist and the mirror tarball does, extract it
|
# If the checkout doesn't exist and the mirror tarball does, extract it
|
||||||
if not os.path.exists(ud.clonedir) and os.path.exists(ud.fullmirror):
|
if not os.path.exists(ud.clonedir) and os.path.exists(ud.fullmirror):
|
||||||
bb.utils.mkdirhier(ud.clonedir)
|
bb.utils.mkdirhier(ud.clonedir)
|
||||||
runfetchcmd("tar -xzf %s" % (ud.fullmirror), d, workdir=ud.clonedir)
|
os.chdir(ud.clonedir)
|
||||||
|
runfetchcmd("tar -xzf %s" % (ud.fullmirror), d)
|
||||||
|
|
||||||
repourl = self._get_repo_url(ud)
|
repourl = self._get_repo_url(ud)
|
||||||
|
|
||||||
@@ -250,38 +195,38 @@ class Git(FetchMethod):
|
|||||||
# We do this since git will use a "-l" option automatically for local urls where possible
|
# We do this since git will use a "-l" option automatically for local urls where possible
|
||||||
if repourl.startswith("file://"):
|
if repourl.startswith("file://"):
|
||||||
repourl = repourl[7:]
|
repourl = repourl[7:]
|
||||||
clone_cmd = "LANG=C %s clone --bare --mirror %s %s --progress" % (ud.basecmd, repourl, ud.clonedir)
|
clone_cmd = "%s clone --bare --mirror %s %s" % (ud.basecmd, repourl, ud.clonedir)
|
||||||
if ud.proto.lower() != 'file':
|
if ud.proto.lower() != 'file':
|
||||||
bb.fetch2.check_network_access(d, clone_cmd)
|
bb.fetch2.check_network_access(d, clone_cmd)
|
||||||
progresshandler = GitProgressHandler(d)
|
runfetchcmd(clone_cmd, d)
|
||||||
runfetchcmd(clone_cmd, d, log=progresshandler)
|
|
||||||
|
|
||||||
|
os.chdir(ud.clonedir)
|
||||||
# Update the checkout if needed
|
# Update the checkout if needed
|
||||||
needupdate = False
|
needupdate = False
|
||||||
for name in ud.names:
|
for name in ud.names:
|
||||||
if not self._contains_ref(ud, d, name, ud.clonedir):
|
if not self._contains_ref(ud, d, name):
|
||||||
needupdate = True
|
needupdate = True
|
||||||
if needupdate:
|
if needupdate:
|
||||||
try:
|
try:
|
||||||
runfetchcmd("%s remote rm origin" % ud.basecmd, d, workdir=ud.clonedir)
|
runfetchcmd("%s remote rm origin" % ud.basecmd, d)
|
||||||
except bb.fetch2.FetchError:
|
except bb.fetch2.FetchError:
|
||||||
logger.debug(1, "No Origin")
|
logger.debug(1, "No Origin")
|
||||||
|
|
||||||
runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d, workdir=ud.clonedir)
|
runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d)
|
||||||
fetch_cmd = "LANG=C %s fetch -f --prune --progress %s refs/*:refs/*" % (ud.basecmd, repourl)
|
fetch_cmd = "%s fetch -f --prune %s refs/*:refs/*" % (ud.basecmd, repourl)
|
||||||
if ud.proto.lower() != 'file':
|
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)
|
||||||
progresshandler = GitProgressHandler(d)
|
runfetchcmd(fetch_cmd, d)
|
||||||
runfetchcmd(fetch_cmd, d, log=progresshandler, workdir=ud.clonedir)
|
runfetchcmd("%s prune-packed" % ud.basecmd, d)
|
||||||
runfetchcmd("%s prune-packed" % ud.basecmd, d, workdir=ud.clonedir)
|
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
|
||||||
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d, workdir=ud.clonedir)
|
|
||||||
try:
|
try:
|
||||||
os.unlink(ud.fullmirror)
|
os.unlink(ud.fullmirror)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
if exc.errno != errno.ENOENT:
|
if exc.errno != errno.ENOENT:
|
||||||
raise
|
raise
|
||||||
|
os.chdir(ud.clonedir)
|
||||||
for name in ud.names:
|
for name in ud.names:
|
||||||
if not self._contains_ref(ud, d, name, ud.clonedir):
|
if not self._contains_ref(ud, d, name):
|
||||||
raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
|
raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
|
||||||
|
|
||||||
def build_mirror_data(self, ud, d):
|
def build_mirror_data(self, ud, d):
|
||||||
@@ -291,9 +236,10 @@ class Git(FetchMethod):
|
|||||||
if os.path.islink(ud.fullmirror):
|
if os.path.islink(ud.fullmirror):
|
||||||
os.unlink(ud.fullmirror)
|
os.unlink(ud.fullmirror)
|
||||||
|
|
||||||
|
os.chdir(ud.clonedir)
|
||||||
logger.info("Creating tarball of git repository")
|
logger.info("Creating tarball of git repository")
|
||||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, os.path.join(".") ), d, workdir=ud.clonedir)
|
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, os.path.join(".") ), d)
|
||||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.clonedir)
|
runfetchcmd("touch %s.done" % (ud.fullmirror), d)
|
||||||
|
|
||||||
def unpack(self, ud, destdir, d):
|
def unpack(self, ud, destdir, d):
|
||||||
""" unpack the downloaded src to destdir"""
|
""" unpack the downloaded src to destdir"""
|
||||||
@@ -315,22 +261,38 @@ class Git(FetchMethod):
|
|||||||
if ud.bareclone:
|
if ud.bareclone:
|
||||||
cloneflags += " --mirror"
|
cloneflags += " --mirror"
|
||||||
|
|
||||||
runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, cloneflags, ud.clonedir, destdir), d)
|
# 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("%s clone %s %s/ %s" % (ud.basecmd, cloneflags, clonedir, destdir), d)
|
||||||
|
os.chdir(destdir)
|
||||||
repourl = self._get_repo_url(ud)
|
repourl = self._get_repo_url(ud)
|
||||||
runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, repourl), d, workdir=destdir)
|
runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, repourl), d)
|
||||||
if not ud.nocheckout:
|
if not ud.nocheckout:
|
||||||
if subdir != "":
|
if subdir != "":
|
||||||
runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d,
|
runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d)
|
||||||
workdir=destdir)
|
runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d)
|
||||||
runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d, workdir=destdir)
|
|
||||||
elif not ud.nobranch:
|
elif not ud.nobranch:
|
||||||
branchname = ud.branches[ud.names[0]]
|
branchname = ud.branches[ud.names[0]]
|
||||||
runfetchcmd("%s checkout -B %s %s" % (ud.basecmd, branchname, \
|
runfetchcmd("%s checkout -B %s %s" % (ud.basecmd, branchname, \
|
||||||
ud.revisions[ud.names[0]]), d, workdir=destdir)
|
ud.revisions[ud.names[0]]), d)
|
||||||
runfetchcmd("%s branch %s --set-upstream-to origin/%s" % (ud.basecmd, branchname, \
|
runfetchcmd("%s branch --set-upstream %s origin/%s" % (ud.basecmd, branchname, \
|
||||||
branchname), d, workdir=destdir)
|
branchname), d)
|
||||||
else:
|
else:
|
||||||
runfetchcmd("%s checkout %s" % (ud.basecmd, ud.revisions[ud.names[0]]), d, workdir=destdir)
|
runfetchcmd("%s checkout %s" % (ud.basecmd, ud.revisions[ud.names[0]]), d)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -344,7 +306,7 @@ class Git(FetchMethod):
|
|||||||
def supports_srcrev(self):
|
def supports_srcrev(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _contains_ref(self, ud, d, name, wd):
|
def _contains_ref(self, ud, d, name):
|
||||||
cmd = ""
|
cmd = ""
|
||||||
if ud.nobranch:
|
if ud.nobranch:
|
||||||
cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
|
cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
|
||||||
@@ -353,7 +315,7 @@ class Git(FetchMethod):
|
|||||||
cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
|
cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
|
||||||
ud.basecmd, ud.revisions[name], ud.branches[name])
|
ud.basecmd, ud.revisions[name], ud.branches[name])
|
||||||
try:
|
try:
|
||||||
output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
|
output = runfetchcmd(cmd, d, quiet=True)
|
||||||
except bb.fetch2.FetchError:
|
except bb.fetch2.FetchError:
|
||||||
return False
|
return False
|
||||||
if len(output.split()) > 1:
|
if len(output.split()) > 1:
|
||||||
@@ -396,17 +358,16 @@ class Git(FetchMethod):
|
|||||||
"""
|
"""
|
||||||
output = self._lsremote(ud, d, "")
|
output = self._lsremote(ud, d, "")
|
||||||
# Tags of the form ^{} may not work, need to fallback to other form
|
# Tags of the form ^{} may not work, need to fallback to other form
|
||||||
if ud.unresolvedrev[name][:5] == "refs/" or ud.usehead:
|
if ud.unresolvedrev[name][:5] == "refs/":
|
||||||
head = ud.unresolvedrev[name]
|
head = ud.unresolvedrev[name]
|
||||||
tag = ud.unresolvedrev[name]
|
tag = ud.unresolvedrev[name]
|
||||||
else:
|
else:
|
||||||
head = "refs/heads/%s" % ud.unresolvedrev[name]
|
head = "refs/heads/%s" % ud.unresolvedrev[name]
|
||||||
tag = "refs/tags/%s" % ud.unresolvedrev[name]
|
tag = "refs/tags/%s" % ud.unresolvedrev[name]
|
||||||
for s in [head, tag + "^{}", tag]:
|
for s in [head, tag + "^{}", tag]:
|
||||||
for l in output.strip().split('\n'):
|
for l in output.split('\n'):
|
||||||
sha1, ref = l.split()
|
if s in l:
|
||||||
if s == ref:
|
return l.split()[0]
|
||||||
return sha1
|
|
||||||
raise bb.fetch2.FetchError("Unable to resolve '%s' in upstream git repository in git ls-remote output for %s" % \
|
raise bb.fetch2.FetchError("Unable to resolve '%s' in upstream git repository in git ls-remote output for %s" % \
|
||||||
(ud.unresolvedrev[name], ud.host+ud.path))
|
(ud.unresolvedrev[name], ud.host+ud.path))
|
||||||
|
|
||||||
@@ -418,7 +379,7 @@ class Git(FetchMethod):
|
|||||||
"""
|
"""
|
||||||
pupver = ('', '')
|
pupver = ('', '')
|
||||||
|
|
||||||
tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX', True) or "(?P<pver>([0-9][\.|_]?)+)")
|
tagregex = re.compile(d.getVar('GITTAGREGEX', True) or "(?P<pver>([0-9][\.|_]?)+)")
|
||||||
try:
|
try:
|
||||||
output = self._lsremote(ud, d, "refs/tags/*")
|
output = self._lsremote(ud, d, "refs/tags/*")
|
||||||
except bb.fetch2.FetchError or bb.fetch2.NetworkAccess:
|
except bb.fetch2.FetchError or bb.fetch2.NetworkAccess:
|
||||||
|
|||||||
@@ -34,42 +34,43 @@ class GitANNEX(Git):
|
|||||||
"""
|
"""
|
||||||
return ud.type in ['gitannex']
|
return ud.type in ['gitannex']
|
||||||
|
|
||||||
def uses_annex(self, ud, d, wd):
|
def uses_annex(self, ud, d):
|
||||||
for name in ud.names:
|
for name in ud.names:
|
||||||
try:
|
try:
|
||||||
runfetchcmd("%s rev-list git-annex" % (ud.basecmd), d, quiet=True, workdir=wd)
|
runfetchcmd("%s rev-list git-annex" % (ud.basecmd), d, quiet=True)
|
||||||
return True
|
return True
|
||||||
except bb.fetch.FetchError:
|
except bb.fetch.FetchError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_annex(self, ud, d, wd):
|
def update_annex(self, ud, d):
|
||||||
try:
|
try:
|
||||||
runfetchcmd("%s annex get --all" % (ud.basecmd), d, quiet=True, workdir=wd)
|
runfetchcmd("%s annex get --all" % (ud.basecmd), d, quiet=True)
|
||||||
except bb.fetch.FetchError:
|
except bb.fetch.FetchError:
|
||||||
return False
|
return False
|
||||||
runfetchcmd("chmod u+w -R %s/annex" % (ud.clonedir), d, quiet=True, workdir=wd)
|
runfetchcmd("chmod u+w -R %s/annex" % (ud.clonedir), d, quiet=True)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def download(self, ud, d):
|
def download(self, ud, d):
|
||||||
Git.download(self, ud, d)
|
Git.download(self, ud, d)
|
||||||
|
|
||||||
annex = self.uses_annex(ud, d, ud.clonedir)
|
os.chdir(ud.clonedir)
|
||||||
|
annex = self.uses_annex(ud, d)
|
||||||
if annex:
|
if annex:
|
||||||
self.update_annex(ud, d, ud.clonedir)
|
self.update_annex(ud, d)
|
||||||
|
|
||||||
def unpack(self, ud, destdir, d):
|
def unpack(self, ud, destdir, d):
|
||||||
Git.unpack(self, ud, destdir, d)
|
Git.unpack(self, ud, destdir, d)
|
||||||
|
|
||||||
|
os.chdir(ud.destdir)
|
||||||
try:
|
try:
|
||||||
runfetchcmd("%s annex init" % (ud.basecmd), d, workdir=ud.destdir)
|
runfetchcmd("%s annex sync" % (ud.basecmd), d)
|
||||||
except bb.fetch.FetchError:
|
except bb.fetch.FetchError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
annex = self.uses_annex(ud, d, ud.destdir)
|
annex = self.uses_annex(ud, d)
|
||||||
if annex:
|
if annex:
|
||||||
runfetchcmd("%s annex get" % (ud.basecmd), d, workdir=ud.destdir)
|
runfetchcmd("%s annex get" % (ud.basecmd), d)
|
||||||
runfetchcmd("chmod u+w -R %s/.git/annex" % (ud.destdir), d, quiet=True, workdir=ud.destdir)
|
runfetchcmd("chmod u+w -R %s/.git/annex" % (ud.destdir), d, quiet=True)
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ class GitSM(Git):
|
|||||||
"""
|
"""
|
||||||
return ud.type in ['gitsm']
|
return ud.type in ['gitsm']
|
||||||
|
|
||||||
def uses_submodules(self, ud, d, wd):
|
def uses_submodules(self, ud, d):
|
||||||
for name in ud.names:
|
for name in ud.names:
|
||||||
try:
|
try:
|
||||||
runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=wd)
|
runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True)
|
||||||
return True
|
return True
|
||||||
except bb.fetch.FetchError:
|
except bb.fetch.FetchError:
|
||||||
pass
|
pass
|
||||||
@@ -107,25 +107,31 @@ class GitSM(Git):
|
|||||||
os.mkdir(tmpclonedir)
|
os.mkdir(tmpclonedir)
|
||||||
os.rename(ud.clonedir, gitdir)
|
os.rename(ud.clonedir, gitdir)
|
||||||
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
|
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
|
||||||
runfetchcmd(ud.basecmd + " reset --hard", d, workdir=tmpclonedir)
|
os.chdir(tmpclonedir)
|
||||||
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d, workdir=tmpclonedir)
|
runfetchcmd(ud.basecmd + " reset --hard", d)
|
||||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d, workdir=tmpclonedir)
|
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d)
|
||||||
|
runfetchcmd(ud.basecmd + " submodule init", d)
|
||||||
|
runfetchcmd(ud.basecmd + " submodule update", d)
|
||||||
self._set_relative_paths(tmpclonedir)
|
self._set_relative_paths(tmpclonedir)
|
||||||
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*false/bare = true/'", d, workdir=tmpclonedir)
|
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*false/bare = true/'", d)
|
||||||
os.rename(gitdir, ud.clonedir,)
|
os.rename(gitdir, ud.clonedir,)
|
||||||
bb.utils.remove(tmpclonedir, True)
|
bb.utils.remove(tmpclonedir, True)
|
||||||
|
|
||||||
def download(self, ud, d):
|
def download(self, ud, d):
|
||||||
Git.download(self, ud, d)
|
Git.download(self, ud, d)
|
||||||
|
|
||||||
submodules = self.uses_submodules(ud, d, ud.clonedir)
|
os.chdir(ud.clonedir)
|
||||||
|
submodules = self.uses_submodules(ud, d)
|
||||||
if submodules:
|
if submodules:
|
||||||
self.update_submodules(ud, d)
|
self.update_submodules(ud, d)
|
||||||
|
|
||||||
def unpack(self, ud, destdir, d):
|
def unpack(self, ud, destdir, d):
|
||||||
Git.unpack(self, ud, destdir, d)
|
Git.unpack(self, ud, destdir, d)
|
||||||
|
|
||||||
submodules = self.uses_submodules(ud, d, ud.destdir)
|
os.chdir(ud.destdir)
|
||||||
|
submodules = self.uses_submodules(ud, d)
|
||||||
if submodules:
|
if submodules:
|
||||||
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d, workdir=ud.destdir)
|
runfetchcmd("cp -r " + ud.clonedir + "/modules " + ud.destdir + "/.git/", d)
|
||||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d, workdir=ud.destdir)
|
runfetchcmd(ud.basecmd + " submodule init", d)
|
||||||
|
runfetchcmd(ud.basecmd + " submodule update", d)
|
||||||
|
|
||||||
|
|||||||
@@ -169,22 +169,25 @@ class Hg(FetchMethod):
|
|||||||
# If the checkout doesn't exist and the mirror tarball does, extract it
|
# If the checkout doesn't exist and the mirror tarball does, extract it
|
||||||
if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
|
if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
bb.utils.mkdirhier(ud.pkgdir)
|
||||||
runfetchcmd("tar -xzf %s" % (ud.fullmirror), d, workdir=ud.pkgdir)
|
os.chdir(ud.pkgdir)
|
||||||
|
runfetchcmd("tar -xzf %s" % (ud.fullmirror), d)
|
||||||
|
|
||||||
if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK):
|
if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK):
|
||||||
# Found the source, check whether need pull
|
# Found the source, check whether need pull
|
||||||
updatecmd = self._buildhgcommand(ud, d, "update")
|
updatecmd = self._buildhgcommand(ud, d, "update")
|
||||||
|
os.chdir(ud.moddir)
|
||||||
logger.debug(1, "Running %s", updatecmd)
|
logger.debug(1, "Running %s", updatecmd)
|
||||||
try:
|
try:
|
||||||
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
runfetchcmd(updatecmd, d)
|
||||||
except bb.fetch2.FetchError:
|
except bb.fetch2.FetchError:
|
||||||
# Runnning pull in the repo
|
# Runnning pull in the repo
|
||||||
pullcmd = self._buildhgcommand(ud, d, "pull")
|
pullcmd = self._buildhgcommand(ud, d, "pull")
|
||||||
logger.info("Pulling " + ud.url)
|
logger.info("Pulling " + ud.url)
|
||||||
# update sources there
|
# update sources there
|
||||||
|
os.chdir(ud.moddir)
|
||||||
logger.debug(1, "Running %s", pullcmd)
|
logger.debug(1, "Running %s", pullcmd)
|
||||||
bb.fetch2.check_network_access(d, pullcmd, ud.url)
|
bb.fetch2.check_network_access(d, pullcmd, ud.url)
|
||||||
runfetchcmd(pullcmd, d, workdir=ud.moddir)
|
runfetchcmd(pullcmd, d)
|
||||||
try:
|
try:
|
||||||
os.unlink(ud.fullmirror)
|
os.unlink(ud.fullmirror)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
@@ -197,15 +200,17 @@ class Hg(FetchMethod):
|
|||||||
logger.info("Fetch " + ud.url)
|
logger.info("Fetch " + ud.url)
|
||||||
# check out sources there
|
# check out sources there
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
bb.utils.mkdirhier(ud.pkgdir)
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
logger.debug(1, "Running %s", fetchcmd)
|
logger.debug(1, "Running %s", fetchcmd)
|
||||||
bb.fetch2.check_network_access(d, fetchcmd, ud.url)
|
bb.fetch2.check_network_access(d, fetchcmd, ud.url)
|
||||||
runfetchcmd(fetchcmd, d, workdir=ud.pkgdir)
|
runfetchcmd(fetchcmd, d)
|
||||||
|
|
||||||
# Even when we clone (fetch), we still need to update as hg's clone
|
# Even when we clone (fetch), we still need to update as hg's clone
|
||||||
# won't checkout the specified revision if its on a branch
|
# won't checkout the specified revision if its on a branch
|
||||||
updatecmd = self._buildhgcommand(ud, d, "update")
|
updatecmd = self._buildhgcommand(ud, d, "update")
|
||||||
|
os.chdir(ud.moddir)
|
||||||
logger.debug(1, "Running %s", updatecmd)
|
logger.debug(1, "Running %s", updatecmd)
|
||||||
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
runfetchcmd(updatecmd, d)
|
||||||
|
|
||||||
def clean(self, ud, d):
|
def clean(self, ud, d):
|
||||||
""" Clean the hg dir """
|
""" Clean the hg dir """
|
||||||
@@ -241,9 +246,10 @@ class Hg(FetchMethod):
|
|||||||
if os.path.islink(ud.fullmirror):
|
if os.path.islink(ud.fullmirror):
|
||||||
os.unlink(ud.fullmirror)
|
os.unlink(ud.fullmirror)
|
||||||
|
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
logger.info("Creating tarball of hg repository")
|
logger.info("Creating tarball of hg repository")
|
||||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d, workdir=ud.pkgdir)
|
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d)
|
||||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.pkgdir)
|
runfetchcmd("touch %s.done" % (ud.fullmirror), d)
|
||||||
|
|
||||||
def localpath(self, ud, d):
|
def localpath(self, ud, d):
|
||||||
return ud.pkgdir
|
return ud.pkgdir
|
||||||
@@ -263,8 +269,10 @@ class Hg(FetchMethod):
|
|||||||
logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
|
logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
|
||||||
runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
|
runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
|
||||||
logger.debug(2, "Unpack: updating source in '" + codir + "'")
|
logger.debug(2, "Unpack: updating source in '" + codir + "'")
|
||||||
runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d, workdir=codir)
|
os.chdir(codir)
|
||||||
runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d, workdir=codir)
|
runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d)
|
||||||
|
runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d)
|
||||||
else:
|
else:
|
||||||
logger.debug(2, "Unpack: extracting source to '" + codir + "'")
|
logger.debug(2, "Unpack: extracting source to '" + codir + "'")
|
||||||
runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d, workdir=ud.moddir)
|
os.chdir(ud.moddir)
|
||||||
|
runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ BitBake build tools.
|
|||||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import urllib.request, urllib.parse, urllib.error
|
import urllib
|
||||||
import bb
|
import bb
|
||||||
import bb.utils
|
import bb.utils
|
||||||
from bb import data
|
from bb import data
|
||||||
@@ -42,10 +42,9 @@ class Local(FetchMethod):
|
|||||||
|
|
||||||
def urldata_init(self, ud, d):
|
def urldata_init(self, ud, d):
|
||||||
# We don't set localfile as for this fetcher the file is already local!
|
# We don't set localfile as for this fetcher the file is already local!
|
||||||
ud.decodedurl = urllib.parse.unquote(ud.url.split("://")[1].split(";")[0])
|
ud.decodedurl = urllib.unquote(ud.url.split("://")[1].split(";")[0])
|
||||||
ud.basename = os.path.basename(ud.decodedurl)
|
ud.basename = os.path.basename(ud.decodedurl)
|
||||||
ud.basepath = ud.decodedurl
|
ud.basepath = ud.decodedurl
|
||||||
ud.needdonestamp = False
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def localpath(self, urldata, d):
|
def localpath(self, urldata, d):
|
||||||
|
|||||||
@@ -1,293 +0,0 @@
|
|||||||
# ex:ts=4:sw=4:sts=4:et
|
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
||||||
"""
|
|
||||||
BitBake 'Fetch' NPM implementation
|
|
||||||
|
|
||||||
The NPM fetcher is used to retrieve files from the npmjs repository
|
|
||||||
|
|
||||||
Usage in the recipe:
|
|
||||||
|
|
||||||
SRC_URI = "npm://registry.npmjs.org/;name=${PN};version=${PV}"
|
|
||||||
Suported SRC_URI options are:
|
|
||||||
|
|
||||||
- name
|
|
||||||
- version
|
|
||||||
|
|
||||||
npm://registry.npmjs.org/${PN}/-/${PN}-${PV}.tgz would become npm://registry.npmjs.org;name=${PN};version=${PV}
|
|
||||||
The fetcher all triggers off the existence of ud.localpath. If that exists and has the ".done" stamp, its assumed the fetch is good/done
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import signal
|
|
||||||
import bb
|
|
||||||
from bb import data
|
|
||||||
from bb.fetch2 import FetchMethod
|
|
||||||
from bb.fetch2 import FetchError
|
|
||||||
from bb.fetch2 import ChecksumError
|
|
||||||
from bb.fetch2 import runfetchcmd
|
|
||||||
from bb.fetch2 import logger
|
|
||||||
from bb.fetch2 import UnpackError
|
|
||||||
from bb.fetch2 import ParameterError
|
|
||||||
from distutils import spawn
|
|
||||||
|
|
||||||
def subprocess_setup():
|
|
||||||
# Python installs a SIGPIPE handler by default. This is usually not what
|
|
||||||
# non-Python subprocesses expect.
|
|
||||||
# SIGPIPE errors are known issues with gzip/bash
|
|
||||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
||||||
|
|
||||||
class Npm(FetchMethod):
|
|
||||||
|
|
||||||
"""Class to fetch urls via 'npm'"""
|
|
||||||
def init(self, d):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def supports(self, ud, d):
|
|
||||||
"""
|
|
||||||
Check to see if a given url can be fetched with npm
|
|
||||||
"""
|
|
||||||
return ud.type in ['npm']
|
|
||||||
|
|
||||||
def debug(self, msg):
|
|
||||||
logger.debug(1, "NpmFetch: %s", msg)
|
|
||||||
|
|
||||||
def clean(self, ud, d):
|
|
||||||
logger.debug(2, "Calling cleanup %s" % ud.pkgname)
|
|
||||||
bb.utils.remove(ud.localpath, False)
|
|
||||||
bb.utils.remove(ud.pkgdatadir, True)
|
|
||||||
bb.utils.remove(ud.fullmirror, False)
|
|
||||||
|
|
||||||
def urldata_init(self, ud, d):
|
|
||||||
"""
|
|
||||||
init NPM specific variable within url data
|
|
||||||
"""
|
|
||||||
if 'downloadfilename' in ud.parm:
|
|
||||||
ud.basename = ud.parm['downloadfilename']
|
|
||||||
else:
|
|
||||||
ud.basename = os.path.basename(ud.path)
|
|
||||||
|
|
||||||
# can't call it ud.name otherwise fetcher base class will start doing sha1stuff
|
|
||||||
# TODO: find a way to get an sha1/sha256 manifest of pkg & all deps
|
|
||||||
ud.pkgname = ud.parm.get("name", None)
|
|
||||||
if not ud.pkgname:
|
|
||||||
raise ParameterError("NPM fetcher requires a name parameter", ud.url)
|
|
||||||
ud.version = ud.parm.get("version", None)
|
|
||||||
if not ud.version:
|
|
||||||
raise ParameterError("NPM fetcher requires a version parameter", ud.url)
|
|
||||||
ud.bbnpmmanifest = "%s-%s.deps.json" % (ud.pkgname, ud.version)
|
|
||||||
ud.registry = "http://%s" % (ud.url.replace('npm://', '', 1).split(';'))[0]
|
|
||||||
prefixdir = "npm/%s" % ud.pkgname
|
|
||||||
ud.pkgdatadir = d.expand("${DL_DIR}/%s" % prefixdir)
|
|
||||||
if not os.path.exists(ud.pkgdatadir):
|
|
||||||
bb.utils.mkdirhier(ud.pkgdatadir)
|
|
||||||
ud.localpath = d.expand("${DL_DIR}/npm/%s" % ud.bbnpmmanifest)
|
|
||||||
|
|
||||||
self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -O -t 2 -T 30 -nv --passive-ftp --no-check-certificate "
|
|
||||||
ud.prefixdir = prefixdir
|
|
||||||
|
|
||||||
ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0")
|
|
||||||
ud.mirrortarball = 'npm_%s-%s.tar.xz' % (ud.pkgname, ud.version)
|
|
||||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
|
|
||||||
|
|
||||||
def need_update(self, ud, d):
|
|
||||||
if os.path.exists(ud.localpath):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _runwget(self, ud, d, command, quiet):
|
|
||||||
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
|
|
||||||
bb.fetch2.check_network_access(d, command)
|
|
||||||
dldir = d.getVar("DL_DIR", True)
|
|
||||||
runfetchcmd(command, d, quiet, workdir=dldir)
|
|
||||||
|
|
||||||
def _unpackdep(self, ud, pkg, data, destdir, dldir, d):
|
|
||||||
file = data[pkg]['tgz']
|
|
||||||
logger.debug(2, "file to extract is %s" % file)
|
|
||||||
if file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
|
|
||||||
cmd = 'tar xz --strip 1 --no-same-owner --warning=no-unknown-keyword -f %s/%s' % (dldir, file)
|
|
||||||
else:
|
|
||||||
bb.fatal("NPM package %s downloaded not a tarball!" % file)
|
|
||||||
|
|
||||||
# Change to subdir before executing command
|
|
||||||
if not os.path.exists(destdir):
|
|
||||||
os.makedirs(destdir)
|
|
||||||
path = d.getVar('PATH', True)
|
|
||||||
if path:
|
|
||||||
cmd = "PATH=\"%s\" %s" % (path, cmd)
|
|
||||||
bb.note("Unpacking %s to %s/" % (file, destdir))
|
|
||||||
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=destdir)
|
|
||||||
|
|
||||||
if ret != 0:
|
|
||||||
raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), ud.url)
|
|
||||||
|
|
||||||
if 'deps' not in data[pkg]:
|
|
||||||
return
|
|
||||||
for dep in data[pkg]['deps']:
|
|
||||||
self._unpackdep(ud, dep, data[pkg]['deps'], "%s/node_modules/%s" % (destdir, dep), dldir, d)
|
|
||||||
|
|
||||||
|
|
||||||
def unpack(self, ud, destdir, d):
|
|
||||||
dldir = d.getVar("DL_DIR", True)
|
|
||||||
depdumpfile = "%s-%s.deps.json" % (ud.pkgname, ud.version)
|
|
||||||
with open("%s/npm/%s" % (dldir, depdumpfile)) as datafile:
|
|
||||||
workobj = json.load(datafile)
|
|
||||||
dldir = "%s/%s" % (os.path.dirname(ud.localpath), ud.pkgname)
|
|
||||||
|
|
||||||
if 'subdir' in ud.parm:
|
|
||||||
unpackdir = '%s/%s' % (destdir, ud.parm.get('subdir'))
|
|
||||||
else:
|
|
||||||
unpackdir = '%s/npmpkg' % destdir
|
|
||||||
|
|
||||||
self._unpackdep(ud, ud.pkgname, workobj, unpackdir, dldir, d)
|
|
||||||
|
|
||||||
def _parse_view(self, output):
|
|
||||||
'''
|
|
||||||
Parse the output of npm view --json; the last JSON result
|
|
||||||
is assumed to be the one that we're interested in.
|
|
||||||
'''
|
|
||||||
pdata = None
|
|
||||||
outdeps = {}
|
|
||||||
datalines = []
|
|
||||||
bracelevel = 0
|
|
||||||
for line in output.splitlines():
|
|
||||||
if bracelevel:
|
|
||||||
datalines.append(line)
|
|
||||||
elif '{' in line:
|
|
||||||
datalines = []
|
|
||||||
datalines.append(line)
|
|
||||||
bracelevel = bracelevel + line.count('{') - line.count('}')
|
|
||||||
if datalines:
|
|
||||||
pdata = json.loads('\n'.join(datalines))
|
|
||||||
return pdata
|
|
||||||
|
|
||||||
def _getdependencies(self, pkg, data, version, d, ud, optional=False, fetchedlist=None):
|
|
||||||
if fetchedlist is None:
|
|
||||||
fetchedlist = []
|
|
||||||
pkgfullname = pkg
|
|
||||||
if version != '*' and not '/' in version:
|
|
||||||
pkgfullname += "@'%s'" % version
|
|
||||||
logger.debug(2, "Calling getdeps on %s" % pkg)
|
|
||||||
fetchcmd = "npm view %s --json --registry %s" % (pkgfullname, ud.registry)
|
|
||||||
output = runfetchcmd(fetchcmd, d, True)
|
|
||||||
pdata = self._parse_view(output)
|
|
||||||
if not pdata:
|
|
||||||
raise FetchError("The command '%s' returned no output" % fetchcmd)
|
|
||||||
if optional:
|
|
||||||
pkg_os = pdata.get('os', None)
|
|
||||||
if pkg_os:
|
|
||||||
if not isinstance(pkg_os, list):
|
|
||||||
pkg_os = [pkg_os]
|
|
||||||
if 'linux' not in pkg_os or '!linux' in pkg_os:
|
|
||||||
logger.debug(2, "Skipping %s since it's incompatible with Linux" % pkg)
|
|
||||||
return
|
|
||||||
#logger.debug(2, "Output URL is %s - %s - %s" % (ud.basepath, ud.basename, ud.localfile))
|
|
||||||
outputurl = pdata['dist']['tarball']
|
|
||||||
data[pkg] = {}
|
|
||||||
data[pkg]['tgz'] = os.path.basename(outputurl)
|
|
||||||
if not outputurl in fetchedlist:
|
|
||||||
self._runwget(ud, d, "%s --directory-prefix=%s %s" % (self.basecmd, ud.prefixdir, outputurl), False)
|
|
||||||
fetchedlist.append(outputurl)
|
|
||||||
|
|
||||||
dependencies = pdata.get('dependencies', {})
|
|
||||||
optionalDependencies = pdata.get('optionalDependencies', {})
|
|
||||||
depsfound = {}
|
|
||||||
optdepsfound = {}
|
|
||||||
data[pkg]['deps'] = {}
|
|
||||||
for dep in dependencies:
|
|
||||||
if dep in optionalDependencies:
|
|
||||||
optdepsfound[dep] = dependencies[dep]
|
|
||||||
else:
|
|
||||||
depsfound[dep] = dependencies[dep]
|
|
||||||
for dep, version in optdepsfound.items():
|
|
||||||
self._getdependencies(dep, data[pkg]['deps'], version, d, ud, optional=True, fetchedlist=fetchedlist)
|
|
||||||
for dep, version in depsfound.items():
|
|
||||||
self._getdependencies(dep, data[pkg]['deps'], version, d, ud, fetchedlist=fetchedlist)
|
|
||||||
|
|
||||||
def _getshrinkeddependencies(self, pkg, data, version, d, ud, lockdown, manifest, toplevel=True):
|
|
||||||
logger.debug(2, "NPM shrinkwrap file is %s" % data)
|
|
||||||
if toplevel:
|
|
||||||
name = data.get('name', None)
|
|
||||||
if name and name != pkg:
|
|
||||||
for obj in data.get('dependencies', []):
|
|
||||||
if obj == pkg:
|
|
||||||
self._getshrinkeddependencies(obj, data['dependencies'][obj], data['dependencies'][obj]['version'], d, ud, lockdown, manifest, False)
|
|
||||||
return
|
|
||||||
outputurl = "invalid"
|
|
||||||
if ('resolved' not in data) or (not data['resolved'].startswith('http')):
|
|
||||||
# will be the case for ${PN}
|
|
||||||
fetchcmd = "npm view %s@%s dist.tarball --registry %s" % (pkg, version, ud.registry)
|
|
||||||
logger.debug(2, "Found this matching URL: %s" % str(fetchcmd))
|
|
||||||
outputurl = runfetchcmd(fetchcmd, d, True)
|
|
||||||
else:
|
|
||||||
outputurl = data['resolved']
|
|
||||||
self._runwget(ud, d, "%s --directory-prefix=%s %s" % (self.basecmd, ud.prefixdir, outputurl), False)
|
|
||||||
manifest[pkg] = {}
|
|
||||||
manifest[pkg]['tgz'] = os.path.basename(outputurl).rstrip()
|
|
||||||
manifest[pkg]['deps'] = {}
|
|
||||||
|
|
||||||
if pkg in lockdown:
|
|
||||||
sha1_expected = lockdown[pkg][version]
|
|
||||||
sha1_data = bb.utils.sha1_file("npm/%s/%s" % (ud.pkgname, manifest[pkg]['tgz']))
|
|
||||||
if sha1_expected != sha1_data:
|
|
||||||
msg = "\nFile: '%s' has %s checksum %s when %s was expected" % (manifest[pkg]['tgz'], 'sha1', sha1_data, sha1_expected)
|
|
||||||
raise ChecksumError('Checksum mismatch!%s' % msg)
|
|
||||||
else:
|
|
||||||
logger.debug(2, "No lockdown data for %s@%s" % (pkg, version))
|
|
||||||
|
|
||||||
if 'dependencies' in data:
|
|
||||||
for obj in data['dependencies']:
|
|
||||||
logger.debug(2, "Found dep is %s" % str(obj))
|
|
||||||
self._getshrinkeddependencies(obj, data['dependencies'][obj], data['dependencies'][obj]['version'], d, ud, lockdown, manifest[pkg]['deps'], False)
|
|
||||||
|
|
||||||
def download(self, ud, d):
|
|
||||||
"""Fetch url"""
|
|
||||||
jsondepobj = {}
|
|
||||||
shrinkobj = {}
|
|
||||||
lockdown = {}
|
|
||||||
|
|
||||||
if not os.listdir(ud.pkgdatadir) and os.path.exists(ud.fullmirror):
|
|
||||||
dest = d.getVar("DL_DIR", True)
|
|
||||||
bb.utils.mkdirhier(dest)
|
|
||||||
runfetchcmd("tar -xJf %s" % (ud.fullmirror), d, workdir=dest)
|
|
||||||
return
|
|
||||||
|
|
||||||
shwrf = d.getVar('NPM_SHRINKWRAP', True)
|
|
||||||
logger.debug(2, "NPM shrinkwrap file is %s" % shwrf)
|
|
||||||
try:
|
|
||||||
with open(shwrf) as datafile:
|
|
||||||
shrinkobj = json.load(datafile)
|
|
||||||
except:
|
|
||||||
logger.warning('Missing shrinkwrap file in NPM_SHRINKWRAP for %s, this will lead to unreliable builds!' % ud.pkgname)
|
|
||||||
lckdf = d.getVar('NPM_LOCKDOWN', True)
|
|
||||||
logger.debug(2, "NPM lockdown file is %s" % lckdf)
|
|
||||||
try:
|
|
||||||
with open(lckdf) as datafile:
|
|
||||||
lockdown = json.load(datafile)
|
|
||||||
except:
|
|
||||||
logger.warning('Missing lockdown file in NPM_LOCKDOWN for %s, this will lead to unreproducible builds!' % ud.pkgname)
|
|
||||||
|
|
||||||
if ('name' not in shrinkobj):
|
|
||||||
self._getdependencies(ud.pkgname, jsondepobj, ud.version, d, ud)
|
|
||||||
else:
|
|
||||||
self._getshrinkeddependencies(ud.pkgname, shrinkobj, ud.version, d, ud, lockdown, jsondepobj)
|
|
||||||
|
|
||||||
with open(ud.localpath, 'w') as outfile:
|
|
||||||
json.dump(jsondepobj, outfile)
|
|
||||||
|
|
||||||
def build_mirror_data(self, ud, d):
|
|
||||||
# Generate a mirror tarball if needed
|
|
||||||
if ud.write_tarballs and not os.path.exists(ud.fullmirror):
|
|
||||||
# it's possible that this symlink points to read-only filesystem with PREMIRROR
|
|
||||||
if os.path.islink(ud.fullmirror):
|
|
||||||
os.unlink(ud.fullmirror)
|
|
||||||
|
|
||||||
dldir = d.getVar("DL_DIR", True)
|
|
||||||
logger.info("Creating tarball of npm data")
|
|
||||||
runfetchcmd("tar -cJf %s npm/%s npm/%s" % (ud.fullmirror, ud.bbnpmmanifest, ud.pkgname), d,
|
|
||||||
workdir=dldir)
|
|
||||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=dldir)
|
|
||||||
@@ -34,13 +34,13 @@ class Osc(FetchMethod):
|
|||||||
|
|
||||||
# Create paths to osc checkouts
|
# Create paths to osc checkouts
|
||||||
relpath = self._strip_leading_slashes(ud.path)
|
relpath = self._strip_leading_slashes(ud.path)
|
||||||
ud.pkgdir = os.path.join(d.getVar('OSCDIR', True), ud.host)
|
ud.pkgdir = os.path.join(data.expand('${OSCDIR}', d), ud.host)
|
||||||
ud.moddir = os.path.join(ud.pkgdir, relpath, ud.module)
|
ud.moddir = os.path.join(ud.pkgdir, relpath, ud.module)
|
||||||
|
|
||||||
if 'rev' in ud.parm:
|
if 'rev' in ud.parm:
|
||||||
ud.revision = ud.parm['rev']
|
ud.revision = ud.parm['rev']
|
||||||
else:
|
else:
|
||||||
pv = d.getVar("PV", False)
|
pv = data.getVar("PV", d, 0)
|
||||||
rev = bb.fetch2.srcrev_internal_helper(ud, d)
|
rev = bb.fetch2.srcrev_internal_helper(ud, d)
|
||||||
if rev and rev != True:
|
if rev and rev != True:
|
||||||
ud.revision = rev
|
ud.revision = rev
|
||||||
@@ -84,25 +84,27 @@ class Osc(FetchMethod):
|
|||||||
|
|
||||||
logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
|
logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
|
||||||
|
|
||||||
if os.access(os.path.join(d.getVar('OSCDIR', True), ud.path, ud.module), os.R_OK):
|
if os.access(os.path.join(data.expand('${OSCDIR}', d), ud.path, ud.module), os.R_OK):
|
||||||
oscupdatecmd = self._buildosccommand(ud, d, "update")
|
oscupdatecmd = self._buildosccommand(ud, d, "update")
|
||||||
logger.info("Update "+ ud.url)
|
logger.info("Update "+ ud.url)
|
||||||
# update sources there
|
# update sources there
|
||||||
|
os.chdir(ud.moddir)
|
||||||
logger.debug(1, "Running %s", oscupdatecmd)
|
logger.debug(1, "Running %s", oscupdatecmd)
|
||||||
bb.fetch2.check_network_access(d, oscupdatecmd, ud.url)
|
bb.fetch2.check_network_access(d, oscupdatecmd, ud.url)
|
||||||
runfetchcmd(oscupdatecmd, d, workdir=ud.moddir)
|
runfetchcmd(oscupdatecmd, d)
|
||||||
else:
|
else:
|
||||||
oscfetchcmd = self._buildosccommand(ud, d, "fetch")
|
oscfetchcmd = self._buildosccommand(ud, d, "fetch")
|
||||||
logger.info("Fetch " + ud.url)
|
logger.info("Fetch " + ud.url)
|
||||||
# check out sources there
|
# check out sources there
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
bb.utils.mkdirhier(ud.pkgdir)
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
logger.debug(1, "Running %s", oscfetchcmd)
|
logger.debug(1, "Running %s", oscfetchcmd)
|
||||||
bb.fetch2.check_network_access(d, oscfetchcmd, ud.url)
|
bb.fetch2.check_network_access(d, oscfetchcmd, ud.url)
|
||||||
runfetchcmd(oscfetchcmd, d, workdir=ud.pkgdir)
|
runfetchcmd(oscfetchcmd, d)
|
||||||
|
|
||||||
|
os.chdir(os.path.join(ud.pkgdir + ud.path))
|
||||||
# tar them up to a defined filename
|
# tar them up to a defined filename
|
||||||
runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d,
|
runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d, cleanup = [ud.localpath])
|
||||||
cleanup=[ud.localpath], workdir=os.path.join(ud.pkgdir + ud.path))
|
|
||||||
|
|
||||||
def supports_srcrev(self):
|
def supports_srcrev(self):
|
||||||
return False
|
return False
|
||||||
@@ -112,7 +114,7 @@ class Osc(FetchMethod):
|
|||||||
Generate a .oscrc to be used for this run.
|
Generate a .oscrc to be used for this run.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config_path = os.path.join(d.getVar('OSCDIR', True), "oscrc")
|
config_path = os.path.join(data.expand('${OSCDIR}', d), "oscrc")
|
||||||
if (os.path.exists(config_path)):
|
if (os.path.exists(config_path)):
|
||||||
os.remove(config_path)
|
os.remove(config_path)
|
||||||
|
|
||||||
@@ -121,8 +123,8 @@ class Osc(FetchMethod):
|
|||||||
f.write("apisrv = %s\n" % ud.host)
|
f.write("apisrv = %s\n" % ud.host)
|
||||||
f.write("scheme = http\n")
|
f.write("scheme = http\n")
|
||||||
f.write("su-wrapper = su -c\n")
|
f.write("su-wrapper = su -c\n")
|
||||||
f.write("build-root = %s\n" % d.getVar('WORKDIR', True))
|
f.write("build-root = %s\n" % data.expand('${WORKDIR}', d))
|
||||||
f.write("urllist = %s\n" % d.getVar("OSCURLLIST", True))
|
f.write("urllist = http://moblin-obs.jf.intel.com:8888/build/%(project)s/%(repository)s/%(buildarch)s/:full/%(name)s.rpm\n")
|
||||||
f.write("extra-pkgs = gzip\n")
|
f.write("extra-pkgs = gzip\n")
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
f.write("[%s]\n" % ud.host)
|
f.write("[%s]\n" % ud.host)
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
# ex:ts=4:sw=4:sts=4:et
|
# ex:ts=4:sw=4:sts=4:et
|
||||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
"""
|
"""
|
||||||
BitBake 'Fetch' implementation for perforce
|
BitBake 'Fetch' implementations
|
||||||
|
|
||||||
|
Classes for obtaining upstream sources for the
|
||||||
|
BitBake build tools.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Copyright (C) 2003, 2004 Chris Larson
|
# Copyright (C) 2003, 2004 Chris Larson
|
||||||
# Copyright (C) 2016 Kodak Alaris, Inc.
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
@@ -23,7 +25,9 @@ BitBake 'Fetch' implementation for perforce
|
|||||||
#
|
#
|
||||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||||
|
|
||||||
|
from future_builtins import zip
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import bb
|
import bb
|
||||||
from bb import data
|
from bb import data
|
||||||
@@ -33,178 +37,151 @@ from bb.fetch2 import logger
|
|||||||
from bb.fetch2 import runfetchcmd
|
from bb.fetch2 import runfetchcmd
|
||||||
|
|
||||||
class Perforce(FetchMethod):
|
class Perforce(FetchMethod):
|
||||||
""" Class to fetch from perforce repositories """
|
|
||||||
def supports(self, ud, d):
|
def supports(self, ud, d):
|
||||||
""" Check to see if a given url can be fetched with perforce. """
|
|
||||||
return ud.type in ['p4']
|
return ud.type in ['p4']
|
||||||
|
|
||||||
def urldata_init(self, ud, d):
|
def doparse(url, d):
|
||||||
"""
|
parm = {}
|
||||||
Initialize perforce specific variables within url data. If P4CONFIG is
|
path = url.split("://")[1]
|
||||||
provided by the env, use it. If P4PORT is specified by the recipe, use
|
delim = path.find("@");
|
||||||
its values, which may override the settings in P4CONFIG.
|
|
||||||
"""
|
|
||||||
ud.basecmd = d.getVar('FETCHCMD_p4', True)
|
|
||||||
if not ud.basecmd:
|
|
||||||
ud.basecmd = "/usr/bin/env p4"
|
|
||||||
|
|
||||||
ud.dldir = d.getVar('P4DIR', True)
|
|
||||||
if not ud.dldir:
|
|
||||||
ud.dldir = '%s/%s' % (d.getVar('DL_DIR', True), 'p4')
|
|
||||||
|
|
||||||
path = ud.url.split('://')[1]
|
|
||||||
path = path.split(';')[0]
|
|
||||||
delim = path.find('@');
|
|
||||||
if delim != -1:
|
if delim != -1:
|
||||||
(ud.user, ud.pswd) = path.split('@')[0].split(':')
|
(user, pswd, host, port) = path.split('@')[0].split(":")
|
||||||
ud.path = path.split('@')[1]
|
path = path.split('@')[1]
|
||||||
else:
|
else:
|
||||||
ud.path = path
|
(host, port) = d.getVar('P4PORT', False).split(':')
|
||||||
|
user = ""
|
||||||
|
pswd = ""
|
||||||
|
|
||||||
ud.usingp4config = False
|
if path.find(";") != -1:
|
||||||
p4port = d.getVar('P4PORT', True)
|
keys=[]
|
||||||
|
values=[]
|
||||||
|
plist = path.split(';')
|
||||||
|
for item in plist:
|
||||||
|
if item.count('='):
|
||||||
|
(key, value) = item.split('=')
|
||||||
|
keys.append(key)
|
||||||
|
values.append(value)
|
||||||
|
|
||||||
if p4port:
|
parm = dict(zip(keys, values))
|
||||||
logger.debug(1, 'Using recipe provided P4PORT: %s' % p4port)
|
path = "//" + path.split(';')[0]
|
||||||
ud.host = p4port
|
host += ":%s" % (port)
|
||||||
else:
|
parm["cset"] = Perforce.getcset(d, path, host, user, pswd, parm)
|
||||||
logger.debug(1, 'Trying to use P4CONFIG to automatically set P4PORT...')
|
|
||||||
ud.usingp4config = True
|
|
||||||
p4cmd = '%s info | grep "Server address"' % ud.basecmd
|
|
||||||
bb.fetch2.check_network_access(d, p4cmd)
|
|
||||||
ud.host = runfetchcmd(p4cmd, d, True)
|
|
||||||
ud.host = ud.host.split(': ')[1].strip()
|
|
||||||
logger.debug(1, 'Determined P4PORT to be: %s' % ud.host)
|
|
||||||
if not ud.host:
|
|
||||||
raise FetchError('Could not determine P4PORT from P4CONFIG')
|
|
||||||
|
|
||||||
if ud.path.find('/...') >= 0:
|
|
||||||
ud.pathisdir = True
|
|
||||||
else:
|
|
||||||
ud.pathisdir = False
|
|
||||||
|
|
||||||
cleanedpath = ud.path.replace('/...', '').replace('/', '.')
|
return host, path, user, pswd, parm
|
||||||
cleanedhost = ud.host.replace(':', '.')
|
doparse = staticmethod(doparse)
|
||||||
ud.pkgdir = os.path.join(ud.dldir, cleanedhost, cleanedpath)
|
|
||||||
|
|
||||||
ud.setup_revisons(d)
|
def getcset(d, depot, host, user, pswd, parm):
|
||||||
|
|
||||||
ud.localfile = data.expand('%s_%s_%s.tar.gz' % (cleanedhost, cleanedpath, ud.revision), d)
|
|
||||||
|
|
||||||
def _buildp4command(self, ud, d, command, depot_filename=None):
|
|
||||||
"""
|
|
||||||
Build a p4 commandline. Valid commands are "changes", "print", and
|
|
||||||
"files". depot_filename is the full path to the file in the depot
|
|
||||||
including the trailing '#rev' value.
|
|
||||||
"""
|
|
||||||
p4opt = ""
|
p4opt = ""
|
||||||
|
if "cset" in parm:
|
||||||
|
return parm["cset"];
|
||||||
|
if user:
|
||||||
|
p4opt += " -u %s" % (user)
|
||||||
|
if pswd:
|
||||||
|
p4opt += " -P %s" % (pswd)
|
||||||
|
if host:
|
||||||
|
p4opt += " -p %s" % (host)
|
||||||
|
|
||||||
if ud.user:
|
p4date = d.getVar("P4DATE", True)
|
||||||
p4opt += ' -u "%s"' % (ud.user)
|
if "revision" in parm:
|
||||||
|
depot += "#%s" % (parm["revision"])
|
||||||
|
elif "label" in parm:
|
||||||
|
depot += "@%s" % (parm["label"])
|
||||||
|
elif p4date:
|
||||||
|
depot += "@%s" % (p4date)
|
||||||
|
|
||||||
if ud.pswd:
|
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
|
||||||
p4opt += ' -P "%s"' % (ud.pswd)
|
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()
|
||||||
|
logger.debug(1, "READ %s", cset)
|
||||||
|
if not cset:
|
||||||
|
return -1
|
||||||
|
|
||||||
if ud.host and not ud.usingp4config:
|
return cset.split(' ')[1]
|
||||||
p4opt += ' -p %s' % (ud.host)
|
getcset = staticmethod(getcset)
|
||||||
|
|
||||||
if hasattr(ud, 'revision') and ud.revision:
|
def urldata_init(self, ud, d):
|
||||||
pathnrev = '%s@%s' % (ud.path, ud.revision)
|
(host, path, user, pswd, parm) = Perforce.doparse(ud.url, d)
|
||||||
|
|
||||||
|
base_path = path.replace('/...', '')
|
||||||
|
base_path = self._strip_leading_slashes(base_path)
|
||||||
|
|
||||||
|
if "label" in parm:
|
||||||
|
version = parm["label"]
|
||||||
else:
|
else:
|
||||||
pathnrev = '%s' % (ud.path)
|
version = Perforce.getcset(d, path, host, user, pswd, parm)
|
||||||
|
|
||||||
if depot_filename:
|
ud.localfile = data.expand('%s+%s+%s.tar.gz' % (host, base_path.replace('/', '.'), version), d)
|
||||||
if ud.pathisdir: # Remove leading path to obtain filename
|
|
||||||
filename = depot_filename[len(ud.path)-1:]
|
|
||||||
else:
|
|
||||||
filename = depot_filename[depot_filename.rfind('/'):]
|
|
||||||
filename = filename[:filename.find('#')] # Remove trailing '#rev'
|
|
||||||
|
|
||||||
if command == 'changes':
|
|
||||||
p4cmd = '%s%s changes -m 1 //%s' % (ud.basecmd, p4opt, pathnrev)
|
|
||||||
elif command == 'print':
|
|
||||||
if depot_filename != None:
|
|
||||||
p4cmd = '%s%s print -o "p4/%s" "%s"' % (ud.basecmd, p4opt, filename, depot_filename)
|
|
||||||
else:
|
|
||||||
raise FetchError('No depot file name provided to p4 %s' % command, ud.url)
|
|
||||||
elif command == 'files':
|
|
||||||
p4cmd = '%s%s files //%s' % (ud.basecmd, p4opt, pathnrev)
|
|
||||||
else:
|
|
||||||
raise FetchError('Invalid p4 command %s' % command, ud.url)
|
|
||||||
|
|
||||||
return p4cmd
|
|
||||||
|
|
||||||
def _p4listfiles(self, ud, d):
|
|
||||||
"""
|
|
||||||
Return a list of the file names which are present in the depot using the
|
|
||||||
'p4 files' command, including trailing '#rev' file revision indicator
|
|
||||||
"""
|
|
||||||
p4cmd = self._buildp4command(ud, d, 'files')
|
|
||||||
bb.fetch2.check_network_access(d, p4cmd)
|
|
||||||
p4fileslist = runfetchcmd(p4cmd, d, True)
|
|
||||||
p4fileslist = [f.rstrip() for f in p4fileslist.splitlines()]
|
|
||||||
|
|
||||||
if not p4fileslist:
|
|
||||||
raise FetchError('Unable to fetch listing of p4 files from %s@%s' % (ud.host, ud.path))
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
filelist = []
|
|
||||||
|
|
||||||
for filename in p4fileslist:
|
|
||||||
item = filename.split(' - ')
|
|
||||||
lastaction = item[1].split()
|
|
||||||
logger.debug(1, 'File: %s Last Action: %s' % (item[0], lastaction[0]))
|
|
||||||
if lastaction[0] == 'delete':
|
|
||||||
continue
|
|
||||||
filelist.append(item[0])
|
|
||||||
|
|
||||||
return filelist
|
|
||||||
|
|
||||||
def download(self, ud, d):
|
def download(self, ud, d):
|
||||||
""" Get the list of files, fetch each one """
|
"""
|
||||||
filelist = self._p4listfiles(ud, d)
|
Fetch urls
|
||||||
if not filelist:
|
"""
|
||||||
raise FetchError('No files found in depot %s@%s' % (ud.host, ud.path))
|
|
||||||
|
|
||||||
bb.utils.remove(ud.pkgdir, True)
|
(host, depot, user, pswd, parm) = Perforce.doparse(ud.url, d)
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
|
||||||
|
|
||||||
for afile in filelist:
|
if depot.find('/...') != -1:
|
||||||
p4fetchcmd = self._buildp4command(ud, d, 'print', afile)
|
path = depot[:depot.find('/...')]
|
||||||
bb.fetch2.check_network_access(d, p4fetchcmd)
|
else:
|
||||||
runfetchcmd(p4fetchcmd, d, workdir=ud.pkgdir)
|
path = depot[:depot.rfind('/')]
|
||||||
|
|
||||||
runfetchcmd('tar -czf %s p4' % (ud.localpath), d, cleanup=[ud.localpath], workdir=ud.pkgdir)
|
module = parm.get('module', os.path.basename(path))
|
||||||
|
|
||||||
def clean(self, ud, d):
|
# Get the p4 command
|
||||||
""" Cleanup p4 specific files and dirs"""
|
p4opt = ""
|
||||||
bb.utils.remove(ud.localpath)
|
if user:
|
||||||
bb.utils.remove(ud.pkgdir, True)
|
p4opt += " -u %s" % (user)
|
||||||
|
|
||||||
def supports_srcrev(self):
|
if pswd:
|
||||||
return True
|
p4opt += " -P %s" % (pswd)
|
||||||
|
|
||||||
def _revision_key(self, ud, d, name):
|
if host:
|
||||||
""" Return a unique key for the url """
|
p4opt += " -p %s" % (host)
|
||||||
return 'p4:%s' % ud.pkgdir
|
|
||||||
|
|
||||||
def _latest_revision(self, ud, d, name):
|
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
|
||||||
""" Return the latest upstream scm revision number """
|
|
||||||
p4cmd = self._buildp4command(ud, d, "changes")
|
|
||||||
bb.fetch2.check_network_access(d, p4cmd)
|
|
||||||
tip = runfetchcmd(p4cmd, d, True)
|
|
||||||
|
|
||||||
if not tip:
|
# create temp directory
|
||||||
raise FetchError('Could not determine the latest perforce changelist')
|
logger.debug(2, "Fetch: creating temporary directory")
|
||||||
|
bb.utils.mkdirhier(d.expand('${WORKDIR}'))
|
||||||
|
mktemp = d.getVar("FETCHCMD_p4mktemp", True) or d.expand("mktemp -d -q '${WORKDIR}/oep4.XXXXXX'")
|
||||||
|
tmpfile, errors = bb.process.run(mktemp)
|
||||||
|
tmpfile = tmpfile.strip()
|
||||||
|
if not tmpfile:
|
||||||
|
raise FetchError("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.", ud.url)
|
||||||
|
|
||||||
tipcset = tip.split(' ')[1]
|
if "label" in parm:
|
||||||
logger.debug(1, 'p4 tip found to be changelist %s' % tipcset)
|
depot = "%s@%s" % (depot, parm["label"])
|
||||||
return tipcset
|
else:
|
||||||
|
cset = Perforce.getcset(d, depot, host, user, pswd, parm)
|
||||||
|
depot = "%s@%s" % (depot, cset)
|
||||||
|
|
||||||
def sortable_revision(self, ud, d, name):
|
os.chdir(tmpfile)
|
||||||
""" Return a sortable revision number """
|
logger.info("Fetch " + ud.url)
|
||||||
return False, self._build_revision(ud, d)
|
logger.info("%s%s files %s", p4cmd, p4opt, depot)
|
||||||
|
p4file, errors = bb.process.run("%s%s files %s" % (p4cmd, p4opt, depot))
|
||||||
|
p4file = [f.rstrip() for f in p4file.splitlines()]
|
||||||
|
|
||||||
def _build_revision(self, ud, d):
|
if not p4file:
|
||||||
return ud.revision
|
raise FetchError("Fetch: unable to get the P4 files from %s" % depot, ud.url)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
for file in p4file:
|
||||||
|
list = file.split()
|
||||||
|
|
||||||
|
if list[2] == "delete":
|
||||||
|
continue
|
||||||
|
|
||||||
|
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)
|
||||||
|
count = count + 1
|
||||||
|
|
||||||
|
if count == 0:
|
||||||
|
logger.error()
|
||||||
|
raise FetchError("Fetch: No files gathered from the P4 fetch", ud.url)
|
||||||
|
|
||||||
|
runfetchcmd("tar -czf %s %s" % (ud.localpath, module), d, cleanup = [ud.localpath])
|
||||||
|
# cleanup
|
||||||
|
bb.utils.prunedir(tmpfile)
|
||||||
|
|||||||
@@ -69,23 +69,24 @@ class Repo(FetchMethod):
|
|||||||
else:
|
else:
|
||||||
username = ""
|
username = ""
|
||||||
|
|
||||||
repodir = os.path.join(codir, "repo")
|
bb.utils.mkdirhier(os.path.join(codir, "repo"))
|
||||||
bb.utils.mkdirhier(repodir)
|
os.chdir(os.path.join(codir, "repo"))
|
||||||
if not os.path.exists(os.path.join(repodir, ".repo")):
|
if not os.path.exists(os.path.join(codir, "repo", ".repo")):
|
||||||
bb.fetch2.check_network_access(d, "repo init -m %s -b %s -u %s://%s%s%s" % (ud.manifest, ud.branch, ud.proto, username, ud.host, ud.path), ud.url)
|
bb.fetch2.check_network_access(d, "repo init -m %s -b %s -u %s://%s%s%s" % (ud.manifest, ud.branch, ud.proto, username, ud.host, ud.path), ud.url)
|
||||||
runfetchcmd("repo init -m %s -b %s -u %s://%s%s%s" % (ud.manifest, ud.branch, ud.proto, username, ud.host, ud.path), d, workdir=repodir)
|
runfetchcmd("repo init -m %s -b %s -u %s://%s%s%s" % (ud.manifest, ud.branch, ud.proto, username, ud.host, ud.path), d)
|
||||||
|
|
||||||
bb.fetch2.check_network_access(d, "repo sync %s" % ud.url, ud.url)
|
bb.fetch2.check_network_access(d, "repo sync %s" % ud.url, ud.url)
|
||||||
runfetchcmd("repo sync", d, workdir=repodir)
|
runfetchcmd("repo sync", d)
|
||||||
|
os.chdir(codir)
|
||||||
|
|
||||||
scmdata = ud.parm.get("scmdata", "")
|
scmdata = ud.parm.get("scmdata", "")
|
||||||
if scmdata == "keep":
|
if scmdata == "keep":
|
||||||
tar_flags = ""
|
tar_flags = ""
|
||||||
else:
|
else:
|
||||||
tar_flags = "--exclude='.repo' --exclude='.git'"
|
tar_flags = "--exclude '.repo' --exclude '.git'"
|
||||||
|
|
||||||
# Create a cache
|
# Create a cache
|
||||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.join(".", "*") ), d, workdir=codir)
|
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.join(".", "*") ), d)
|
||||||
|
|
||||||
def supports_srcrev(self):
|
def supports_srcrev(self):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ SRC_URI = "sftp://user@host.example.com/dir/path.file.txt"
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import bb
|
import bb
|
||||||
import urllib.request, urllib.parse, urllib.error
|
import urllib
|
||||||
|
import commands
|
||||||
from bb import data
|
from bb import data
|
||||||
from bb.fetch2 import URI
|
from bb.fetch2 import URI
|
||||||
from bb.fetch2 import FetchMethod
|
from bb.fetch2 import FetchMethod
|
||||||
@@ -92,7 +93,7 @@ class SFTP(FetchMethod):
|
|||||||
else:
|
else:
|
||||||
ud.basename = os.path.basename(ud.path)
|
ud.basename = os.path.basename(ud.path)
|
||||||
|
|
||||||
ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
|
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
|
||||||
|
|
||||||
def download(self, ud, d):
|
def download(self, ud, d):
|
||||||
"""Fetch urls"""
|
"""Fetch urls"""
|
||||||
@@ -120,7 +121,8 @@ class SFTP(FetchMethod):
|
|||||||
|
|
||||||
remote = '%s%s:%s' % (user, urlo.hostname, path)
|
remote = '%s%s:%s' % (user, urlo.hostname, path)
|
||||||
|
|
||||||
cmd = '%s %s %s %s' % (basecmd, port, remote, lpath)
|
cmd = '%s %s %s %s' % (basecmd, port, commands.mkarg(remote),
|
||||||
|
commands.mkarg(lpath))
|
||||||
|
|
||||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||||
runfetchcmd(cmd, d)
|
runfetchcmd(cmd, d)
|
||||||
|
|||||||
@@ -114,10 +114,12 @@ class SSH(FetchMethod):
|
|||||||
fr = host
|
fr = host
|
||||||
fr += ':%s' % path
|
fr += ':%s' % path
|
||||||
|
|
||||||
|
|
||||||
|
import commands
|
||||||
cmd = 'scp -B -r %s %s %s/' % (
|
cmd = 'scp -B -r %s %s %s/' % (
|
||||||
portarg,
|
portarg,
|
||||||
fr,
|
commands.mkarg(fr),
|
||||||
dldir
|
commands.mkarg(dldir)
|
||||||
)
|
)
|
||||||
|
|
||||||
bb.fetch2.check_network_access(d, cmd, urldata.url)
|
bb.fetch2.check_network_access(d, cmd, urldata.url)
|
||||||
|
|||||||
@@ -126,32 +126,35 @@ class Svn(FetchMethod):
|
|||||||
if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK):
|
if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK):
|
||||||
svnupdatecmd = self._buildsvncommand(ud, d, "update")
|
svnupdatecmd = self._buildsvncommand(ud, d, "update")
|
||||||
logger.info("Update " + ud.url)
|
logger.info("Update " + ud.url)
|
||||||
|
# update sources there
|
||||||
|
os.chdir(ud.moddir)
|
||||||
# We need to attempt to run svn upgrade first in case its an older working format
|
# We need to attempt to run svn upgrade first in case its an older working format
|
||||||
try:
|
try:
|
||||||
runfetchcmd(ud.basecmd + " upgrade", d, workdir=ud.moddir)
|
runfetchcmd(ud.basecmd + " upgrade", d)
|
||||||
except FetchError:
|
except FetchError:
|
||||||
pass
|
pass
|
||||||
logger.debug(1, "Running %s", svnupdatecmd)
|
logger.debug(1, "Running %s", svnupdatecmd)
|
||||||
bb.fetch2.check_network_access(d, svnupdatecmd, ud.url)
|
bb.fetch2.check_network_access(d, svnupdatecmd, ud.url)
|
||||||
runfetchcmd(svnupdatecmd, d, workdir=ud.moddir)
|
runfetchcmd(svnupdatecmd, d)
|
||||||
else:
|
else:
|
||||||
svnfetchcmd = self._buildsvncommand(ud, d, "fetch")
|
svnfetchcmd = self._buildsvncommand(ud, d, "fetch")
|
||||||
logger.info("Fetch " + ud.url)
|
logger.info("Fetch " + ud.url)
|
||||||
# check out sources there
|
# check out sources there
|
||||||
bb.utils.mkdirhier(ud.pkgdir)
|
bb.utils.mkdirhier(ud.pkgdir)
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
logger.debug(1, "Running %s", svnfetchcmd)
|
logger.debug(1, "Running %s", svnfetchcmd)
|
||||||
bb.fetch2.check_network_access(d, svnfetchcmd, ud.url)
|
bb.fetch2.check_network_access(d, svnfetchcmd, ud.url)
|
||||||
runfetchcmd(svnfetchcmd, d, workdir=ud.pkgdir)
|
runfetchcmd(svnfetchcmd, d)
|
||||||
|
|
||||||
scmdata = ud.parm.get("scmdata", "")
|
scmdata = ud.parm.get("scmdata", "")
|
||||||
if scmdata == "keep":
|
if scmdata == "keep":
|
||||||
tar_flags = ""
|
tar_flags = ""
|
||||||
else:
|
else:
|
||||||
tar_flags = "--exclude='.svn'"
|
tar_flags = "--exclude '.svn'"
|
||||||
|
|
||||||
|
os.chdir(ud.pkgdir)
|
||||||
# tar them up to a defined filename
|
# tar them up to a defined filename
|
||||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d,
|
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d, cleanup = [ud.localpath])
|
||||||
cleanup=[ud.localpath], workdir=ud.pkgdir)
|
|
||||||
|
|
||||||
def clean(self, ud, d):
|
def clean(self, ud, d):
|
||||||
""" Clean SVN specific files and dirs """
|
""" Clean SVN specific files and dirs """
|
||||||
|
|||||||
@@ -31,37 +31,13 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import bb
|
import bb
|
||||||
import bb.progress
|
import urllib
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
from bb import data
|
from bb import data
|
||||||
from bb.fetch2 import FetchMethod
|
from bb.fetch2 import FetchMethod
|
||||||
from bb.fetch2 import FetchError
|
from bb.fetch2 import FetchError
|
||||||
from bb.fetch2 import logger
|
from bb.fetch2 import logger
|
||||||
from bb.fetch2 import runfetchcmd
|
from bb.fetch2 import runfetchcmd
|
||||||
from bb.utils import export_proxies
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from bs4 import SoupStrainer
|
|
||||||
|
|
||||||
class WgetProgressHandler(bb.progress.LineFilterProgressHandler):
|
|
||||||
"""
|
|
||||||
Extract progress information from wget output.
|
|
||||||
Note: relies on --progress=dot (with -v or without -q/-nv) being
|
|
||||||
specified on the wget command line.
|
|
||||||
"""
|
|
||||||
def __init__(self, d):
|
|
||||||
super(WgetProgressHandler, self).__init__(d)
|
|
||||||
# Send an initial progress event so the bar gets shown
|
|
||||||
self._fire_progress(0)
|
|
||||||
|
|
||||||
def writeline(self, line):
|
|
||||||
percs = re.findall(r'(\d+)%\s+([\d.]+[A-Z])', line)
|
|
||||||
if percs:
|
|
||||||
progress = int(percs[-1][0])
|
|
||||||
rate = percs[-1][1] + '/s'
|
|
||||||
self.update(progress, rate)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Wget(FetchMethod):
|
class Wget(FetchMethod):
|
||||||
"""Class to fetch urls via 'wget'"""
|
"""Class to fetch urls via 'wget'"""
|
||||||
@@ -84,19 +60,15 @@ class Wget(FetchMethod):
|
|||||||
else:
|
else:
|
||||||
ud.basename = os.path.basename(ud.path)
|
ud.basename = os.path.basename(ud.path)
|
||||||
|
|
||||||
ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
|
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
|
||||||
if not ud.localfile:
|
|
||||||
ud.localfile = data.expand(urllib.parse.unquote(ud.host + ud.path).replace("/", "."), d)
|
|
||||||
|
|
||||||
self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 --passive-ftp --no-check-certificate"
|
self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
|
||||||
|
|
||||||
def _runwget(self, ud, d, command, quiet):
|
def _runwget(self, ud, d, command, quiet):
|
||||||
|
|
||||||
progresshandler = WgetProgressHandler(d)
|
|
||||||
|
|
||||||
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
|
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
|
||||||
bb.fetch2.check_network_access(d, command)
|
bb.fetch2.check_network_access(d, command)
|
||||||
runfetchcmd(command + ' --progress=dot -v', d, quiet, log=progresshandler)
|
runfetchcmd(command, d, quiet)
|
||||||
|
|
||||||
def download(self, ud, d):
|
def download(self, ud, d):
|
||||||
"""Fetch urls"""
|
"""Fetch urls"""
|
||||||
@@ -108,9 +80,6 @@ class Wget(FetchMethod):
|
|||||||
bb.utils.mkdirhier(os.path.dirname(dldir + os.sep + ud.localfile))
|
bb.utils.mkdirhier(os.path.dirname(dldir + os.sep + ud.localfile))
|
||||||
fetchcmd += " -O " + dldir + os.sep + ud.localfile
|
fetchcmd += " -O " + dldir + os.sep + ud.localfile
|
||||||
|
|
||||||
if ud.user and ud.pswd:
|
|
||||||
fetchcmd += " --user=%s --password=%s --auth-no-challenge" % (ud.user, ud.pswd)
|
|
||||||
|
|
||||||
uri = ud.url.split(";")[0]
|
uri = ud.url.split(";")[0]
|
||||||
if os.path.exists(ud.localpath):
|
if os.path.exists(ud.localpath):
|
||||||
# file exists, but we didnt complete it.. trying again..
|
# file exists, but we didnt complete it.. trying again..
|
||||||
@@ -131,12 +100,12 @@ class Wget(FetchMethod):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def checkstatus(self, fetch, ud, d, try_again=True):
|
def checkstatus(self, fetch, ud, d):
|
||||||
import urllib.request, urllib.error, urllib.parse, socket, http.client
|
import urllib2, socket, httplib
|
||||||
from urllib.response import addinfourl
|
from urllib import addinfourl
|
||||||
from bb.fetch2 import FetchConnectionCache
|
from bb.fetch2 import FetchConnectionCache
|
||||||
|
|
||||||
class HTTPConnectionCache(http.client.HTTPConnection):
|
class HTTPConnectionCache(httplib.HTTPConnection):
|
||||||
if fetch.connection_cache:
|
if fetch.connection_cache:
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""Connect to the host and port specified in __init__."""
|
"""Connect to the host and port specified in __init__."""
|
||||||
@@ -152,7 +121,7 @@ class Wget(FetchMethod):
|
|||||||
if self._tunnel_host:
|
if self._tunnel_host:
|
||||||
self._tunnel()
|
self._tunnel()
|
||||||
|
|
||||||
class CacheHTTPHandler(urllib.request.HTTPHandler):
|
class CacheHTTPHandler(urllib2.HTTPHandler):
|
||||||
def http_open(self, req):
|
def http_open(self, req):
|
||||||
return self.do_open(HTTPConnectionCache, req)
|
return self.do_open(HTTPConnectionCache, req)
|
||||||
|
|
||||||
@@ -166,7 +135,7 @@ class Wget(FetchMethod):
|
|||||||
- geturl(): return the original request URL
|
- geturl(): return the original request URL
|
||||||
- code: HTTP status code
|
- code: HTTP status code
|
||||||
"""
|
"""
|
||||||
host = req.host
|
host = req.get_host()
|
||||||
if not host:
|
if not host:
|
||||||
raise urlllib2.URLError('no host given')
|
raise urlllib2.URLError('no host given')
|
||||||
|
|
||||||
@@ -174,7 +143,7 @@ class Wget(FetchMethod):
|
|||||||
h.set_debuglevel(self._debuglevel)
|
h.set_debuglevel(self._debuglevel)
|
||||||
|
|
||||||
headers = dict(req.unredirected_hdrs)
|
headers = dict(req.unredirected_hdrs)
|
||||||
headers.update(dict((k, v) for k, v in list(req.headers.items())
|
headers.update(dict((k, v) for k, v in req.headers.items()
|
||||||
if k not in headers))
|
if k not in headers))
|
||||||
|
|
||||||
# We want to make an HTTP/1.1 request, but the addinfourl
|
# We want to make an HTTP/1.1 request, but the addinfourl
|
||||||
@@ -191,7 +160,7 @@ class Wget(FetchMethod):
|
|||||||
headers["Connection"] = "Keep-Alive" # Works for HTTP/1.0
|
headers["Connection"] = "Keep-Alive" # Works for HTTP/1.0
|
||||||
|
|
||||||
headers = dict(
|
headers = dict(
|
||||||
(name.title(), val) for name, val in list(headers.items()))
|
(name.title(), val) for name, val in headers.items())
|
||||||
|
|
||||||
if req._tunnel_host:
|
if req._tunnel_host:
|
||||||
tunnel_headers = {}
|
tunnel_headers = {}
|
||||||
@@ -204,12 +173,12 @@ class Wget(FetchMethod):
|
|||||||
h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
|
h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
h.request(req.get_method(), req.selector, req.data, headers)
|
h.request(req.get_method(), req.get_selector(), req.data, headers)
|
||||||
except socket.error as err: # XXX what error?
|
except socket.error, err: # XXX what error?
|
||||||
# Don't close connection when cache is enabled.
|
# Don't close connection when cache is enabled.
|
||||||
if fetch.connection_cache is None:
|
if fetch.connection_cache is None:
|
||||||
h.close()
|
h.close()
|
||||||
raise urllib.error.URLError(err)
|
raise urllib2.URLError(err)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
r = h.getresponse(buffering=True)
|
r = h.getresponse(buffering=True)
|
||||||
@@ -249,7 +218,23 @@ class Wget(FetchMethod):
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
class HTTPMethodFallback(urllib.request.BaseHandler):
|
def export_proxies(d):
|
||||||
|
variables = ['http_proxy', 'HTTP_PROXY', 'https_proxy', 'HTTPS_PROXY',
|
||||||
|
'ftp_proxy', 'FTP_PROXY', 'no_proxy', 'NO_PROXY']
|
||||||
|
exported = False
|
||||||
|
|
||||||
|
for v in variables:
|
||||||
|
if v in os.environ.keys():
|
||||||
|
exported = True
|
||||||
|
else:
|
||||||
|
v_proxy = d.getVar(v, True)
|
||||||
|
if v_proxy is not None:
|
||||||
|
os.environ[v] = v_proxy
|
||||||
|
exported = True
|
||||||
|
|
||||||
|
return exported
|
||||||
|
|
||||||
|
class HTTPMethodFallback(urllib2.BaseHandler):
|
||||||
"""
|
"""
|
||||||
Fallback to GET if HEAD is not allowed (405 HTTP error)
|
Fallback to GET if HEAD is not allowed (405 HTTP error)
|
||||||
"""
|
"""
|
||||||
@@ -257,11 +242,11 @@ class Wget(FetchMethod):
|
|||||||
fp.read()
|
fp.read()
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
newheaders = dict((k,v) for k,v in list(req.headers.items())
|
newheaders = dict((k,v) for k,v in req.headers.items()
|
||||||
if k.lower() not in ("content-length", "content-type"))
|
if k.lower() not in ("content-length", "content-type"))
|
||||||
return self.parent.open(urllib.request.Request(req.get_full_url(),
|
return self.parent.open(urllib2.Request(req.get_full_url(),
|
||||||
headers=newheaders,
|
headers=newheaders,
|
||||||
origin_req_host=req.origin_req_host,
|
origin_req_host=req.get_origin_req_host(),
|
||||||
unverifiable=True))
|
unverifiable=True))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -276,49 +261,38 @@ class Wget(FetchMethod):
|
|||||||
"""
|
"""
|
||||||
http_error_406 = http_error_405
|
http_error_406 = http_error_405
|
||||||
|
|
||||||
class FixedHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
|
class FixedHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||||
"""
|
"""
|
||||||
urllib2.HTTPRedirectHandler resets the method to GET on redirect,
|
urllib2.HTTPRedirectHandler resets the method to GET on redirect,
|
||||||
when we want to follow redirects using the original method.
|
when we want to follow redirects using the original method.
|
||||||
"""
|
"""
|
||||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||||
newreq = urllib.request.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl)
|
newreq = urllib2.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl)
|
||||||
newreq.get_method = lambda: req.get_method()
|
newreq.get_method = lambda: req.get_method()
|
||||||
return newreq
|
return newreq
|
||||||
exported_proxies = export_proxies(d)
|
exported_proxies = export_proxies(d)
|
||||||
|
|
||||||
handlers = [FixedHTTPRedirectHandler, HTTPMethodFallback]
|
handlers = [FixedHTTPRedirectHandler, HTTPMethodFallback]
|
||||||
if export_proxies:
|
if export_proxies:
|
||||||
handlers.append(urllib.request.ProxyHandler())
|
handlers.append(urllib2.ProxyHandler())
|
||||||
handlers.append(CacheHTTPHandler())
|
handlers.append(CacheHTTPHandler())
|
||||||
# XXX: Since Python 2.7.9 ssl cert validation is enabled by default
|
# XXX: Since Python 2.7.9 ssl cert validation is enabled by default
|
||||||
# see PEP-0476, this causes verification errors on some https servers
|
# see PEP-0476, this causes verification errors on some https servers
|
||||||
# so disable by default.
|
# so disable by default.
|
||||||
import ssl
|
import ssl
|
||||||
if hasattr(ssl, '_create_unverified_context'):
|
if hasattr(ssl, '_create_unverified_context'):
|
||||||
handlers.append(urllib.request.HTTPSHandler(context=ssl._create_unverified_context()))
|
handlers.append(urllib2.HTTPSHandler(context=ssl._create_unverified_context()))
|
||||||
opener = urllib.request.build_opener(*handlers)
|
opener = urllib2.build_opener(*handlers)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
uri = ud.url.split(";")[0]
|
uri = ud.url.split(";")[0]
|
||||||
r = urllib.request.Request(uri)
|
r = urllib2.Request(uri)
|
||||||
r.get_method = lambda: "HEAD"
|
r.get_method = lambda: "HEAD"
|
||||||
|
|
||||||
if ud.user:
|
|
||||||
import base64
|
|
||||||
encodeuser = base64.b64encode(ud.user.encode('utf-8')).decode("utf-8")
|
|
||||||
authheader = "Basic %s" % encodeuser
|
|
||||||
r.add_header("Authorization", authheader)
|
|
||||||
|
|
||||||
opener.open(r)
|
opener.open(r)
|
||||||
except urllib.error.URLError as e:
|
except urllib2.URLError as e:
|
||||||
if try_again:
|
# debug for now to avoid spamming the logs in e.g. remote sstate searches
|
||||||
logger.debug(2, "checkstatus: trying again")
|
logger.debug(2, "checkstatus() urlopen failed: %s" % e)
|
||||||
return self.checkstatus(fetch, ud, d, False)
|
return False
|
||||||
else:
|
|
||||||
# debug for now to avoid spamming the logs in e.g. remote sstate searches
|
|
||||||
logger.debug(2, "checkstatus() urlopen failed: %s" % e)
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _parse_path(self, regex, s):
|
def _parse_path(self, regex, s):
|
||||||
@@ -419,7 +393,7 @@ class Wget(FetchMethod):
|
|||||||
version = ['', '', '']
|
version = ['', '', '']
|
||||||
|
|
||||||
bb.debug(3, "VersionURL: %s" % (url))
|
bb.debug(3, "VersionURL: %s" % (url))
|
||||||
soup = BeautifulSoup(self._fetch_index(url, ud, d), "html.parser", parse_only=SoupStrainer("a"))
|
soup = BeautifulSoup(self._fetch_index(url, ud, d))
|
||||||
if not soup:
|
if not soup:
|
||||||
bb.debug(3, "*** %s NO SOUP" % (url))
|
bb.debug(3, "*** %s NO SOUP" % (url))
|
||||||
return ""
|
return ""
|
||||||
@@ -458,10 +432,10 @@ class Wget(FetchMethod):
|
|||||||
version_dir = ['', '', '']
|
version_dir = ['', '', '']
|
||||||
version = ['', '', '']
|
version = ['', '', '']
|
||||||
|
|
||||||
dirver_regex = re.compile("(?P<pfx>\D*)(?P<ver>(\d+[\.\-_])+(\d+))")
|
dirver_regex = re.compile("(\D*)((\d+[\.\-_])+(\d+))")
|
||||||
s = dirver_regex.search(dirver)
|
s = dirver_regex.search(dirver)
|
||||||
if s:
|
if s:
|
||||||
version_dir[1] = s.group('ver')
|
version_dir[1] = s.group(2)
|
||||||
else:
|
else:
|
||||||
version_dir[1] = dirver
|
version_dir[1] = dirver
|
||||||
|
|
||||||
@@ -469,26 +443,16 @@ class Wget(FetchMethod):
|
|||||||
ud.path.split(dirver)[0], ud.user, ud.pswd, {}])
|
ud.path.split(dirver)[0], ud.user, ud.pswd, {}])
|
||||||
bb.debug(3, "DirURL: %s, %s" % (dirs_uri, package))
|
bb.debug(3, "DirURL: %s, %s" % (dirs_uri, package))
|
||||||
|
|
||||||
soup = BeautifulSoup(self._fetch_index(dirs_uri, ud, d), "html.parser", parse_only=SoupStrainer("a"))
|
soup = BeautifulSoup(self._fetch_index(dirs_uri, ud, d))
|
||||||
if not soup:
|
if not soup:
|
||||||
return version[1]
|
return version[1]
|
||||||
|
|
||||||
for line in soup.find_all('a', href=True):
|
for line in soup.find_all('a', href=True):
|
||||||
s = dirver_regex.search(line['href'].strip("/"))
|
s = dirver_regex.search(line['href'].strip("/"))
|
||||||
if s:
|
if s:
|
||||||
sver = s.group('ver')
|
version_dir_new = ['', s.group(2), '']
|
||||||
|
|
||||||
# When prefix is part of the version directory it need to
|
|
||||||
# ensure that only version directory is used so remove previous
|
|
||||||
# directories if exists.
|
|
||||||
#
|
|
||||||
# Example: pfx = '/dir1/dir2/v' and version = '2.5' the expected
|
|
||||||
# result is v2.5.
|
|
||||||
spfx = s.group('pfx').split('/')[-1]
|
|
||||||
|
|
||||||
version_dir_new = ['', sver, '']
|
|
||||||
if self._vercmp(version_dir, version_dir_new) <= 0:
|
if self._vercmp(version_dir, version_dir_new) <= 0:
|
||||||
dirver_new = spfx + sver
|
dirver_new = s.group(1) + s.group(2)
|
||||||
path = ud.path.replace(dirver, dirver_new, True) \
|
path = ud.path.replace(dirver, dirver_new, True) \
|
||||||
.split(package)[0]
|
.split(package)[0]
|
||||||
uri = bb.fetch.encodeurl([ud.type, ud.host, path,
|
uri = bb.fetch.encodeurl([ud.type, ud.host, path,
|
||||||
@@ -542,7 +506,7 @@ class Wget(FetchMethod):
|
|||||||
self.suffix_regex_comp = re.compile(psuffix_regex)
|
self.suffix_regex_comp = re.compile(psuffix_regex)
|
||||||
|
|
||||||
# compile regex, can be specific by package or generic regex
|
# compile regex, can be specific by package or generic regex
|
||||||
pn_regex = d.getVar('UPSTREAM_CHECK_REGEX', True)
|
pn_regex = d.getVar('REGEX', True)
|
||||||
if pn_regex:
|
if pn_regex:
|
||||||
package_custom_regex_comp = re.compile(pn_regex)
|
package_custom_regex_comp = re.compile(pn_regex)
|
||||||
else:
|
else:
|
||||||
@@ -578,7 +542,7 @@ class Wget(FetchMethod):
|
|||||||
bb.debug(3, "latest_versionstring, regex: %s" % (package_regex.pattern))
|
bb.debug(3, "latest_versionstring, regex: %s" % (package_regex.pattern))
|
||||||
|
|
||||||
uri = ""
|
uri = ""
|
||||||
regex_uri = d.getVar("UPSTREAM_CHECK_URI", True)
|
regex_uri = d.getVar("REGEX_URI", True)
|
||||||
if not regex_uri:
|
if not regex_uri:
|
||||||
path = ud.path.split(package)[0]
|
path = ud.path.split(package)[0]
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import sys
|
|||||||
import logging
|
import logging
|
||||||
import optparse
|
import optparse
|
||||||
import warnings
|
import warnings
|
||||||
import fcntl
|
|
||||||
|
|
||||||
import bb
|
import bb
|
||||||
from bb import event
|
from bb import event
|
||||||
@@ -101,12 +100,11 @@ def import_extension_module(pkg, modulename, checkattr):
|
|||||||
# Dynamically load the UI based on the ui name. Although we
|
# Dynamically load the UI based on the ui name. Although we
|
||||||
# suggest a fixed set this allows you to have flexibility in which
|
# suggest a fixed set this allows you to have flexibility in which
|
||||||
# ones are available.
|
# ones are available.
|
||||||
module = __import__(pkg.__name__, fromlist=[modulename])
|
module = __import__(pkg.__name__, fromlist = [modulename])
|
||||||
return getattr(module, modulename)
|
return getattr(module, modulename)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
modules = present_options(list_extension_modules(pkg, checkattr))
|
raise BBMainException('FATAL: Unable to import extension module "%s" from %s. Valid extension modules: %s' % (modulename, pkg.__name__, present_options(list_extension_modules(pkg, checkattr))))
|
||||||
raise BBMainException('FATAL: Unable to import extension module "%s" from %s. '
|
|
||||||
'Valid extension modules: %s' % (modulename, pkg.__name__, modules))
|
|
||||||
|
|
||||||
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
|
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
|
||||||
warnlog = logging.getLogger("BitBake.Warnings")
|
warnlog = logging.getLogger("BitBake.Warnings")
|
||||||
@@ -117,7 +115,7 @@ def _showwarning(message, category, filename, lineno, file=None, line=None):
|
|||||||
_warnings_showwarning(message, category, filename, lineno, file, line)
|
_warnings_showwarning(message, category, filename, lineno, file, line)
|
||||||
else:
|
else:
|
||||||
s = warnings.formatwarning(message, category, filename, lineno)
|
s = warnings.formatwarning(message, category, filename, lineno)
|
||||||
warnlog.warning(s)
|
warnlog.warn(s)
|
||||||
|
|
||||||
warnings.showwarning = _showwarning
|
warnings.showwarning = _showwarning
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
@@ -131,189 +129,133 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
|||||||
|
|
||||||
def parseCommandLine(self, argv=sys.argv):
|
def parseCommandLine(self, argv=sys.argv):
|
||||||
parser = optparse.OptionParser(
|
parser = optparse.OptionParser(
|
||||||
formatter=BitbakeHelpFormatter(),
|
formatter = BitbakeHelpFormatter(),
|
||||||
version="BitBake Build Tool Core version %s" % bb.__version__,
|
version = "BitBake Build Tool Core version %s" % bb.__version__,
|
||||||
usage="""%prog [options] [recipename/target recipe:do_task ...]
|
usage = """%prog [options] [recipename/target recipe:do_task ...]
|
||||||
|
|
||||||
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
|
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
|
||||||
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
|
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
|
||||||
will provide the layer, BBFILES and other configuration information.""")
|
will provide the layer, BBFILES and other configuration information.""")
|
||||||
|
|
||||||
parser.add_option("-b", "--buildfile", action="store", dest="buildfile", default=None,
|
parser.add_option("-b", "--buildfile", help = "Execute tasks from a specific .bb recipe directly. WARNING: Does not handle any dependencies from other recipes.",
|
||||||
help="Execute tasks from a specific .bb recipe directly. WARNING: Does "
|
action = "store", dest = "buildfile", default = None)
|
||||||
"not handle any dependencies from other recipes.")
|
|
||||||
|
|
||||||
parser.add_option("-k", "--continue", action="store_false", dest="abort", default=True,
|
parser.add_option("-k", "--continue", help = "Continue as much as possible after an error. While the target that failed and anything depending on it cannot be built, as much as possible will be built before stopping.",
|
||||||
help="Continue as much as possible after an error. While the target that "
|
action = "store_false", dest = "abort", default = True)
|
||||||
"failed and anything depending on it cannot be built, as much as "
|
|
||||||
"possible will be built before stopping.")
|
|
||||||
|
|
||||||
parser.add_option("-a", "--tryaltconfigs", action="store_true",
|
parser.add_option("-a", "--tryaltconfigs", help = "Continue with builds by trying to use alternative providers where possible.",
|
||||||
dest="tryaltconfigs", default=False,
|
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||||
help="Continue with builds by trying to use alternative providers "
|
|
||||||
"where possible.")
|
|
||||||
|
|
||||||
parser.add_option("-f", "--force", action="store_true", dest="force", default=False,
|
parser.add_option("-f", "--force", help = "Force the specified targets/task to run (invalidating any existing stamp file).",
|
||||||
help="Force the specified targets/task to run (invalidating any "
|
action = "store_true", dest = "force", default = False)
|
||||||
"existing stamp file).")
|
|
||||||
|
|
||||||
parser.add_option("-c", "--cmd", action="store", dest="cmd",
|
parser.add_option("-c", "--cmd", help = "Specify the task to execute. The exact options available depend on the metadata. Some examples might be 'compile' or 'populate_sysroot' or 'listtasks' may give a list of the tasks available.",
|
||||||
help="Specify the task to execute. The exact options available "
|
action = "store", dest = "cmd")
|
||||||
"depend on the metadata. Some examples might be 'compile'"
|
|
||||||
" or 'populate_sysroot' or 'listtasks' may give a list of "
|
|
||||||
"the tasks available.")
|
|
||||||
|
|
||||||
parser.add_option("-C", "--clear-stamp", action="store", dest="invalidate_stamp",
|
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified task such as 'compile' and then run the default task for the specified target(s).",
|
||||||
help="Invalidate the stamp for the specified task such as 'compile' "
|
action = "store", dest = "invalidate_stamp")
|
||||||
"and then run the default task for the specified target(s).")
|
|
||||||
|
|
||||||
parser.add_option("-r", "--read", action="append", dest="prefile", default=[],
|
parser.add_option("-r", "--read", help = "Read the specified file before bitbake.conf.",
|
||||||
help="Read the specified file before bitbake.conf.")
|
action = "append", dest = "prefile", default = [])
|
||||||
|
|
||||||
parser.add_option("-R", "--postread", action="append", dest="postfile", default=[],
|
parser.add_option("-R", "--postread", help = "Read the specified file after bitbake.conf.",
|
||||||
help="Read the specified file after bitbake.conf.")
|
action = "append", dest = "postfile", default = [])
|
||||||
|
|
||||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
parser.add_option("-v", "--verbose", help = "Output more log message data to the terminal.",
|
||||||
help="Output more log message data to the terminal.")
|
action = "store_true", dest = "verbose", default = False)
|
||||||
|
|
||||||
parser.add_option("-D", "--debug", action="count", dest="debug", default=0,
|
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||||
help="Increase the debug level. You can specify this more than once.")
|
action = "count", dest="debug", default = 0)
|
||||||
|
|
||||||
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False,
|
parser.add_option("-n", "--dry-run", help = "Don't execute, just go through the motions.",
|
||||||
help="Output less log message data to the terminal.")
|
action = "store_true", dest = "dry_run", default = False)
|
||||||
|
|
||||||
parser.add_option("-n", "--dry-run", action="store_true", dest="dry_run", default=False,
|
parser.add_option("-S", "--dump-signatures", help = "Dump out the signature construction information, with no task execution. The SIGNATURE_HANDLER parameter is passed to the handler. Two common values are none and printdiff but the handler may define more/less. none means only dump the signature, printdiff means compare the dumped signature with the cached one.",
|
||||||
help="Don't execute, just go through the motions.")
|
action = "append", dest = "dump_signatures", default = [], metavar="SIGNATURE_HANDLER")
|
||||||
|
|
||||||
parser.add_option("-S", "--dump-signatures", action="append", dest="dump_signatures",
|
parser.add_option("-p", "--parse-only", help = "Quit after parsing the BB recipes.",
|
||||||
default=[], metavar="SIGNATURE_HANDLER",
|
action = "store_true", dest = "parse_only", default = False)
|
||||||
help="Dump out the signature construction information, with no task "
|
|
||||||
"execution. The SIGNATURE_HANDLER parameter is passed to the "
|
|
||||||
"handler. Two common values are none and printdiff but the handler "
|
|
||||||
"may define more/less. none means only dump the signature, printdiff"
|
|
||||||
" means compare the dumped signature with the cached one.")
|
|
||||||
|
|
||||||
parser.add_option("-p", "--parse-only", action="store_true",
|
parser.add_option("-s", "--show-versions", help = "Show current and preferred versions of all recipes.",
|
||||||
dest="parse_only", default=False,
|
action = "store_true", dest = "show_versions", default = False)
|
||||||
help="Quit after parsing the BB recipes.")
|
|
||||||
|
|
||||||
parser.add_option("-s", "--show-versions", action="store_true",
|
parser.add_option("-e", "--environment", help = "Show the global or per-recipe environment complete with information about where variables were set/changed.",
|
||||||
dest="show_versions", default=False,
|
action = "store_true", dest = "show_environment", default = False)
|
||||||
help="Show current and preferred versions of all recipes.")
|
|
||||||
|
|
||||||
parser.add_option("-e", "--environment", action="store_true",
|
parser.add_option("-g", "--graphviz", help = "Save dependency tree information for the specified targets in the dot syntax.",
|
||||||
dest="show_environment", default=False,
|
action = "store_true", dest = "dot_graph", default = False)
|
||||||
help="Show the global or per-recipe environment complete with information"
|
|
||||||
" about where variables were set/changed.")
|
|
||||||
|
|
||||||
parser.add_option("-g", "--graphviz", 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""",
|
||||||
help="Save dependency tree information for the specified "
|
action = "append", dest = "extra_assume_provided", default = [])
|
||||||
"targets in the dot syntax.")
|
|
||||||
|
|
||||||
parser.add_option("-I", "--ignore-deps", action="append",
|
parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
|
||||||
dest="extra_assume_provided", default=[],
|
action = "append", dest = "debug_domains", default = [])
|
||||||
help="Assume these dependencies don't exist and are already provided "
|
|
||||||
"(equivalent to ASSUME_PROVIDED). Useful to make dependency "
|
|
||||||
"graphs more appealing")
|
|
||||||
|
|
||||||
parser.add_option("-l", "--log-domains", action="append", dest="debug_domains", default=[],
|
parser.add_option("-P", "--profile", help = "Profile the command and save reports.",
|
||||||
help="Show debug logging for the specified logging domains")
|
action = "store_true", dest = "profile", default = False)
|
||||||
|
|
||||||
parser.add_option("-P", "--profile", action="store_true", dest="profile", default=False,
|
env_ui = os.environ.get('BITBAKE_UI', None)
|
||||||
help="Profile the command and save reports.")
|
default_ui = env_ui or 'knotty'
|
||||||
|
# @CHOICES@ is substituted out by BitbakeHelpFormatter above
|
||||||
|
parser.add_option("-u", "--ui", help = "The user interface to use (@CHOICES@ - default %default).",
|
||||||
|
action="store", dest="ui", default=default_ui)
|
||||||
|
|
||||||
# @CHOICES@ is substituted out by BitbakeHelpFormatter above
|
# @CHOICES@ is substituted out by BitbakeHelpFormatter above
|
||||||
parser.add_option("-u", "--ui", action="store", dest="ui",
|
parser.add_option("-t", "--servertype", help = "Choose which server type to use (@CHOICES@ - default %default).",
|
||||||
default=os.environ.get('BITBAKE_UI', 'knotty'),
|
action = "store", dest = "servertype", default = "process")
|
||||||
help="The user interface to use (@CHOICES@ - default %default).")
|
|
||||||
|
|
||||||
# @CHOICES@ is substituted out by BitbakeHelpFormatter above
|
parser.add_option("", "--token", help = "Specify the connection token to be used when connecting to a remote server.",
|
||||||
parser.add_option("-t", "--servertype", action="store", dest="servertype",
|
action = "store", dest = "xmlrpctoken")
|
||||||
default=["process", "xmlrpc"]["BBSERVER" in os.environ],
|
|
||||||
help="Choose which server type to use (@CHOICES@ - default %default).")
|
|
||||||
|
|
||||||
parser.add_option("", "--token", action="store", dest="xmlrpctoken",
|
parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not.",
|
||||||
default=os.environ.get("BBTOKEN"),
|
action = "store_true", dest = "revisions_changed", default = False)
|
||||||
help="Specify the connection token to be used when connecting "
|
|
||||||
"to a remote server.")
|
|
||||||
|
|
||||||
parser.add_option("", "--revisions-changed", action="store_true",
|
parser.add_option("", "--server-only", help = "Run bitbake without a UI, only starting a server (cooker) process.",
|
||||||
dest="revisions_changed", default=False,
|
action = "store_true", dest = "server_only", default = False)
|
||||||
help="Set the exit code depending on whether upstream floating "
|
|
||||||
"revisions have changed or not.")
|
|
||||||
|
|
||||||
parser.add_option("", "--server-only", action="store_true",
|
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to.",
|
||||||
dest="server_only", default=False,
|
action = "store", dest = "bind", default = False)
|
||||||
help="Run bitbake without a UI, only starting a server "
|
|
||||||
"(cooker) process.")
|
|
||||||
|
|
||||||
parser.add_option("", "--foreground", action="store_true",
|
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks. sstate will be ignored and everything needed, built.",
|
||||||
help="Run bitbake server in foreground.")
|
action = "store_true", dest = "nosetscene", default = False)
|
||||||
|
|
||||||
parser.add_option("-B", "--bind", action="store", dest="bind", default=False,
|
parser.add_option("", "--remote-server", help = "Connect to the specified server.",
|
||||||
help="The name/address for the bitbake server to bind to.")
|
action = "store", dest = "remote_server", default = False)
|
||||||
|
|
||||||
parser.add_option("-T", "--idle-timeout", type=int,
|
parser.add_option("-m", "--kill-server", help = "Terminate the remote server.",
|
||||||
default=int(os.environ.get("BBTIMEOUT", "0")),
|
action = "store_true", dest = "kill_server", default = False)
|
||||||
help="Set timeout to unload bitbake server due to inactivity")
|
|
||||||
|
|
||||||
parser.add_option("", "--no-setscene", action="store_true",
|
parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
|
||||||
dest="nosetscene", default=False,
|
action = "store_true", dest = "observe_only", default = False)
|
||||||
help="Do not run any setscene tasks. sstate will be ignored and "
|
|
||||||
"everything needed, built.")
|
|
||||||
|
|
||||||
parser.add_option("", "--setscene-only", action="store_true",
|
parser.add_option("", "--status-only", help = "Check the status of the remote bitbake server.",
|
||||||
dest="setsceneonly", default=False,
|
action = "store_true", dest = "status_only", default = False)
|
||||||
help="Only run setscene tasks, don't run any real tasks.")
|
|
||||||
|
|
||||||
parser.add_option("", "--remote-server", action="store", dest="remote_server",
|
parser.add_option("-w", "--write-log", help = "Writes the event log of the build to a bitbake event json file. Use '' (empty string) to assign the name automatically.",
|
||||||
default=os.environ.get("BBSERVER"),
|
action = "store", dest = "writeeventlog")
|
||||||
help="Connect to the specified server.")
|
|
||||||
|
|
||||||
parser.add_option("-m", "--kill-server", action="store_true",
|
|
||||||
dest="kill_server", default=False,
|
|
||||||
help="Terminate the remote server.")
|
|
||||||
|
|
||||||
parser.add_option("", "--observe-only", action="store_true",
|
|
||||||
dest="observe_only", default=False,
|
|
||||||
help="Connect to a server as an observing-only client.")
|
|
||||||
|
|
||||||
parser.add_option("", "--status-only", action="store_true",
|
|
||||||
dest="status_only", default=False,
|
|
||||||
help="Check the status of the remote bitbake server.")
|
|
||||||
|
|
||||||
parser.add_option("-w", "--write-log", action="store", dest="writeeventlog",
|
|
||||||
default=os.environ.get("BBEVENTLOG"),
|
|
||||||
help="Writes the event log of the build to a bitbake event json file. "
|
|
||||||
"Use '' (empty string) to assign the name automatically.")
|
|
||||||
|
|
||||||
options, targets = parser.parse_args(argv)
|
options, targets = parser.parse_args(argv)
|
||||||
|
|
||||||
if options.quiet and options.verbose:
|
# some environmental variables set also configuration options
|
||||||
parser.error("options --quiet and --verbose are mutually exclusive")
|
if "BBSERVER" in os.environ:
|
||||||
|
options.servertype = "xmlrpc"
|
||||||
|
options.remote_server = os.environ["BBSERVER"]
|
||||||
|
|
||||||
if options.quiet and options.debug:
|
if "BBTOKEN" in os.environ:
|
||||||
parser.error("options --quiet and --debug are mutually exclusive")
|
options.xmlrpctoken = os.environ["BBTOKEN"]
|
||||||
|
|
||||||
# use configuration files from environment variables
|
if "BBEVENTLOG" is os.environ:
|
||||||
if "BBPRECONF" in os.environ:
|
options.writeeventlog = os.environ["BBEVENTLOG"]
|
||||||
options.prefile.append(os.environ["BBPRECONF"])
|
|
||||||
|
|
||||||
if "BBPOSTCONF" in os.environ:
|
|
||||||
options.postfile.append(os.environ["BBPOSTCONF"])
|
|
||||||
|
|
||||||
# fill in proper log name if not supplied
|
# fill in proper log name if not supplied
|
||||||
if options.writeeventlog is not None and len(options.writeeventlog) == 0:
|
if options.writeeventlog is not None and len(options.writeeventlog) == 0:
|
||||||
from datetime import datetime
|
import datetime
|
||||||
eventlog = "bitbake_eventlog_%s.json" % datetime.now().strftime("%Y%m%d%H%M%S")
|
options.writeeventlog = "bitbake_eventlog_%s.json" % datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
options.writeeventlog = eventlog
|
|
||||||
|
|
||||||
# if BBSERVER says to autodetect, let's do that
|
# if BBSERVER says to autodetect, let's do that
|
||||||
if options.remote_server:
|
if options.remote_server:
|
||||||
port = -1
|
[host, port] = options.remote_server.split(":", 2)
|
||||||
if options.remote_server != 'autostart':
|
port = int(port)
|
||||||
host, port = options.remote_server.split(":", 2)
|
|
||||||
port = int(port)
|
|
||||||
# use automatic port if port set to -1, means read it from
|
# use automatic port if port set to -1, means read it from
|
||||||
# the bitbake.lock file; this is a bit tricky, but we always expect
|
# the bitbake.lock file; this is a bit tricky, but we always expect
|
||||||
# to be in the base of the build directory if we need to have a
|
# to be in the base of the build directory if we need to have a
|
||||||
@@ -330,22 +272,19 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
|||||||
lf.close()
|
lf.close()
|
||||||
options.remote_server = remotedef
|
options.remote_server = remotedef
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if options.remote_server != 'autostart':
|
raise BBMainException("Failed to read bitbake.lock (%s), invalid port" % str(e))
|
||||||
raise BBMainException("Failed to read bitbake.lock (%s), invalid port" % str(e))
|
|
||||||
|
|
||||||
return options, targets[1:]
|
return options, targets[1:]
|
||||||
|
|
||||||
|
|
||||||
def start_server(servermodule, configParams, configuration, features):
|
def start_server(servermodule, configParams, configuration, features):
|
||||||
server = servermodule.BitBakeServer()
|
server = servermodule.BitBakeServer()
|
||||||
single_use = not configParams.server_only and os.getenv('BBSERVER') != 'autostart'
|
|
||||||
if configParams.bind:
|
if configParams.bind:
|
||||||
(host, port) = configParams.bind.split(':')
|
(host, port) = configParams.bind.split(':')
|
||||||
server.initServer((host, int(port)), single_use=single_use,
|
server.initServer((host, int(port)))
|
||||||
idle_timeout=configParams.idle_timeout)
|
configuration.interface = [ server.serverImpl.host, server.serverImpl.port ]
|
||||||
configuration.interface = [server.serverImpl.host, server.serverImpl.port]
|
|
||||||
else:
|
else:
|
||||||
server.initServer(single_use=single_use)
|
server.initServer()
|
||||||
configuration.interface = []
|
configuration.interface = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -356,17 +295,20 @@ def start_server(servermodule, configParams, configuration, features):
|
|||||||
server.addcooker(cooker)
|
server.addcooker(cooker)
|
||||||
server.saveConnectionDetails()
|
server.saveConnectionDetails()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
exc_info = sys.exc_info()
|
||||||
while hasattr(server, "event_queue"):
|
while hasattr(server, "event_queue"):
|
||||||
import queue
|
try:
|
||||||
|
import queue
|
||||||
|
except ImportError:
|
||||||
|
import Queue as queue
|
||||||
try:
|
try:
|
||||||
event = server.event_queue.get(block=False)
|
event = server.event_queue.get(block=False)
|
||||||
except (queue.Empty, IOError):
|
except (queue.Empty, IOError):
|
||||||
break
|
break
|
||||||
if isinstance(event, logging.LogRecord):
|
if isinstance(event, logging.LogRecord):
|
||||||
logger.handle(event)
|
logger.handle(event)
|
||||||
raise
|
raise exc_info[1], None, exc_info[2]
|
||||||
if not configParams.foreground:
|
server.detach()
|
||||||
server.detach()
|
|
||||||
cooker.lock.close()
|
cooker.lock.close()
|
||||||
return server
|
return server
|
||||||
|
|
||||||
@@ -382,10 +324,7 @@ def bitbake_main(configParams, configuration):
|
|||||||
# updates to log files for use with tail
|
# updates to log files for use with tail
|
||||||
try:
|
try:
|
||||||
if sys.stdout.name == '<stdout>':
|
if sys.stdout.name == '<stdout>':
|
||||||
# Reopen with O_SYNC (unbuffered)
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||||
fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
|
|
||||||
fl |= os.O_SYNC
|
|
||||||
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -402,21 +341,10 @@ def bitbake_main(configParams, configuration):
|
|||||||
if not configParams.bind:
|
if not configParams.bind:
|
||||||
raise BBMainException("FATAL: The '--server-only' option requires a name/address "
|
raise BBMainException("FATAL: The '--server-only' option requires a name/address "
|
||||||
"to bind to with the -B option.\n")
|
"to bind to with the -B option.\n")
|
||||||
else:
|
|
||||||
try:
|
|
||||||
#Checking that the port is a number
|
|
||||||
int(configParams.bind.split(":")[1])
|
|
||||||
except (ValueError,IndexError):
|
|
||||||
raise BBMainException(
|
|
||||||
"FATAL: Malformed host:port bind parameter")
|
|
||||||
if configParams.remote_server:
|
if configParams.remote_server:
|
||||||
raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" %
|
raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" %
|
||||||
("the BBSERVER environment variable" if "BBSERVER" in os.environ \
|
("the BBSERVER environment variable" if "BBSERVER" in os.environ \
|
||||||
else "the '--remote-server' option"))
|
else "the '--remote-server' option" ))
|
||||||
|
|
||||||
elif configParams.foreground:
|
|
||||||
raise BBMainException("FATAL: The '--foreground' option can only be used "
|
|
||||||
"with --server-only.\n")
|
|
||||||
|
|
||||||
if configParams.bind and configParams.servertype != "xmlrpc":
|
if configParams.bind and configParams.servertype != "xmlrpc":
|
||||||
raise BBMainException("FATAL: If '-B' or '--bind' is defined, we must "
|
raise BBMainException("FATAL: If '-B' or '--bind' is defined, we must "
|
||||||
@@ -431,8 +359,7 @@ def bitbake_main(configParams, configuration):
|
|||||||
"connecting to a server.\n")
|
"connecting to a server.\n")
|
||||||
|
|
||||||
if configParams.kill_server and not configParams.remote_server:
|
if configParams.kill_server and not configParams.remote_server:
|
||||||
raise BBMainException("FATAL: '--kill-server' can only be used to "
|
raise BBMainException("FATAL: '--kill-server' can only be used to terminate a remote server")
|
||||||
"terminate a remote server")
|
|
||||||
|
|
||||||
if "BBDEBUG" in os.environ:
|
if "BBDEBUG" in os.environ:
|
||||||
level = int(os.environ["BBDEBUG"])
|
level = int(os.environ["BBDEBUG"])
|
||||||
@@ -440,7 +367,7 @@ def bitbake_main(configParams, configuration):
|
|||||||
configuration.debug = level
|
configuration.debug = level
|
||||||
|
|
||||||
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
|
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
|
||||||
configuration.debug_domains)
|
configuration.debug_domains)
|
||||||
|
|
||||||
# Ensure logging messages get sent to the UI as events
|
# Ensure logging messages get sent to the UI as events
|
||||||
handler = bb.event.LogHandler()
|
handler = bb.event.LogHandler()
|
||||||
@@ -468,17 +395,8 @@ def bitbake_main(configParams, configuration):
|
|||||||
server = start_server(servermodule, configParams, configuration, featureset)
|
server = start_server(servermodule, configParams, configuration, featureset)
|
||||||
bb.event.ui_queue = []
|
bb.event.ui_queue = []
|
||||||
else:
|
else:
|
||||||
if os.getenv('BBSERVER') == 'autostart':
|
|
||||||
if configParams.remote_server == 'autostart' or \
|
|
||||||
not servermodule.check_connection(configParams.remote_server, timeout=2):
|
|
||||||
configParams.bind = 'localhost:0'
|
|
||||||
srv = start_server(servermodule, configParams, configuration, featureset)
|
|
||||||
configParams.remote_server = '%s:%d' % tuple(configuration.interface)
|
|
||||||
bb.event.ui_queue = []
|
|
||||||
|
|
||||||
# we start a stub server that is actually a XMLRPClient that connects to a real server
|
# we start a stub server that is actually a XMLRPClient that connects to a real server
|
||||||
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only,
|
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only, configParams.xmlrpctoken)
|
||||||
configParams.xmlrpctoken)
|
|
||||||
server.saveConnectionDetails(configParams.remote_server)
|
server.saveConnectionDetails(configParams.remote_server)
|
||||||
|
|
||||||
|
|
||||||
@@ -488,13 +406,6 @@ def bitbake_main(configParams, configuration):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
bb.fatal("Could not connect to server %s: %s" % (configParams.remote_server, str(e)))
|
bb.fatal("Could not connect to server %s: %s" % (configParams.remote_server, str(e)))
|
||||||
|
|
||||||
if configParams.kill_server:
|
|
||||||
server_connection.connection.terminateServer()
|
|
||||||
bb.event.ui_queue = []
|
|
||||||
return 0
|
|
||||||
|
|
||||||
server_connection.setupEventQueue()
|
|
||||||
|
|
||||||
# Restore the environment in case the UI needs it
|
# Restore the environment in case the UI needs it
|
||||||
for k in cleanedvars:
|
for k in cleanedvars:
|
||||||
os.environ[k] = cleanedvars[k]
|
os.environ[k] = cleanedvars[k]
|
||||||
@@ -506,17 +417,18 @@ def bitbake_main(configParams, configuration):
|
|||||||
server_connection.terminate()
|
server_connection.terminate()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if configParams.kill_server:
|
||||||
|
server_connection.connection.terminateServer()
|
||||||
|
bb.event.ui_queue = []
|
||||||
|
return 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return ui_module.main(server_connection.connection, server_connection.events,
|
return ui_module.main(server_connection.connection, server_connection.events, configParams)
|
||||||
configParams)
|
|
||||||
finally:
|
finally:
|
||||||
bb.event.ui_queue = []
|
bb.event.ui_queue = []
|
||||||
server_connection.terminate()
|
server_connection.terminate()
|
||||||
else:
|
else:
|
||||||
print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host,
|
print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
|
||||||
server.serverImpl.port))
|
|
||||||
if configParams.foreground:
|
|
||||||
server.serverImpl.serve_forever()
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -19,22 +19,11 @@
|
|||||||
|
|
||||||
from bb.utils import better_compile, better_exec
|
from bb.utils import better_compile, better_exec
|
||||||
|
|
||||||
def insert_method(modulename, code, fn, lineno):
|
def insert_method(modulename, code, fn):
|
||||||
"""
|
"""
|
||||||
Add code of a module should be added. The methods
|
Add code of a module should be added. The methods
|
||||||
will be simply added, no checking will be done
|
will be simply added, no checking will be done
|
||||||
"""
|
"""
|
||||||
comp = better_compile(code, modulename, fn, lineno=lineno)
|
comp = better_compile(code, modulename, fn )
|
||||||
better_exec(comp, None, code, fn)
|
better_exec(comp, None, code, fn)
|
||||||
|
|
||||||
compilecache = {}
|
|
||||||
|
|
||||||
def compile_cache(code):
|
|
||||||
h = hash(code)
|
|
||||||
if h in compilecache:
|
|
||||||
return compilecache[h]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def compile_cache_add(code, compileobj):
|
|
||||||
h = hash(code)
|
|
||||||
compilecache[h] = compileobj
|
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ class diskMonitor:
|
|||||||
if minSpace and freeSpace < minSpace:
|
if minSpace and freeSpace < minSpace:
|
||||||
# Always show warning, the self.checked would always be False if the action is WARN
|
# Always show warning, the self.checked would always be False if the action is WARN
|
||||||
if self.preFreeS[k] == 0 or self.preFreeS[k] - freeSpace > self.spaceInterval and not self.checked[k]:
|
if self.preFreeS[k] == 0 or self.preFreeS[k] - freeSpace > self.spaceInterval and not self.checked[k]:
|
||||||
logger.warning("The free space of %s (%s) is running low (%.3fGB left)" % \
|
logger.warn("The free space of %s (%s) is running low (%.3fGB left)" % \
|
||||||
(path, dev, freeSpace / 1024 / 1024 / 1024.0))
|
(path, dev, freeSpace / 1024 / 1024 / 1024.0))
|
||||||
self.preFreeS[k] = freeSpace
|
self.preFreeS[k] = freeSpace
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ class diskMonitor:
|
|||||||
continue
|
continue
|
||||||
# Always show warning, the self.checked would always be False if the action is WARN
|
# Always show warning, the self.checked would always be False if the action is WARN
|
||||||
if self.preFreeI[k] == 0 or self.preFreeI[k] - freeInode > self.inodeInterval and not self.checked[k]:
|
if self.preFreeI[k] == 0 or self.preFreeI[k] - freeInode > self.inodeInterval and not self.checked[k]:
|
||||||
logger.warning("The free inode of %s (%s) is running low (%.3fK left)" % \
|
logger.warn("The free inode of %s (%s) is running low (%.3fK left)" % \
|
||||||
(path, dev, freeInode / 1024.0))
|
(path, dev, freeInode / 1024.0))
|
||||||
self.preFreeI[k] = freeInode
|
self.preFreeI[k] = freeInode
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class BBLogFormatter(logging.Formatter):
|
|||||||
}
|
}
|
||||||
|
|
||||||
color_enabled = False
|
color_enabled = False
|
||||||
BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
|
BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(29,38)
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
DEBUG3 : CYAN,
|
DEBUG3 : CYAN,
|
||||||
@@ -90,9 +90,8 @@ class BBLogFormatter(logging.Formatter):
|
|||||||
if self.color_enabled:
|
if self.color_enabled:
|
||||||
record = self.colorize(record)
|
record = self.colorize(record)
|
||||||
msg = logging.Formatter.format(self, record)
|
msg = logging.Formatter.format(self, record)
|
||||||
if hasattr(record, 'bb_exc_formatted'):
|
|
||||||
msg += '\n' + ''.join(record.bb_exc_formatted)
|
if hasattr(record, 'bb_exc_info'):
|
||||||
elif hasattr(record, 'bb_exc_info'):
|
|
||||||
etype, value, tb = record.bb_exc_info
|
etype, value, tb = record.bb_exc_info
|
||||||
formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
|
formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
|
||||||
msg += '\n' + ''.join(formatted)
|
msg += '\n' + ''.join(formatted)
|
||||||
@@ -182,12 +181,9 @@ def constructLogOptions():
|
|||||||
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
|
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
|
||||||
return level, debug_domains
|
return level, debug_domains
|
||||||
|
|
||||||
def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
|
def addDefaultlogFilter(handler, cls = BBLogFilter):
|
||||||
level, debug_domains = constructLogOptions()
|
level, debug_domains = constructLogOptions()
|
||||||
|
|
||||||
if forcelevel is not None:
|
|
||||||
level = forcelevel
|
|
||||||
|
|
||||||
cls(handler, level, debug_domains)
|
cls(handler, level, debug_domains)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -161,8 +161,8 @@ def vars_from_file(mypkg, d):
|
|||||||
def get_file_depends(d):
|
def get_file_depends(d):
|
||||||
'''Return the dependent files'''
|
'''Return the dependent files'''
|
||||||
dep_files = []
|
dep_files = []
|
||||||
depends = d.getVar('__base_depends', False) or []
|
depends = d.getVar('__base_depends', True) or []
|
||||||
depends = depends + (d.getVar('__depends', False) or [])
|
depends = depends + (d.getVar('__depends', True) or [])
|
||||||
for (fn, _) in depends:
|
for (fn, _) in depends:
|
||||||
dep_files.append(os.path.abspath(fn))
|
dep_files.append(os.path.abspath(fn))
|
||||||
return " ".join(dep_files)
|
return " ".join(dep_files)
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from future_builtins import filter
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
import logging
|
import logging
|
||||||
@@ -69,33 +70,6 @@ class ExportNode(AstNode):
|
|||||||
def eval(self, data):
|
def eval(self, data):
|
||||||
data.setVarFlag(self.var, "export", 1, op = 'exported')
|
data.setVarFlag(self.var, "export", 1, op = 'exported')
|
||||||
|
|
||||||
class UnsetNode(AstNode):
|
|
||||||
def __init__(self, filename, lineno, var):
|
|
||||||
AstNode.__init__(self, filename, lineno)
|
|
||||||
self.var = var
|
|
||||||
|
|
||||||
def eval(self, data):
|
|
||||||
loginfo = {
|
|
||||||
'variable': self.var,
|
|
||||||
'file': self.filename,
|
|
||||||
'line': self.lineno,
|
|
||||||
}
|
|
||||||
data.delVar(self.var,**loginfo)
|
|
||||||
|
|
||||||
class UnsetFlagNode(AstNode):
|
|
||||||
def __init__(self, filename, lineno, var, flag):
|
|
||||||
AstNode.__init__(self, filename, lineno)
|
|
||||||
self.var = var
|
|
||||||
self.flag = flag
|
|
||||||
|
|
||||||
def eval(self, data):
|
|
||||||
loginfo = {
|
|
||||||
'variable': self.var,
|
|
||||||
'file': self.filename,
|
|
||||||
'line': self.lineno,
|
|
||||||
}
|
|
||||||
data.delVarFlag(self.var, self.flag, **loginfo)
|
|
||||||
|
|
||||||
class DataNode(AstNode):
|
class DataNode(AstNode):
|
||||||
"""
|
"""
|
||||||
Various data related updates. For the sake of sanity
|
Various data related updates. For the sake of sanity
|
||||||
@@ -109,7 +83,7 @@ class DataNode(AstNode):
|
|||||||
|
|
||||||
def getFunc(self, key, data):
|
def getFunc(self, key, data):
|
||||||
if 'flag' in self.groupd and self.groupd['flag'] != None:
|
if 'flag' in self.groupd and self.groupd['flag'] != None:
|
||||||
return data.getVarFlag(key, self.groupd['flag'], expand=False, noweakdefault=True)
|
return data.getVarFlag(key, self.groupd['flag'], noweakdefault=True)
|
||||||
else:
|
else:
|
||||||
return data.getVar(key, False, noweakdefault=True, parsing=True)
|
return data.getVar(key, False, noweakdefault=True, parsing=True)
|
||||||
|
|
||||||
@@ -165,39 +139,26 @@ class DataNode(AstNode):
|
|||||||
data.setVar(key, val, parsing=True, **loginfo)
|
data.setVar(key, val, parsing=True, **loginfo)
|
||||||
|
|
||||||
class MethodNode(AstNode):
|
class MethodNode(AstNode):
|
||||||
tr_tbl = str.maketrans('/.+-@%&', '_______')
|
tr_tbl = string.maketrans('/.+-@%&', '_______')
|
||||||
|
|
||||||
def __init__(self, filename, lineno, func_name, body, python, fakeroot):
|
def __init__(self, filename, lineno, func_name, body):
|
||||||
AstNode.__init__(self, filename, lineno)
|
AstNode.__init__(self, filename, lineno)
|
||||||
self.func_name = func_name
|
self.func_name = func_name
|
||||||
self.body = body
|
self.body = body
|
||||||
self.python = python
|
|
||||||
self.fakeroot = fakeroot
|
|
||||||
|
|
||||||
def eval(self, data):
|
def eval(self, data):
|
||||||
text = '\n'.join(self.body)
|
text = '\n'.join(self.body)
|
||||||
funcname = self.func_name
|
|
||||||
if self.func_name == "__anonymous":
|
if self.func_name == "__anonymous":
|
||||||
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(MethodNode.tr_tbl)))
|
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(MethodNode.tr_tbl)))
|
||||||
self.python = True
|
|
||||||
text = "def %s(d):\n" % (funcname) + text
|
text = "def %s(d):\n" % (funcname) + text
|
||||||
bb.methodpool.insert_method(funcname, text, self.filename, self.lineno - len(self.body))
|
bb.methodpool.insert_method(funcname, text, self.filename)
|
||||||
anonfuncs = data.getVar('__BBANONFUNCS', False) or []
|
anonfuncs = data.getVar('__BBANONFUNCS', False) or []
|
||||||
anonfuncs.append(funcname)
|
anonfuncs.append(funcname)
|
||||||
data.setVar('__BBANONFUNCS', anonfuncs)
|
data.setVar('__BBANONFUNCS', anonfuncs)
|
||||||
if data.getVar(funcname, False):
|
data.setVar(funcname, text, parsing=True)
|
||||||
# clean up old version of this piece of metadata, as its
|
else:
|
||||||
# flags could cause problems
|
data.setVarFlag(self.func_name, "func", 1)
|
||||||
data.delVarFlag(funcname, 'python')
|
data.setVar(self.func_name, text, parsing=True)
|
||||||
data.delVarFlag(funcname, 'fakeroot')
|
|
||||||
if self.python:
|
|
||||||
data.setVarFlag(funcname, "python", "1")
|
|
||||||
if self.fakeroot:
|
|
||||||
data.setVarFlag(funcname, "fakeroot", "1")
|
|
||||||
data.setVarFlag(funcname, "func", 1)
|
|
||||||
data.setVar(funcname, text, parsing=True)
|
|
||||||
data.setVarFlag(funcname, 'filename', self.filename)
|
|
||||||
data.setVarFlag(funcname, 'lineno', str(self.lineno - len(self.body)))
|
|
||||||
|
|
||||||
class PythonMethodNode(AstNode):
|
class PythonMethodNode(AstNode):
|
||||||
def __init__(self, filename, lineno, function, modulename, body):
|
def __init__(self, filename, lineno, function, modulename, body):
|
||||||
@@ -211,12 +172,31 @@ class PythonMethodNode(AstNode):
|
|||||||
# 'this' file. This means we will not parse methods from
|
# 'this' file. This means we will not parse methods from
|
||||||
# bb classes twice
|
# bb classes twice
|
||||||
text = '\n'.join(self.body)
|
text = '\n'.join(self.body)
|
||||||
bb.methodpool.insert_method(self.modulename, text, self.filename, self.lineno - len(self.body) - 1)
|
bb.methodpool.insert_method(self.modulename, text, self.filename)
|
||||||
data.setVarFlag(self.function, "func", 1)
|
data.setVarFlag(self.function, "func", 1)
|
||||||
data.setVarFlag(self.function, "python", 1)
|
data.setVarFlag(self.function, "python", 1)
|
||||||
data.setVar(self.function, text, parsing=True)
|
data.setVar(self.function, text, parsing=True)
|
||||||
data.setVarFlag(self.function, 'filename', self.filename)
|
|
||||||
data.setVarFlag(self.function, 'lineno', str(self.lineno - len(self.body) - 1))
|
class MethodFlagsNode(AstNode):
|
||||||
|
def __init__(self, filename, lineno, key, m):
|
||||||
|
AstNode.__init__(self, filename, lineno)
|
||||||
|
self.key = key
|
||||||
|
self.m = m
|
||||||
|
|
||||||
|
def eval(self, data):
|
||||||
|
if data.getVar(self.key, False):
|
||||||
|
# clean up old version of this piece of metadata, as its
|
||||||
|
# flags could cause problems
|
||||||
|
data.setVarFlag(self.key, 'python', None)
|
||||||
|
data.setVarFlag(self.key, 'fakeroot', None)
|
||||||
|
if self.m.group("py") is not None:
|
||||||
|
data.setVarFlag(self.key, "python", "1")
|
||||||
|
else:
|
||||||
|
data.delVarFlag(self.key, "python")
|
||||||
|
if self.m.group("fr") is not None:
|
||||||
|
data.setVarFlag(self.key, "fakeroot", "1")
|
||||||
|
else:
|
||||||
|
data.delVarFlag(self.key, "fakeroot")
|
||||||
|
|
||||||
class ExportFuncsNode(AstNode):
|
class ExportFuncsNode(AstNode):
|
||||||
def __init__(self, filename, lineno, fns, classname):
|
def __init__(self, filename, lineno, fns, classname):
|
||||||
@@ -229,7 +209,7 @@ class ExportFuncsNode(AstNode):
|
|||||||
for func in self.n:
|
for func in self.n:
|
||||||
calledfunc = self.classname + "_" + func
|
calledfunc = self.classname + "_" + func
|
||||||
|
|
||||||
if data.getVar(func, False) and not data.getVarFlag(func, 'export_func', False):
|
if data.getVar(func, False) and not data.getVarFlag(func, 'export_func'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data.getVar(func, False):
|
if data.getVar(func, False):
|
||||||
@@ -237,15 +217,13 @@ class ExportFuncsNode(AstNode):
|
|||||||
data.setVarFlag(func, 'func', None)
|
data.setVarFlag(func, 'func', None)
|
||||||
|
|
||||||
for flag in [ "func", "python" ]:
|
for flag in [ "func", "python" ]:
|
||||||
if data.getVarFlag(calledfunc, flag, False):
|
if data.getVarFlag(calledfunc, flag):
|
||||||
data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag, False))
|
data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag))
|
||||||
for flag in [ "dirs" ]:
|
for flag in [ "dirs" ]:
|
||||||
if data.getVarFlag(func, flag, False):
|
if data.getVarFlag(func, flag):
|
||||||
data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag, False))
|
data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag))
|
||||||
data.setVarFlag(func, "filename", "autogenerated")
|
|
||||||
data.setVarFlag(func, "lineno", 1)
|
|
||||||
|
|
||||||
if data.getVarFlag(calledfunc, "python", False):
|
if data.getVarFlag(calledfunc, "python"):
|
||||||
data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n", parsing=True)
|
data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n", parsing=True)
|
||||||
else:
|
else:
|
||||||
if "-" in self.classname:
|
if "-" in self.classname:
|
||||||
@@ -297,21 +275,18 @@ def handleInclude(statements, filename, lineno, m, force):
|
|||||||
def handleExport(statements, filename, lineno, m):
|
def handleExport(statements, filename, lineno, m):
|
||||||
statements.append(ExportNode(filename, lineno, m.group(1)))
|
statements.append(ExportNode(filename, lineno, m.group(1)))
|
||||||
|
|
||||||
def handleUnset(statements, filename, lineno, m):
|
|
||||||
statements.append(UnsetNode(filename, lineno, m.group(1)))
|
|
||||||
|
|
||||||
def handleUnsetFlag(statements, filename, lineno, m):
|
|
||||||
statements.append(UnsetFlagNode(filename, lineno, m.group(1), m.group(2)))
|
|
||||||
|
|
||||||
def handleData(statements, filename, lineno, groupd):
|
def handleData(statements, filename, lineno, groupd):
|
||||||
statements.append(DataNode(filename, lineno, groupd))
|
statements.append(DataNode(filename, lineno, groupd))
|
||||||
|
|
||||||
def handleMethod(statements, filename, lineno, func_name, body, python, fakeroot):
|
def handleMethod(statements, filename, lineno, func_name, body):
|
||||||
statements.append(MethodNode(filename, lineno, func_name, body, python, fakeroot))
|
statements.append(MethodNode(filename, lineno, func_name, body))
|
||||||
|
|
||||||
def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
|
def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
|
||||||
statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
|
statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
|
||||||
|
|
||||||
|
def handleMethodFlags(statements, filename, lineno, key, m):
|
||||||
|
statements.append(MethodFlagsNode(filename, lineno, key, m))
|
||||||
|
|
||||||
def handleExportFuncs(statements, filename, lineno, m, classname):
|
def handleExportFuncs(statements, filename, lineno, m, classname):
|
||||||
statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
|
statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
|
||||||
|
|
||||||
@@ -339,15 +314,10 @@ def handleInherit(statements, filename, lineno, m):
|
|||||||
statements.append(InheritNode(filename, lineno, classes))
|
statements.append(InheritNode(filename, lineno, classes))
|
||||||
|
|
||||||
def finalize(fn, d, variant = None):
|
def finalize(fn, d, variant = None):
|
||||||
saved_handlers = bb.event.get_handlers().copy()
|
all_handlers = {}
|
||||||
|
|
||||||
for var in d.getVar('__BBHANDLERS', False) or []:
|
for var in d.getVar('__BBHANDLERS', False) or []:
|
||||||
# try to add the handler
|
# try to add the handler
|
||||||
handlerfn = d.getVarFlag(var, "filename", False)
|
bb.event.register(var, d.getVar(var, False), (d.getVarFlag(var, "eventmask", True) or "").split())
|
||||||
if not handlerfn:
|
|
||||||
bb.fatal("Undefined event handler function '%s'" % var)
|
|
||||||
handlerln = int(d.getVarFlag(var, "lineno", False))
|
|
||||||
bb.event.register(var, d.getVar(var, False), (d.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
|
|
||||||
|
|
||||||
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
|
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
|
||||||
|
|
||||||
@@ -367,7 +337,6 @@ def finalize(fn, d, variant = None):
|
|||||||
d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
|
d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
|
||||||
|
|
||||||
bb.event.fire(bb.event.RecipeParsed(fn), d)
|
bb.event.fire(bb.event.RecipeParsed(fn), d)
|
||||||
bb.event.set_handlers(saved_handlers)
|
|
||||||
|
|
||||||
def _create_variants(datastores, names, function, onlyfinalise):
|
def _create_variants(datastores, names, function, onlyfinalise):
|
||||||
def create_variant(name, orig_d, arg = None):
|
def create_variant(name, orig_d, arg = None):
|
||||||
@@ -377,17 +346,17 @@ def _create_variants(datastores, names, function, onlyfinalise):
|
|||||||
function(arg or name, new_d)
|
function(arg or name, new_d)
|
||||||
datastores[name] = new_d
|
datastores[name] = new_d
|
||||||
|
|
||||||
for variant in list(datastores.keys()):
|
for variant, variant_d in datastores.items():
|
||||||
for name in names:
|
for name in names:
|
||||||
if not variant:
|
if not variant:
|
||||||
# Based on main recipe
|
# Based on main recipe
|
||||||
create_variant(name, datastores[""])
|
create_variant(name, variant_d)
|
||||||
else:
|
else:
|
||||||
create_variant("%s-%s" % (variant, name), datastores[variant], name)
|
create_variant("%s-%s" % (variant, name), variant_d, name)
|
||||||
|
|
||||||
def _expand_versions(versions):
|
def _expand_versions(versions):
|
||||||
def expand_one(version, start, end):
|
def expand_one(version, start, end):
|
||||||
for i in range(start, end + 1):
|
for i in xrange(start, end + 1):
|
||||||
ver = _bbversions_re.sub(str(i), version, 1)
|
ver = _bbversions_re.sub(str(i), version, 1)
|
||||||
yield ver
|
yield ver
|
||||||
|
|
||||||
@@ -496,13 +465,17 @@ def multi_finalize(fn, d):
|
|||||||
safe_d.setVar("BBCLASSEXTEND", extended)
|
safe_d.setVar("BBCLASSEXTEND", extended)
|
||||||
_create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
|
_create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
|
||||||
|
|
||||||
for variant in datastores.keys():
|
for variant, variant_d in datastores.iteritems():
|
||||||
if variant:
|
if variant:
|
||||||
try:
|
try:
|
||||||
if not onlyfinalise or variant in onlyfinalise:
|
if not onlyfinalise or variant in onlyfinalise:
|
||||||
finalize(fn, datastores[variant], variant)
|
finalize(fn, variant_d, variant)
|
||||||
except bb.parse.SkipRecipe as e:
|
except bb.parse.SkipRecipe as e:
|
||||||
datastores[variant].setVar("__SKIPPED", e.args[0])
|
variant_d.setVar("__SKIPPED", e.args[0])
|
||||||
|
|
||||||
|
if len(datastores) > 1:
|
||||||
|
variants = filter(None, datastores.iterkeys())
|
||||||
|
safe_d.setVar("__VARIANTS", " ".join(variants))
|
||||||
|
|
||||||
datastores[""] = d
|
datastores[""] = d
|
||||||
return datastores
|
return datastores
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
import re, bb, os
|
import re, bb, os
|
||||||
import logging
|
import logging
|
||||||
import bb.build, bb.utils
|
import bb.build, bb.utils
|
||||||
@@ -47,6 +47,7 @@ __addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
|
|||||||
__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
|
__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
|
||||||
__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
|
__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
|
||||||
|
|
||||||
|
|
||||||
__infunc__ = []
|
__infunc__ = []
|
||||||
__inpython__ = False
|
__inpython__ = False
|
||||||
__body__ = []
|
__body__ = []
|
||||||
@@ -54,6 +55,15 @@ __classname__ = ""
|
|||||||
|
|
||||||
cached_statements = {}
|
cached_statements = {}
|
||||||
|
|
||||||
|
# We need to indicate EOF to the feeder. This code is so messy that
|
||||||
|
# factoring it out to a close_parse_file method is out of question.
|
||||||
|
# We will use the IN_PYTHON_EOF as an indicator to just close the method
|
||||||
|
#
|
||||||
|
# The two parts using it are tightly integrated anyway
|
||||||
|
IN_PYTHON_EOF = -9999999999999
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def supports(fn, d):
|
def supports(fn, d):
|
||||||
"""Return True if fn has a supported extension"""
|
"""Return True if fn has a supported extension"""
|
||||||
return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
|
return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
|
||||||
@@ -100,7 +110,7 @@ def get_statements(filename, absolute_filename, base_name):
|
|||||||
file.close()
|
file.close()
|
||||||
if __inpython__:
|
if __inpython__:
|
||||||
# add a blank line to close out any python definition
|
# add a blank line to close out any python definition
|
||||||
feeder(lineno, "", filename, base_name, statements, eof=True)
|
feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
|
||||||
|
|
||||||
if filename.endswith(".bbclass") or filename.endswith(".inc"):
|
if filename.endswith(".bbclass") or filename.endswith(".inc"):
|
||||||
cached_statements[absolute_filename] = statements
|
cached_statements[absolute_filename] = statements
|
||||||
@@ -161,12 +171,12 @@ def handle(fn, d, include):
|
|||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def feeder(lineno, s, fn, root, statements, eof=False):
|
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__, bb, __residue__, __classname__
|
||||||
if __infunc__:
|
if __infunc__:
|
||||||
if s == '}':
|
if s == '}':
|
||||||
__body__.append('')
|
__body__.append('')
|
||||||
ast.handleMethod(statements, fn, lineno, __infunc__[0], __body__, __infunc__[3], __infunc__[4])
|
ast.handleMethod(statements, fn, lineno, __infunc__[0], __body__)
|
||||||
__infunc__ = []
|
__infunc__ = []
|
||||||
__body__ = []
|
__body__ = []
|
||||||
else:
|
else:
|
||||||
@@ -175,7 +185,7 @@ def feeder(lineno, s, fn, root, statements, eof=False):
|
|||||||
|
|
||||||
if __inpython__:
|
if __inpython__:
|
||||||
m = __python_func_regexp__.match(s)
|
m = __python_func_regexp__.match(s)
|
||||||
if m and not eof:
|
if m and lineno != IN_PYTHON_EOF:
|
||||||
__body__.append(s)
|
__body__.append(s)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -184,7 +194,7 @@ def feeder(lineno, s, fn, root, statements, eof=False):
|
|||||||
__body__ = []
|
__body__ = []
|
||||||
__inpython__ = False
|
__inpython__ = False
|
||||||
|
|
||||||
if eof:
|
if lineno == IN_PYTHON_EOF:
|
||||||
return
|
return
|
||||||
|
|
||||||
if s and s[0] == '#':
|
if s and s[0] == '#':
|
||||||
@@ -211,7 +221,8 @@ def feeder(lineno, s, fn, root, statements, eof=False):
|
|||||||
|
|
||||||
m = __func_start_regexp__.match(s)
|
m = __func_start_regexp__.match(s)
|
||||||
if m:
|
if m:
|
||||||
__infunc__ = [m.group("func") or "__anonymous", fn, lineno, m.group("py") is not None, m.group("fr") is not None]
|
__infunc__ = [m.group("func") or "__anonymous", fn, lineno]
|
||||||
|
ast.handleMethodFlags(statements, fn, lineno, __infunc__[0], m)
|
||||||
return
|
return
|
||||||
|
|
||||||
m = __def_regexp__.match(s)
|
m = __def_regexp__.match(s)
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ __config_regexp__ = re.compile( r"""
|
|||||||
__include_regexp__ = re.compile( r"include\s+(.+)" )
|
__include_regexp__ = re.compile( r"include\s+(.+)" )
|
||||||
__require_regexp__ = re.compile( r"require\s+(.+)" )
|
__require_regexp__ = re.compile( r"require\s+(.+)" )
|
||||||
__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
|
__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
|
||||||
__unset_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/]+)$" )
|
|
||||||
__unset_flag_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/]+)\[([a-zA-Z0-9\-_+.${}/]+)\]$" )
|
|
||||||
|
|
||||||
def init(data):
|
def init(data):
|
||||||
topdir = data.getVar('TOPDIR', False)
|
topdir = data.getVar('TOPDIR', False)
|
||||||
@@ -86,13 +84,13 @@ def include(parentfn, fn, lineno, data, error_out):
|
|||||||
bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
|
bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
|
||||||
abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
|
abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
|
||||||
if abs_fn and bb.parse.check_dependency(data, abs_fn):
|
if abs_fn and bb.parse.check_dependency(data, abs_fn):
|
||||||
logger.warning("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
|
logger.warn("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
|
||||||
for af in attempts:
|
for af in attempts:
|
||||||
bb.parse.mark_dependency(data, af)
|
bb.parse.mark_dependency(data, af)
|
||||||
if abs_fn:
|
if abs_fn:
|
||||||
fn = abs_fn
|
fn = abs_fn
|
||||||
elif bb.parse.check_dependency(data, fn):
|
elif bb.parse.check_dependency(data, fn):
|
||||||
logger.warning("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
|
logger.warn("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bb.parse.handle(fn, data, True)
|
bb.parse.handle(fn, data, True)
|
||||||
@@ -187,16 +185,6 @@ def feeder(lineno, s, fn, statements):
|
|||||||
ast.handleExport(statements, fn, lineno, m)
|
ast.handleExport(statements, fn, lineno, m)
|
||||||
return
|
return
|
||||||
|
|
||||||
m = __unset_regexp__.match(s)
|
|
||||||
if m:
|
|
||||||
ast.handleUnset(statements, fn, lineno, m)
|
|
||||||
return
|
|
||||||
|
|
||||||
m = __unset_flag_regexp__.match(s)
|
|
||||||
if m:
|
|
||||||
ast.handleUnsetFlag(statements, fn, lineno, m)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise ParseError("unparsed line: '%s'" % s, fn, lineno);
|
raise ParseError("unparsed line: '%s'" % s, fn, lineno);
|
||||||
|
|
||||||
# Add us to the handlers list
|
# Add us to the handlers list
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ class SQLTable(collections.MutableMapping):
|
|||||||
self._execute("DELETE from %s where key=?;" % self.table, [key])
|
self._execute("DELETE from %s where key=?;" % self.table, [key])
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if not isinstance(key, str):
|
if not isinstance(key, basestring):
|
||||||
raise TypeError('Only string keys are supported')
|
raise TypeError('Only string keys are supported')
|
||||||
elif not isinstance(value, str):
|
elif not isinstance(value, basestring):
|
||||||
raise TypeError('Only string values are supported')
|
raise TypeError('Only string values are supported')
|
||||||
|
|
||||||
data = self._execute("SELECT * from %s where key=?;" %
|
data = self._execute("SELECT * from %s where key=?;" %
|
||||||
@@ -178,7 +178,7 @@ class PersistData(object):
|
|||||||
"""
|
"""
|
||||||
Return a list of key + value pairs for a domain
|
Return a list of key + value pairs for a domain
|
||||||
"""
|
"""
|
||||||
return list(self.data[domain].items())
|
return self.data[domain].items()
|
||||||
|
|
||||||
def getValue(self, domain, key):
|
def getValue(self, domain, key):
|
||||||
"""
|
"""
|
||||||
@@ -201,7 +201,6 @@ class PersistData(object):
|
|||||||
def connect(database):
|
def connect(database):
|
||||||
connection = sqlite3.connect(database, timeout=5, isolation_level=None)
|
connection = sqlite3.connect(database, timeout=5, isolation_level=None)
|
||||||
connection.execute("pragma synchronous = off;")
|
connection.execute("pragma synchronous = off;")
|
||||||
connection.text_factory = str
|
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
def persist(domain, d):
|
def persist(domain, d):
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class CmdError(RuntimeError):
|
|||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if not isinstance(self.command, str):
|
if not isinstance(self.command, basestring):
|
||||||
cmd = subprocess.list2cmdline(self.command)
|
cmd = subprocess.list2cmdline(self.command)
|
||||||
else:
|
else:
|
||||||
cmd = self.command
|
cmd = self.command
|
||||||
@@ -97,8 +97,6 @@ def _logged_communicate(pipe, log, input, extrafiles):
|
|||||||
try:
|
try:
|
||||||
while pipe.poll() is None:
|
while pipe.poll() is None:
|
||||||
rlist = rin
|
rlist = rin
|
||||||
stdoutbuf = b""
|
|
||||||
stderrbuf = b""
|
|
||||||
try:
|
try:
|
||||||
r,w,e = select.select (rlist, [], [], 1)
|
r,w,e = select.select (rlist, [], [], 1)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
@@ -106,26 +104,16 @@ def _logged_communicate(pipe, log, input, extrafiles):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
if pipe.stdout in r:
|
if pipe.stdout in r:
|
||||||
data = stdoutbuf + pipe.stdout.read()
|
data = pipe.stdout.read()
|
||||||
if data is not None and len(data) > 0:
|
if data is not None:
|
||||||
try:
|
outdata.append(data)
|
||||||
data = data.decode("utf-8")
|
log.write(data)
|
||||||
outdata.append(data)
|
|
||||||
log.write(data)
|
|
||||||
stdoutbuf = b""
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
stdoutbuf = data
|
|
||||||
|
|
||||||
if pipe.stderr in r:
|
if pipe.stderr in r:
|
||||||
data = stderrbuf + pipe.stderr.read()
|
data = pipe.stderr.read()
|
||||||
if data is not None and len(data) > 0:
|
if data is not None:
|
||||||
try:
|
errdata.append(data)
|
||||||
data = data.decode("utf-8")
|
log.write(data)
|
||||||
errdata.append(data)
|
|
||||||
log.write(data)
|
|
||||||
stderrbuf = b""
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
stderrbuf = data
|
|
||||||
|
|
||||||
readextras(r)
|
readextras(r)
|
||||||
|
|
||||||
@@ -147,7 +135,7 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
|
|||||||
if not extrafiles:
|
if not extrafiles:
|
||||||
extrafiles = []
|
extrafiles = []
|
||||||
|
|
||||||
if isinstance(cmd, str) and not "shell" in options:
|
if isinstance(cmd, basestring) and not "shell" in options:
|
||||||
options["shell"] = True
|
options["shell"] = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -162,10 +150,6 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
|
|||||||
stdout, stderr = _logged_communicate(pipe, log, input, extrafiles)
|
stdout, stderr = _logged_communicate(pipe, log, input, extrafiles)
|
||||||
else:
|
else:
|
||||||
stdout, stderr = pipe.communicate(input)
|
stdout, stderr = pipe.communicate(input)
|
||||||
if stdout:
|
|
||||||
stdout = stdout.decode("utf-8")
|
|
||||||
if stderr:
|
|
||||||
stderr = stderr.decode("utf-8")
|
|
||||||
|
|
||||||
if pipe.returncode != 0:
|
if pipe.returncode != 0:
|
||||||
raise ExecutionError(cmd, pipe.returncode, stdout, stderr)
|
raise ExecutionError(cmd, pipe.returncode, stdout, stderr)
|
||||||
|
|||||||
@@ -1,276 +0,0 @@
|
|||||||
"""
|
|
||||||
BitBake progress handling code
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Copyright (C) 2016 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 sys
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import inspect
|
|
||||||
import bb.event
|
|
||||||
import bb.build
|
|
||||||
|
|
||||||
class ProgressHandler(object):
|
|
||||||
"""
|
|
||||||
Base class that can pretend to be a file object well enough to be
|
|
||||||
used to build objects to intercept console output and determine the
|
|
||||||
progress of some operation.
|
|
||||||
"""
|
|
||||||
def __init__(self, d, outfile=None):
|
|
||||||
self._progress = 0
|
|
||||||
self._data = d
|
|
||||||
self._lastevent = 0
|
|
||||||
if outfile:
|
|
||||||
self._outfile = outfile
|
|
||||||
else:
|
|
||||||
self._outfile = sys.stdout
|
|
||||||
|
|
||||||
def _fire_progress(self, taskprogress, rate=None):
|
|
||||||
"""Internal function to fire the progress event"""
|
|
||||||
bb.event.fire(bb.build.TaskProgress(taskprogress, rate), self._data)
|
|
||||||
|
|
||||||
def write(self, string):
|
|
||||||
self._outfile.write(string)
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
self._outfile.flush()
|
|
||||||
|
|
||||||
def update(self, progress, rate=None):
|
|
||||||
ts = time.time()
|
|
||||||
if progress > 100:
|
|
||||||
progress = 100
|
|
||||||
if progress != self._progress or self._lastevent + 1 < ts:
|
|
||||||
self._fire_progress(progress, rate)
|
|
||||||
self._lastevent = ts
|
|
||||||
self._progress = progress
|
|
||||||
|
|
||||||
class LineFilterProgressHandler(ProgressHandler):
|
|
||||||
"""
|
|
||||||
A ProgressHandler variant that provides the ability to filter out
|
|
||||||
the lines if they contain progress information. Additionally, it
|
|
||||||
filters out anything before the last line feed on a line. This can
|
|
||||||
be used to keep the logs clean of output that we've only enabled for
|
|
||||||
getting progress, assuming that that can be done on a per-line
|
|
||||||
basis.
|
|
||||||
"""
|
|
||||||
def __init__(self, d, outfile=None):
|
|
||||||
self._linebuffer = ''
|
|
||||||
super(LineFilterProgressHandler, self).__init__(d, outfile)
|
|
||||||
|
|
||||||
def write(self, string):
|
|
||||||
self._linebuffer += string
|
|
||||||
while True:
|
|
||||||
breakpos = self._linebuffer.find('\n') + 1
|
|
||||||
if breakpos == 0:
|
|
||||||
break
|
|
||||||
line = self._linebuffer[:breakpos]
|
|
||||||
self._linebuffer = self._linebuffer[breakpos:]
|
|
||||||
# Drop any line feeds and anything that precedes them
|
|
||||||
lbreakpos = line.rfind('\r') + 1
|
|
||||||
if lbreakpos:
|
|
||||||
line = line[lbreakpos:]
|
|
||||||
if self.writeline(line):
|
|
||||||
super(LineFilterProgressHandler, self).write(line)
|
|
||||||
|
|
||||||
def writeline(self, line):
|
|
||||||
return True
|
|
||||||
|
|
||||||
class BasicProgressHandler(ProgressHandler):
|
|
||||||
def __init__(self, d, regex=r'(\d+)%', outfile=None):
|
|
||||||
super(BasicProgressHandler, self).__init__(d, outfile)
|
|
||||||
self._regex = re.compile(regex)
|
|
||||||
# Send an initial progress event so the bar gets shown
|
|
||||||
self._fire_progress(0)
|
|
||||||
|
|
||||||
def write(self, string):
|
|
||||||
percs = self._regex.findall(string)
|
|
||||||
if percs:
|
|
||||||
progress = int(percs[-1])
|
|
||||||
self.update(progress)
|
|
||||||
super(BasicProgressHandler, self).write(string)
|
|
||||||
|
|
||||||
class OutOfProgressHandler(ProgressHandler):
|
|
||||||
def __init__(self, d, regex, outfile=None):
|
|
||||||
super(OutOfProgressHandler, self).__init__(d, outfile)
|
|
||||||
self._regex = re.compile(regex)
|
|
||||||
# Send an initial progress event so the bar gets shown
|
|
||||||
self._fire_progress(0)
|
|
||||||
|
|
||||||
def write(self, string):
|
|
||||||
nums = self._regex.findall(string)
|
|
||||||
if nums:
|
|
||||||
progress = (float(nums[-1][0]) / float(nums[-1][1])) * 100
|
|
||||||
self.update(progress)
|
|
||||||
super(OutOfProgressHandler, self).write(string)
|
|
||||||
|
|
||||||
class MultiStageProgressReporter(object):
|
|
||||||
"""
|
|
||||||
Class which allows reporting progress without the caller
|
|
||||||
having to know where they are in the overall sequence. Useful
|
|
||||||
for tasks made up of python code spread across multiple
|
|
||||||
classes / functions - the progress reporter object can
|
|
||||||
be passed around or stored at the object level and calls
|
|
||||||
to next_stage() and update() made whereever needed.
|
|
||||||
"""
|
|
||||||
def __init__(self, d, stage_weights, debug=False):
|
|
||||||
"""
|
|
||||||
Initialise the progress reporter.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
* d: the datastore (needed for firing the events)
|
|
||||||
* stage_weights: a list of weight values, one for each stage.
|
|
||||||
The value is scaled internally so you only need to specify
|
|
||||||
values relative to other values in the list, so if there
|
|
||||||
are two stages and the first takes 2s and the second takes
|
|
||||||
10s you would specify [2, 10] (or [1, 5], it doesn't matter).
|
|
||||||
* debug: specify True (and ensure you call finish() at the end)
|
|
||||||
in order to show a printout of the calculated stage weights
|
|
||||||
based on timing each stage. Use this to determine what the
|
|
||||||
weights should be when you're not sure.
|
|
||||||
"""
|
|
||||||
self._data = d
|
|
||||||
total = sum(stage_weights)
|
|
||||||
self._stage_weights = [float(x)/total for x in stage_weights]
|
|
||||||
self._stage = -1
|
|
||||||
self._base_progress = 0
|
|
||||||
# Send an initial progress event so the bar gets shown
|
|
||||||
self._fire_progress(0)
|
|
||||||
self._debug = debug
|
|
||||||
self._finished = False
|
|
||||||
if self._debug:
|
|
||||||
self._last_time = time.time()
|
|
||||||
self._stage_times = []
|
|
||||||
self._stage_total = None
|
|
||||||
self._callers = []
|
|
||||||
|
|
||||||
def _fire_progress(self, taskprogress):
|
|
||||||
bb.event.fire(bb.build.TaskProgress(taskprogress), self._data)
|
|
||||||
|
|
||||||
def next_stage(self, stage_total=None):
|
|
||||||
"""
|
|
||||||
Move to the next stage.
|
|
||||||
Parameters:
|
|
||||||
* stage_total: optional total for progress within the stage,
|
|
||||||
see update() for details
|
|
||||||
NOTE: you need to call this before the first stage.
|
|
||||||
"""
|
|
||||||
self._stage += 1
|
|
||||||
self._stage_total = stage_total
|
|
||||||
if self._stage == 0:
|
|
||||||
# First stage
|
|
||||||
if self._debug:
|
|
||||||
self._last_time = time.time()
|
|
||||||
else:
|
|
||||||
if self._stage < len(self._stage_weights):
|
|
||||||
self._base_progress = sum(self._stage_weights[:self._stage]) * 100
|
|
||||||
if self._debug:
|
|
||||||
currtime = time.time()
|
|
||||||
self._stage_times.append(currtime - self._last_time)
|
|
||||||
self._last_time = currtime
|
|
||||||
self._callers.append(inspect.getouterframes(inspect.currentframe())[1])
|
|
||||||
elif not self._debug:
|
|
||||||
bb.warn('ProgressReporter: current stage beyond declared number of stages')
|
|
||||||
self._base_progress = 100
|
|
||||||
self._fire_progress(self._base_progress)
|
|
||||||
|
|
||||||
def update(self, stage_progress):
|
|
||||||
"""
|
|
||||||
Update progress within the current stage.
|
|
||||||
Parameters:
|
|
||||||
* stage_progress: progress value within the stage. If stage_total
|
|
||||||
was specified when next_stage() was last called, then this
|
|
||||||
value is considered to be out of stage_total, otherwise it should
|
|
||||||
be a percentage value from 0 to 100.
|
|
||||||
"""
|
|
||||||
if self._stage_total:
|
|
||||||
stage_progress = (float(stage_progress) / self._stage_total) * 100
|
|
||||||
if self._stage < 0:
|
|
||||||
bb.warn('ProgressReporter: update called before first call to next_stage()')
|
|
||||||
elif self._stage < len(self._stage_weights):
|
|
||||||
progress = self._base_progress + (stage_progress * self._stage_weights[self._stage])
|
|
||||||
else:
|
|
||||||
progress = self._base_progress
|
|
||||||
if progress > 100:
|
|
||||||
progress = 100
|
|
||||||
self._fire_progress(progress)
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
if self._finished:
|
|
||||||
return
|
|
||||||
self._finished = True
|
|
||||||
if self._debug:
|
|
||||||
import math
|
|
||||||
self._stage_times.append(time.time() - self._last_time)
|
|
||||||
mintime = max(min(self._stage_times), 0.01)
|
|
||||||
self._callers.append(None)
|
|
||||||
stage_weights = [int(math.ceil(x / mintime)) for x in self._stage_times]
|
|
||||||
bb.warn('Stage weights: %s' % stage_weights)
|
|
||||||
out = []
|
|
||||||
for stage_weight, caller in zip(stage_weights, self._callers):
|
|
||||||
if caller:
|
|
||||||
out.append('Up to %s:%d: %d' % (caller[1], caller[2], stage_weight))
|
|
||||||
else:
|
|
||||||
out.append('Up to finish: %d' % stage_weight)
|
|
||||||
bb.warn('Stage times:\n %s' % '\n '.join(out))
|
|
||||||
|
|
||||||
class MultiStageProcessProgressReporter(MultiStageProgressReporter):
|
|
||||||
"""
|
|
||||||
Version of MultiStageProgressReporter intended for use with
|
|
||||||
standalone processes (such as preparing the runqueue)
|
|
||||||
"""
|
|
||||||
def __init__(self, d, processname, stage_weights, debug=False):
|
|
||||||
self._processname = processname
|
|
||||||
self._started = False
|
|
||||||
MultiStageProgressReporter.__init__(self, d, stage_weights, debug)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
if not self._started:
|
|
||||||
bb.event.fire(bb.event.ProcessStarted(self._processname, 100), self._data)
|
|
||||||
self._started = True
|
|
||||||
|
|
||||||
def _fire_progress(self, taskprogress):
|
|
||||||
if taskprogress == 0:
|
|
||||||
self.start()
|
|
||||||
return
|
|
||||||
bb.event.fire(bb.event.ProcessProgress(self._processname, taskprogress), self._data)
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
MultiStageProgressReporter.finish(self)
|
|
||||||
bb.event.fire(bb.event.ProcessFinished(self._processname), self._data)
|
|
||||||
|
|
||||||
class DummyMultiStageProcessProgressReporter(MultiStageProgressReporter):
|
|
||||||
"""
|
|
||||||
MultiStageProcessProgressReporter that takes the calls and does nothing
|
|
||||||
with them (to avoid a bunch of "if progress_reporter:" checks)
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
MultiStageProcessProgressReporter.__init__(self, "", None, [])
|
|
||||||
|
|
||||||
def _fire_progress(self, taskprogress, rate=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def next_stage(self, stage_total=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update(self, stage_progress):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
pass
|
|
||||||
@@ -121,14 +121,11 @@ def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
|
|||||||
preferred_file = None
|
preferred_file = None
|
||||||
preferred_ver = None
|
preferred_ver = None
|
||||||
|
|
||||||
# pn can contain '_', e.g. gcc-cross-x86_64 and an override cannot
|
localdata = data.createCopy(cfgData)
|
||||||
# hence we do this manually rather than use OVERRIDES
|
localdata.setVar('OVERRIDES', "%s:pn-%s:%s" % (data.getVar('OVERRIDES', localdata), pn, pn))
|
||||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn, True)
|
bb.data.update_data(localdata)
|
||||||
if not preferred_v:
|
|
||||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn, True)
|
|
||||||
if not preferred_v:
|
|
||||||
preferred_v = cfgData.getVar("PREFERRED_VERSION", True)
|
|
||||||
|
|
||||||
|
preferred_v = localdata.getVar('PREFERRED_VERSION', True)
|
||||||
if preferred_v:
|
if preferred_v:
|
||||||
m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
|
m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
|
||||||
if m:
|
if m:
|
||||||
@@ -226,7 +223,7 @@ def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
|
|||||||
def _filterProviders(providers, item, cfgData, dataCache):
|
def _filterProviders(providers, item, cfgData, dataCache):
|
||||||
"""
|
"""
|
||||||
Take a list of providers and filter/reorder according to the
|
Take a list of providers and filter/reorder according to the
|
||||||
environment variables
|
environment variables and previous build results
|
||||||
"""
|
"""
|
||||||
eligible = []
|
eligible = []
|
||||||
preferred_versions = {}
|
preferred_versions = {}
|
||||||
@@ -245,7 +242,7 @@ def _filterProviders(providers, item, cfgData, dataCache):
|
|||||||
pkg_pn[pn] = []
|
pkg_pn[pn] = []
|
||||||
pkg_pn[pn].append(p)
|
pkg_pn[pn].append(p)
|
||||||
|
|
||||||
logger.debug(1, "providers for %s are: %s", item, list(pkg_pn.keys()))
|
logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
|
||||||
|
|
||||||
# First add PREFERRED_VERSIONS
|
# First add PREFERRED_VERSIONS
|
||||||
for pn in pkg_pn:
|
for pn in pkg_pn:
|
||||||
@@ -283,7 +280,7 @@ def _filterProviders(providers, item, cfgData, dataCache):
|
|||||||
def filterProviders(providers, item, cfgData, dataCache):
|
def filterProviders(providers, item, cfgData, dataCache):
|
||||||
"""
|
"""
|
||||||
Take a list of providers and filter/reorder according to the
|
Take a list of providers and filter/reorder according to the
|
||||||
environment variables
|
environment variables and previous build results
|
||||||
Takes a "normal" target item
|
Takes a "normal" target item
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -311,56 +308,38 @@ def filterProviders(providers, item, cfgData, dataCache):
|
|||||||
def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
||||||
"""
|
"""
|
||||||
Take a list of providers and filter/reorder according to the
|
Take a list of providers and filter/reorder according to the
|
||||||
environment variables
|
environment variables and previous build results
|
||||||
Takes a "runtime" target item
|
Takes a "runtime" target item
|
||||||
"""
|
"""
|
||||||
|
|
||||||
eligible = _filterProviders(providers, item, cfgData, dataCache)
|
eligible = _filterProviders(providers, item, cfgData, dataCache)
|
||||||
|
|
||||||
# First try and match any PREFERRED_RPROVIDER entry
|
# Should use dataCache.preferred here?
|
||||||
prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item, True)
|
preferred = []
|
||||||
foundUnique = False
|
preferred_vars = []
|
||||||
if prefervar:
|
pns = {}
|
||||||
for p in eligible:
|
for p in eligible:
|
||||||
pn = dataCache.pkg_fn[p]
|
pns[dataCache.pkg_fn[p]] = p
|
||||||
if prefervar == pn:
|
for p in eligible:
|
||||||
logger.verbose("selecting %s to satisfy %s due to PREFERRED_RPROVIDER", pn, item)
|
pn = dataCache.pkg_fn[p]
|
||||||
eligible.remove(p)
|
provides = dataCache.pn_provides[pn]
|
||||||
eligible = [p] + eligible
|
for provide in provides:
|
||||||
foundUnique = True
|
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
|
||||||
numberPreferred = 1
|
#logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
|
||||||
|
if prefervar in pns and pns[prefervar] not in preferred:
|
||||||
|
var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
|
||||||
|
logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
|
||||||
|
preferred_vars.append(var)
|
||||||
|
pref = pns[prefervar]
|
||||||
|
eligible.remove(pref)
|
||||||
|
eligible = [pref] + eligible
|
||||||
|
preferred.append(pref)
|
||||||
break
|
break
|
||||||
|
|
||||||
# If we didn't find an RPROVIDER entry, try and infer the provider from PREFERRED_PROVIDER entries
|
numberPreferred = len(preferred)
|
||||||
# by looking through the provides of each eligible recipe and seeing if a PREFERRED_PROVIDER was set.
|
|
||||||
# This is most useful for virtual/ entries rather than having a RPROVIDER per entry.
|
|
||||||
if not foundUnique:
|
|
||||||
# Should use dataCache.preferred here?
|
|
||||||
preferred = []
|
|
||||||
preferred_vars = []
|
|
||||||
pns = {}
|
|
||||||
for p in eligible:
|
|
||||||
pns[dataCache.pkg_fn[p]] = p
|
|
||||||
for p in eligible:
|
|
||||||
pn = dataCache.pkg_fn[p]
|
|
||||||
provides = dataCache.pn_provides[pn]
|
|
||||||
for provide in provides:
|
|
||||||
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
|
|
||||||
#logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
|
|
||||||
if prefervar in pns and pns[prefervar] not in preferred:
|
|
||||||
var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
|
|
||||||
logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
|
|
||||||
preferred_vars.append(var)
|
|
||||||
pref = pns[prefervar]
|
|
||||||
eligible.remove(pref)
|
|
||||||
eligible = [pref] + eligible
|
|
||||||
preferred.append(pref)
|
|
||||||
break
|
|
||||||
|
|
||||||
numberPreferred = len(preferred)
|
|
||||||
|
|
||||||
if numberPreferred > 1:
|
if numberPreferred > 1:
|
||||||
logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s. You could set PREFERRED_RPROVIDER_%s" % (item, preferred, preferred_vars, item))
|
logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s", item, preferred, preferred_vars)
|
||||||
|
|
||||||
logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
|
logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
|
||||||
|
|
||||||
@@ -400,32 +379,3 @@ def getRuntimeProviders(dataCache, rdepend):
|
|||||||
logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
|
logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
|
||||||
|
|
||||||
return rproviders
|
return rproviders
|
||||||
|
|
||||||
|
|
||||||
def buildWorldTargetList(dataCache, task=None):
|
|
||||||
"""
|
|
||||||
Build package list for "bitbake world"
|
|
||||||
"""
|
|
||||||
if dataCache.world_target:
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.debug(1, "collating packages for \"world\"")
|
|
||||||
for f in dataCache.possible_world:
|
|
||||||
terminal = True
|
|
||||||
pn = dataCache.pkg_fn[f]
|
|
||||||
if task and task not in dataCache.task_deps[f]['tasks']:
|
|
||||||
logger.debug(2, "World build skipping %s as task %s doesn't exist", f, task)
|
|
||||||
terminal = False
|
|
||||||
|
|
||||||
for p in dataCache.pn_provides[pn]:
|
|
||||||
if p.startswith('virtual/'):
|
|
||||||
logger.debug(2, "World build skipping %s due to %s provider starting with virtual/", f, p)
|
|
||||||
terminal = False
|
|
||||||
break
|
|
||||||
for pf in dataCache.providers[p]:
|
|
||||||
if dataCache.pkg_fn[pf] != pn:
|
|
||||||
logger.debug(2, "World build skipping %s due to both us and %s providing %s", f, pf, p)
|
|
||||||
terminal = False
|
|
||||||
break
|
|
||||||
if terminal:
|
|
||||||
dataCache.world_target.add(pn)
|
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ def utility_sed(name, args, interp, env, stdin, stdout, stderr, debugflags):
|
|||||||
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
|
||||||
|
|
||||||
# Scan pattern arguments and append a space if necessary
|
# Scan pattern arguments and append a space if necessary
|
||||||
for i in range(len(args)):
|
for i in xrange(len(args)):
|
||||||
if not RE_SED.search(args[i]):
|
if not RE_SED.search(args[i]):
|
||||||
continue
|
continue
|
||||||
args[i] = args[i] + ' '
|
args[i] = args[i] + ' '
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ class Environment:
|
|||||||
"""
|
"""
|
||||||
# Save and remove previous arguments
|
# Save and remove previous arguments
|
||||||
prevargs = []
|
prevargs = []
|
||||||
for i in range(int(self._env['#'])):
|
for i in xrange(int(self._env['#'])):
|
||||||
i = str(i+1)
|
i = str(i+1)
|
||||||
prevargs.append(self._env[i])
|
prevargs.append(self._env[i])
|
||||||
del self._env[i]
|
del self._env[i]
|
||||||
@@ -488,7 +488,7 @@ class Environment:
|
|||||||
return prevargs
|
return prevargs
|
||||||
|
|
||||||
def get_positional_args(self):
|
def get_positional_args(self):
|
||||||
return [self._env[str(i+1)] for i in range(int(self._env['#']))]
|
return [self._env[str(i+1)] for i in xrange(int(self._env['#']))]
|
||||||
|
|
||||||
def get_variables(self):
|
def get_variables(self):
|
||||||
return dict(self._env)
|
return dict(self._env)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ except NameError:
|
|||||||
from Set import Set as set
|
from Set import Set as set
|
||||||
|
|
||||||
from ply import lex
|
from ply import lex
|
||||||
from bb.pysh.sherrors import *
|
from sherrors import *
|
||||||
|
|
||||||
class NeedMore(Exception):
|
class NeedMore(Exception):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bb.pysh.pyshlex as pyshlex
|
import pyshlex
|
||||||
tokens = pyshlex.tokens
|
tokens = pyshlex.tokens
|
||||||
|
|
||||||
from ply import yacc
|
from ply import yacc
|
||||||
import bb.pysh.sherrors as sherrors
|
import sherrors
|
||||||
|
|
||||||
class IORedirect:
|
class IORedirect:
|
||||||
def __init__(self, op, filename, io_number=None):
|
def __init__(self, op, filename, io_number=None):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -63,9 +63,6 @@ class BitBakeBaseServerConnection():
|
|||||||
def terminate(self):
|
def terminate(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setupEventQueue(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
""" BitBakeBaseServer class is the common ancestor to all Bitbake servers
|
""" BitBakeBaseServer class is the common ancestor to all Bitbake servers
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import select
|
import select
|
||||||
from queue import Empty
|
from Queue import Empty
|
||||||
from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
|
from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
|
||||||
|
|
||||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||||
@@ -53,12 +53,10 @@ class ServerCommunicator():
|
|||||||
while True:
|
while True:
|
||||||
# don't let the user ctrl-c while we're waiting for a response
|
# don't let the user ctrl-c while we're waiting for a response
|
||||||
try:
|
try:
|
||||||
for idx in range(0,4): # 0, 1, 2, 3
|
if self.connection.poll(20):
|
||||||
if self.connection.poll(5):
|
return self.connection.recv()
|
||||||
return self.connection.recv()
|
else:
|
||||||
else:
|
bb.fatal("Timeout while attempting to communicate with bitbake server")
|
||||||
bb.warn("Timeout while attempting to communicate with bitbake server")
|
|
||||||
bb.fatal("Gave up; Too many tries: timeout while attempting to communicate with bitbake server")
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -108,7 +106,6 @@ class ProcessServer(Process, BaseImplServer):
|
|||||||
# the UI and communicated to us
|
# the UI and communicated to us
|
||||||
self.quitin.close()
|
self.quitin.close()
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
bb.utils.set_process_name("Cooker")
|
|
||||||
while not self.quit:
|
while not self.quit:
|
||||||
try:
|
try:
|
||||||
if self.command_channel.poll():
|
if self.command_channel.poll():
|
||||||
@@ -137,7 +134,7 @@ class ProcessServer(Process, BaseImplServer):
|
|||||||
if not fds:
|
if not fds:
|
||||||
fds = []
|
fds = []
|
||||||
|
|
||||||
for function, data in list(self._idlefuns.items()):
|
for function, data in self._idlefuns.items():
|
||||||
try:
|
try:
|
||||||
retval = function(self, data, False)
|
retval = function(self, data, False)
|
||||||
if retval is False:
|
if retval is False:
|
||||||
@@ -145,7 +142,7 @@ class ProcessServer(Process, BaseImplServer):
|
|||||||
nextsleep = None
|
nextsleep = None
|
||||||
elif retval is True:
|
elif retval is True:
|
||||||
nextsleep = None
|
nextsleep = None
|
||||||
elif isinstance(retval, float) and nextsleep:
|
elif isinstance(retval, float):
|
||||||
if (retval < nextsleep):
|
if (retval < nextsleep):
|
||||||
nextsleep = retval
|
nextsleep = retval
|
||||||
elif nextsleep is None:
|
elif nextsleep is None:
|
||||||
@@ -213,19 +210,19 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
|
|||||||
# Wrap Queue to provide API which isn't server implementation specific
|
# Wrap Queue to provide API which isn't server implementation specific
|
||||||
class ProcessEventQueue(multiprocessing.queues.Queue):
|
class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||||
def __init__(self, maxsize):
|
def __init__(self, maxsize):
|
||||||
multiprocessing.queues.Queue.__init__(self, maxsize, ctx=multiprocessing.get_context())
|
multiprocessing.queues.Queue.__init__(self, maxsize)
|
||||||
self.exit = False
|
self.exit = False
|
||||||
bb.utils.set_process_name("ProcessEQueue")
|
|
||||||
|
|
||||||
def setexit(self):
|
def setexit(self):
|
||||||
self.exit = True
|
self.exit = True
|
||||||
|
|
||||||
def waitEvent(self, timeout):
|
def waitEvent(self, timeout):
|
||||||
if self.exit:
|
if self.exit:
|
||||||
return self.getEvent()
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
if not self.server.is_alive():
|
if not self.server.is_alive():
|
||||||
return self.getEvent()
|
self.setexit()
|
||||||
|
return None
|
||||||
return self.get(True, timeout)
|
return self.get(True, timeout)
|
||||||
except Empty:
|
except Empty:
|
||||||
return None
|
return None
|
||||||
@@ -234,15 +231,14 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
|
|||||||
try:
|
try:
|
||||||
if not self.server.is_alive():
|
if not self.server.is_alive():
|
||||||
self.setexit()
|
self.setexit()
|
||||||
|
return None
|
||||||
return self.get(False)
|
return self.get(False)
|
||||||
except Empty:
|
except Empty:
|
||||||
if self.exit:
|
|
||||||
sys.exit(1)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class BitBakeServer(BitBakeBaseServer):
|
class BitBakeServer(BitBakeBaseServer):
|
||||||
def initServer(self, single_use=True):
|
def initServer(self):
|
||||||
# establish communication channels. We use bidirectional pipes for
|
# establish communication channels. We use bidirectional pipes for
|
||||||
# ui <--> server command/response pairs
|
# ui <--> server command/response pairs
|
||||||
# and a queue for server -> ui event notifications
|
# and a queue for server -> ui event notifications
|
||||||
|
|||||||
@@ -31,33 +31,31 @@
|
|||||||
in the server's main loop.
|
in the server's main loop.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import time
|
|
||||||
import socket
|
|
||||||
import signal
|
|
||||||
import threading
|
|
||||||
import pickle
|
|
||||||
import inspect
|
|
||||||
import select
|
|
||||||
import http.client
|
|
||||||
import xmlrpc.client
|
|
||||||
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
|
||||||
|
|
||||||
import bb
|
import bb
|
||||||
|
import xmlrpclib, sys
|
||||||
from bb import daemonize
|
from bb import daemonize
|
||||||
from bb.ui import uievent
|
from bb.ui import uievent
|
||||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
import hashlib, time
|
||||||
|
import socket
|
||||||
|
import os, signal
|
||||||
|
import threading
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
class BBTransport(xmlrpc.client.Transport):
|
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||||
|
import inspect, select, httplib
|
||||||
|
|
||||||
|
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||||
|
|
||||||
|
class BBTransport(xmlrpclib.Transport):
|
||||||
def __init__(self, timeout):
|
def __init__(self, timeout):
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.connection_token = None
|
self.connection_token = None
|
||||||
xmlrpc.client.Transport.__init__(self)
|
xmlrpclib.Transport.__init__(self)
|
||||||
|
|
||||||
# Modified from default to pass timeout to HTTPConnection
|
# Modified from default to pass timeout to HTTPConnection
|
||||||
def make_connection(self, host):
|
def make_connection(self, host):
|
||||||
@@ -69,7 +67,7 @@ class BBTransport(xmlrpc.client.Transport):
|
|||||||
# create a HTTP connection object from a host descriptor
|
# create a HTTP connection object from a host descriptor
|
||||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||||
#store the host argument along with the connection object
|
#store the host argument along with the connection object
|
||||||
self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout)
|
self._connection = host, httplib.HTTPConnection(chost, timeout=self.timeout)
|
||||||
return self._connection[1]
|
return self._connection[1]
|
||||||
|
|
||||||
def set_connection_token(self, token):
|
def set_connection_token(self, token):
|
||||||
@@ -78,30 +76,13 @@ class BBTransport(xmlrpc.client.Transport):
|
|||||||
def send_content(self, h, body):
|
def send_content(self, h, body):
|
||||||
if self.connection_token:
|
if self.connection_token:
|
||||||
h.putheader("Bitbake-token", self.connection_token)
|
h.putheader("Bitbake-token", self.connection_token)
|
||||||
xmlrpc.client.Transport.send_content(self, h, body)
|
xmlrpclib.Transport.send_content(self, h, body)
|
||||||
|
|
||||||
def _create_server(host, port, timeout = 60):
|
def _create_server(host, port, timeout = 60):
|
||||||
t = BBTransport(timeout)
|
t = BBTransport(timeout)
|
||||||
s = xmlrpc.client.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True, use_builtin_types=True)
|
s = xmlrpclib.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True)
|
||||||
return s, t
|
return s, t
|
||||||
|
|
||||||
def check_connection(remote, timeout):
|
|
||||||
try:
|
|
||||||
host, port = remote.split(":")
|
|
||||||
port = int(port)
|
|
||||||
except Exception as e:
|
|
||||||
bb.warn("Failed to read remote definition (%s)" % str(e))
|
|
||||||
raise e
|
|
||||||
|
|
||||||
server, _transport = _create_server(host, port, timeout)
|
|
||||||
try:
|
|
||||||
ret, err = server.runCommand(['getVariable', 'TOPDIR'])
|
|
||||||
if err or not ret:
|
|
||||||
return False
|
|
||||||
except ConnectionError:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
class BitBakeServerCommands():
|
class BitBakeServerCommands():
|
||||||
|
|
||||||
def __init__(self, server):
|
def __init__(self, server):
|
||||||
@@ -116,10 +97,10 @@ class BitBakeServerCommands():
|
|||||||
|
|
||||||
# we don't allow connections if the cooker is running
|
# we don't allow connections if the cooker is running
|
||||||
if (self.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]):
|
if (self.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]):
|
||||||
return None, "Cooker is busy: %s" % bb.cooker.state.get_name(self.cooker.state)
|
return None
|
||||||
|
|
||||||
self.event_handle = bb.event.register_UIHhandler(s, True)
|
self.event_handle = bb.event.register_UIHhandler(s, True)
|
||||||
return self.event_handle, 'OK'
|
return self.event_handle
|
||||||
|
|
||||||
def unregisterEventHandler(self, handlerNum):
|
def unregisterEventHandler(self, handlerNum):
|
||||||
"""
|
"""
|
||||||
@@ -147,7 +128,7 @@ class BitBakeServerCommands():
|
|||||||
def addClient(self):
|
def addClient(self):
|
||||||
if self.has_client:
|
if self.has_client:
|
||||||
return None
|
return None
|
||||||
token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest()
|
token = hashlib.md5(str(time.time())).hexdigest()
|
||||||
self.server.set_connection_token(token)
|
self.server.set_connection_token(token)
|
||||||
self.has_client = True
|
self.has_client = True
|
||||||
return token
|
return token
|
||||||
@@ -197,7 +178,7 @@ class XMLRPCProxyServer(BaseImplServer):
|
|||||||
""" not a real working server, but a stub for a proxy server connection
|
""" not a real working server, but a stub for a proxy server connection
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, host, port, use_builtin_types=True):
|
def __init__(self, host, port):
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
@@ -205,12 +186,13 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
|||||||
# remove this when you're done with debugging
|
# remove this when you're done with debugging
|
||||||
# allow_reuse_address = True
|
# allow_reuse_address = True
|
||||||
|
|
||||||
def __init__(self, interface, single_use=False, idle_timeout=0):
|
def __init__(self, interface):
|
||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
BaseImplServer.__init__(self)
|
BaseImplServer.__init__(self)
|
||||||
self.single_use = single_use
|
if (interface[1] == 0): # anonymous port, not getting reused
|
||||||
|
self.single_use = True
|
||||||
# Use auto port configuration
|
# Use auto port configuration
|
||||||
if (interface[1] == -1):
|
if (interface[1] == -1):
|
||||||
interface = (interface[0], 0)
|
interface = (interface[0], 0)
|
||||||
@@ -223,10 +205,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
|||||||
self.commands = BitBakeServerCommands(self)
|
self.commands = BitBakeServerCommands(self)
|
||||||
self.autoregister_all_functions(self.commands, "")
|
self.autoregister_all_functions(self.commands, "")
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.time = time.time()
|
self.single_use = False
|
||||||
self.idle_timeout = idle_timeout
|
|
||||||
if idle_timeout:
|
|
||||||
self.register_idle_function(self.handle_idle_timeout, self)
|
|
||||||
|
|
||||||
def addcooker(self, cooker):
|
def addcooker(self, cooker):
|
||||||
BaseImplServer.addcooker(self, cooker)
|
BaseImplServer.addcooker(self, cooker)
|
||||||
@@ -242,12 +221,6 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
|||||||
if name.startswith(prefix):
|
if name.startswith(prefix):
|
||||||
self.register_function(method, name[len(prefix):])
|
self.register_function(method, name[len(prefix):])
|
||||||
|
|
||||||
def handle_idle_timeout(self, server, data, abort):
|
|
||||||
if not abort:
|
|
||||||
if time.time() - server.time > server.idle_timeout:
|
|
||||||
server.quit = True
|
|
||||||
print("Server idle timeout expired")
|
|
||||||
return []
|
|
||||||
|
|
||||||
def serve_forever(self):
|
def serve_forever(self):
|
||||||
# Start the actual XMLRPC server
|
# Start the actual XMLRPC server
|
||||||
@@ -261,7 +234,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
|||||||
while not self.quit:
|
while not self.quit:
|
||||||
fds = [self]
|
fds = [self]
|
||||||
nextsleep = 0.1
|
nextsleep = 0.1
|
||||||
for function, data in list(self._idlefuns.items()):
|
for function, data in self._idlefuns.items():
|
||||||
retval = None
|
retval = None
|
||||||
try:
|
try:
|
||||||
retval = function(self, data, False)
|
retval = function(self, data, False)
|
||||||
@@ -290,15 +263,13 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
|||||||
try:
|
try:
|
||||||
fd_sets = select.select(fds, [], [], socktimeout)
|
fd_sets = select.select(fds, [], [], socktimeout)
|
||||||
if fd_sets[0] and self in fd_sets[0]:
|
if fd_sets[0] and self in fd_sets[0]:
|
||||||
if self.idle_timeout:
|
|
||||||
self.time = time.time()
|
|
||||||
self._handle_request_noblock()
|
self._handle_request_noblock()
|
||||||
except IOError:
|
except IOError:
|
||||||
# we ignore interrupted calls
|
# we ignore interrupted calls
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Tell idle functions we're exiting
|
# Tell idle functions we're exiting
|
||||||
for function, data in list(self._idlefuns.items()):
|
for function, data in self._idlefuns.items():
|
||||||
try:
|
try:
|
||||||
retval = function(self, data, True)
|
retval = function(self, data, True)
|
||||||
except:
|
except:
|
||||||
@@ -331,9 +302,7 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
self.transport.set_connection_token(token)
|
self.transport.set_connection_token(token)
|
||||||
return self
|
|
||||||
|
|
||||||
def setupEventQueue(self):
|
|
||||||
self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo)
|
self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo)
|
||||||
for event in bb.event.ui_queue:
|
for event in bb.event.ui_queue:
|
||||||
self.events.queue_event(event)
|
self.events.queue_event(event)
|
||||||
@@ -345,6 +314,8 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
|||||||
# no need to log it here, the error shall be sent to the client
|
# no need to log it here, the error shall be sent to the client
|
||||||
raise BaseException(error)
|
raise BaseException(error)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def removeClient(self):
|
def removeClient(self):
|
||||||
if not self.observer_only:
|
if not self.observer_only:
|
||||||
self.connection.removeClient()
|
self.connection.removeClient()
|
||||||
@@ -363,10 +334,9 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class BitBakeServer(BitBakeBaseServer):
|
class BitBakeServer(BitBakeBaseServer):
|
||||||
def initServer(self, interface = ("localhost", 0),
|
def initServer(self, interface = ("localhost", 0)):
|
||||||
single_use = False, idle_timeout=0):
|
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.serverImpl = XMLRPCServer(interface, single_use, idle_timeout)
|
self.serverImpl = XMLRPCServer(interface)
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
daemonize.createDaemon(self.serverImpl.serve_forever, "bitbake-cookerdaemon.log")
|
daemonize.createDaemon(self.serverImpl.serve_forever, "bitbake-cookerdaemon.log")
|
||||||
@@ -411,7 +381,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
|
|||||||
bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
|
bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
|
||||||
raise e
|
raise e
|
||||||
try:
|
try:
|
||||||
self.serverImpl = XMLRPCProxyServer(host, port, use_builtin_types=True)
|
self.serverImpl = XMLRPCProxyServer(host, port)
|
||||||
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only, featureset)
|
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only, featureset)
|
||||||
return self.connection.connect(self.token)
|
return self.connection.connect(self.token)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -3,14 +3,18 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import pickle
|
|
||||||
import bb.data
|
import bb.data
|
||||||
from bb.checksum import FileChecksumCache
|
|
||||||
|
|
||||||
logger = logging.getLogger('BitBake.SigGen')
|
logger = logging.getLogger('BitBake.SigGen')
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
logger.info('Importing cPickle failed. Falling back to a very slow implementation.')
|
||||||
|
|
||||||
def init(d):
|
def init(d):
|
||||||
siggens = [obj for obj in globals().values()
|
siggens = [obj for obj in globals().itervalues()
|
||||||
if type(obj) is type and issubclass(obj, SignatureGenerator)]
|
if type(obj) is type and issubclass(obj, SignatureGenerator)]
|
||||||
|
|
||||||
desired = d.getVar("BB_SIGNATURE_HANDLER", True) or "noop"
|
desired = d.getVar("BB_SIGNATURE_HANDLER", True) or "noop"
|
||||||
@@ -30,11 +34,9 @@ class SignatureGenerator(object):
|
|||||||
name = "noop"
|
name = "noop"
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.basehash = {}
|
|
||||||
self.taskhash = {}
|
self.taskhash = {}
|
||||||
self.runtaskdeps = {}
|
self.runtaskdeps = {}
|
||||||
self.file_checksum_values = {}
|
self.file_checksum_values = {}
|
||||||
self.taints = {}
|
|
||||||
|
|
||||||
def finalise(self, fn, d, varient):
|
def finalise(self, fn, d, varient):
|
||||||
return
|
return
|
||||||
@@ -42,8 +44,7 @@ class SignatureGenerator(object):
|
|||||||
def get_taskhash(self, fn, task, deps, dataCache):
|
def get_taskhash(self, fn, task, deps, dataCache):
|
||||||
return "0"
|
return "0"
|
||||||
|
|
||||||
def writeout_file_checksum_cache(self):
|
def set_taskdata(self, hashes, deps, checksum):
|
||||||
"""Write/update the file checksum cache onto disk"""
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def stampfile(self, stampbase, file_name, taskname, extrainfo):
|
def stampfile(self, stampbase, file_name, taskname, extrainfo):
|
||||||
@@ -62,10 +63,11 @@ class SignatureGenerator(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def get_taskdata(self):
|
def get_taskdata(self):
|
||||||
return (self.runtaskdeps, self.taskhash, self.file_checksum_values, self.taints, self.basehash)
|
return (self.runtaskdeps, self.taskhash, self.file_checksum_values)
|
||||||
|
|
||||||
def set_taskdata(self, data):
|
def set_taskdata(self, data):
|
||||||
self.runtaskdeps, self.taskhash, self.file_checksum_values, self.taints, self.basehash = data
|
self.runtaskdeps, self.taskhash, self.file_checksum_values = data
|
||||||
|
|
||||||
|
|
||||||
class SignatureGeneratorBasic(SignatureGenerator):
|
class SignatureGeneratorBasic(SignatureGenerator):
|
||||||
"""
|
"""
|
||||||
@@ -85,12 +87,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST", True) or "").split())
|
self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST", True) or "").split())
|
||||||
self.taskwhitelist = None
|
self.taskwhitelist = None
|
||||||
self.init_rundepcheck(data)
|
self.init_rundepcheck(data)
|
||||||
checksum_cache_file = data.getVar("BB_HASH_CHECKSUM_CACHE_FILE", True)
|
|
||||||
if checksum_cache_file:
|
|
||||||
self.checksum_cache = FileChecksumCache()
|
|
||||||
self.checksum_cache.init_cache(data, checksum_cache_file)
|
|
||||||
else:
|
|
||||||
self.checksum_cache = None
|
|
||||||
|
|
||||||
def init_rundepcheck(self, data):
|
def init_rundepcheck(self, data):
|
||||||
self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST", True) or None
|
self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST", True) or None
|
||||||
@@ -133,11 +129,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
var = lookupcache[dep]
|
var = lookupcache[dep]
|
||||||
if var is not None:
|
if var is not None:
|
||||||
data = data + str(var)
|
data = data + str(var)
|
||||||
datahash = hashlib.md5(data.encode("utf-8")).hexdigest()
|
self.basehash[fn + "." + task] = hashlib.md5(data).hexdigest()
|
||||||
k = fn + "." + task
|
|
||||||
if k in self.basehash and self.basehash[k] != datahash:
|
|
||||||
bb.error("When reparsing %s, the basehash value changed from %s to %s. The metadata is not deterministic and this needs to be fixed." % (k, self.basehash[k], datahash))
|
|
||||||
self.basehash[k] = datahash
|
|
||||||
taskdeps[task] = alldeps
|
taskdeps[task] = alldeps
|
||||||
|
|
||||||
self.taskdeps[fn] = taskdeps
|
self.taskdeps[fn] = taskdeps
|
||||||
@@ -148,14 +140,13 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
|
|
||||||
def finalise(self, fn, d, variant):
|
def finalise(self, fn, d, variant):
|
||||||
|
|
||||||
mc = d.getVar("__BBMULTICONFIG", False) or ""
|
if variant:
|
||||||
if variant or mc:
|
fn = "virtual:" + variant + ":" + fn
|
||||||
fn = bb.cache.realfn2virtual(fn, variant, mc)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
taskdeps = self._build_data(fn, d)
|
taskdeps = self._build_data(fn, d)
|
||||||
except:
|
except:
|
||||||
bb.warn("Error during finalise of %s" % fn)
|
bb.note("Error during finalise of %s" % fn)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
#Slow but can be useful for debugging mismatched basehashes
|
#Slow but can be useful for debugging mismatched basehashes
|
||||||
@@ -186,11 +177,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
def get_taskhash(self, fn, task, deps, dataCache):
|
def get_taskhash(self, fn, task, deps, dataCache):
|
||||||
k = fn + "." + task
|
k = fn + "." + task
|
||||||
data = dataCache.basetaskhash[k]
|
data = dataCache.basetaskhash[k]
|
||||||
self.basehash[k] = data
|
|
||||||
self.runtaskdeps[k] = []
|
self.runtaskdeps[k] = []
|
||||||
self.file_checksum_values[k] = []
|
self.file_checksum_values[k] = {}
|
||||||
recipename = dataCache.pkg_fn[fn]
|
recipename = dataCache.pkg_fn[fn]
|
||||||
|
|
||||||
for dep in sorted(deps, key=clean_basepath):
|
for dep in sorted(deps, key=clean_basepath):
|
||||||
depname = dataCache.pkg_fn[self.pkgnameextract.search(dep).group('fn')]
|
depname = dataCache.pkg_fn[self.pkgnameextract.search(dep).group('fn')]
|
||||||
if not self.rundep_check(fn, recipename, task, dep, depname, dataCache):
|
if not self.rundep_check(fn, recipename, task, dep, depname, dataCache):
|
||||||
@@ -201,12 +190,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
self.runtaskdeps[k].append(dep)
|
self.runtaskdeps[k].append(dep)
|
||||||
|
|
||||||
if task in dataCache.file_checksums[fn]:
|
if task in dataCache.file_checksums[fn]:
|
||||||
if self.checksum_cache:
|
checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
|
||||||
checksums = self.checksum_cache.get_checksums(dataCache.file_checksums[fn][task], recipename)
|
|
||||||
else:
|
|
||||||
checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
|
|
||||||
for (f,cs) in checksums:
|
for (f,cs) in checksums:
|
||||||
self.file_checksum_values[k].append((f,cs))
|
self.file_checksum_values[k][f] = cs
|
||||||
if cs:
|
if cs:
|
||||||
data = data + cs
|
data = data + cs
|
||||||
|
|
||||||
@@ -222,29 +208,17 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
if taint:
|
if taint:
|
||||||
data = data + taint
|
data = data + taint
|
||||||
self.taints[k] = taint
|
self.taints[k] = taint
|
||||||
logger.warning("%s is tainted from a forced run" % k)
|
logger.warn("%s is tainted from a forced run" % k)
|
||||||
|
|
||||||
h = hashlib.md5(data.encode("utf-8")).hexdigest()
|
h = hashlib.md5(data).hexdigest()
|
||||||
self.taskhash[k] = h
|
self.taskhash[k] = h
|
||||||
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def writeout_file_checksum_cache(self):
|
|
||||||
"""Write/update the file checksum cache onto disk"""
|
|
||||||
if self.checksum_cache:
|
|
||||||
self.checksum_cache.save_extras()
|
|
||||||
self.checksum_cache.save_merge()
|
|
||||||
else:
|
|
||||||
bb.fetch2.fetcher_parse_save()
|
|
||||||
bb.fetch2.fetcher_parse_done()
|
|
||||||
|
|
||||||
def dump_sigtask(self, fn, task, stampbase, runtime):
|
def dump_sigtask(self, fn, task, stampbase, runtime):
|
||||||
|
|
||||||
k = fn + "." + task
|
k = fn + "." + task
|
||||||
referencestamp = stampbase
|
if runtime == "customfile":
|
||||||
if isinstance(runtime, str) and runtime.startswith("customfile"):
|
|
||||||
sigfile = stampbase
|
sigfile = stampbase
|
||||||
referencestamp = runtime[11:]
|
|
||||||
elif runtime and k in self.taskhash:
|
elif runtime and k in self.taskhash:
|
||||||
sigfile = stampbase + "." + task + ".sigdata" + "." + self.taskhash[k]
|
sigfile = stampbase + "." + task + ".sigdata" + "." + self.taskhash[k]
|
||||||
else:
|
else:
|
||||||
@@ -253,7 +227,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
bb.utils.mkdirhier(os.path.dirname(sigfile))
|
bb.utils.mkdirhier(os.path.dirname(sigfile))
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
data['task'] = task
|
|
||||||
data['basewhitelist'] = self.basewhitelist
|
data['basewhitelist'] = self.basewhitelist
|
||||||
data['taskwhitelist'] = self.taskwhitelist
|
data['taskwhitelist'] = self.taskwhitelist
|
||||||
data['taskdeps'] = self.taskdeps[fn][task]
|
data['taskdeps'] = self.taskdeps[fn][task]
|
||||||
@@ -269,13 +242,12 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
|
|
||||||
if runtime and k in self.taskhash:
|
if runtime and k in self.taskhash:
|
||||||
data['runtaskdeps'] = self.runtaskdeps[k]
|
data['runtaskdeps'] = self.runtaskdeps[k]
|
||||||
data['file_checksum_values'] = [(os.path.basename(f), cs) for f,cs in self.file_checksum_values[k]]
|
data['file_checksum_values'] = [(os.path.basename(f), cs) for f,cs in self.file_checksum_values[k].items()]
|
||||||
data['runtaskhashes'] = {}
|
data['runtaskhashes'] = {}
|
||||||
for dep in data['runtaskdeps']:
|
for dep in data['runtaskdeps']:
|
||||||
data['runtaskhashes'][dep] = self.taskhash[dep]
|
data['runtaskhashes'][dep] = self.taskhash[dep]
|
||||||
data['taskhash'] = self.taskhash[k]
|
|
||||||
|
|
||||||
taint = self.read_taint(fn, task, referencestamp)
|
taint = self.read_taint(fn, task, stampbase)
|
||||||
if taint:
|
if taint:
|
||||||
data['taint'] = taint
|
data['taint'] = taint
|
||||||
|
|
||||||
@@ -283,21 +255,12 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
if 'nostamp:' in self.taints[k]:
|
if 'nostamp:' in self.taints[k]:
|
||||||
data['taint'] = self.taints[k]
|
data['taint'] = self.taints[k]
|
||||||
|
|
||||||
computed_basehash = calc_basehash(data)
|
|
||||||
if computed_basehash != self.basehash[k]:
|
|
||||||
bb.error("Basehash mismatch %s versus %s for %s" % (computed_basehash, self.basehash[k], k))
|
|
||||||
if runtime and k in self.taskhash:
|
|
||||||
computed_taskhash = calc_taskhash(data)
|
|
||||||
if computed_taskhash != self.taskhash[k]:
|
|
||||||
bb.error("Taskhash mismatch %s versus %s for %s" % (computed_taskhash, self.taskhash[k], k))
|
|
||||||
sigfile = sigfile.replace(self.taskhash[k], computed_taskhash)
|
|
||||||
|
|
||||||
fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(sigfile), prefix="sigtask.")
|
fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(sigfile), prefix="sigtask.")
|
||||||
try:
|
try:
|
||||||
with os.fdopen(fd, "wb") as stream:
|
with os.fdopen(fd, "wb") as stream:
|
||||||
p = pickle.dump(data, stream, -1)
|
p = pickle.dump(data, stream, -1)
|
||||||
stream.flush()
|
stream.flush()
|
||||||
os.chmod(tmpfile, 0o664)
|
os.chmod(tmpfile, 0664)
|
||||||
os.rename(tmpfile, sigfile)
|
os.rename(tmpfile, sigfile)
|
||||||
except (OSError, IOError) as err:
|
except (OSError, IOError) as err:
|
||||||
try:
|
try:
|
||||||
@@ -306,18 +269,16 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
|||||||
pass
|
pass
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
def dump_sigs(self, dataCaches, options):
|
def dump_sigs(self, dataCache, options):
|
||||||
for fn in self.taskdeps:
|
for fn in self.taskdeps:
|
||||||
for task in self.taskdeps[fn]:
|
for task in self.taskdeps[fn]:
|
||||||
tid = fn + ":" + task
|
|
||||||
(mc, _, _) = bb.runqueue.split_tid(tid)
|
|
||||||
k = fn + "." + task
|
k = fn + "." + task
|
||||||
if k not in self.taskhash:
|
if k not in self.taskhash:
|
||||||
continue
|
continue
|
||||||
if dataCaches[mc].basetaskhash[k] != self.basehash[k]:
|
if dataCache.basetaskhash[k] != self.basehash[k]:
|
||||||
bb.error("Bitbake's cached basehash does not match the one we just generated (%s)!" % k)
|
bb.error("Bitbake's cached basehash does not match the one we just generated (%s)!" % k)
|
||||||
bb.error("The mismatched hashes were %s and %s" % (dataCaches[mc].basetaskhash[k], self.basehash[k]))
|
bb.error("The mismatched hashes were %s and %s" % (dataCache.basetaskhash[k], self.basehash[k]))
|
||||||
self.dump_sigtask(fn, task, dataCaches[mc].stamp[fn], True)
|
self.dump_sigtask(fn, task, dataCache.stamp[fn], True)
|
||||||
|
|
||||||
class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
|
class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
|
||||||
name = "basichash"
|
name = "basichash"
|
||||||
@@ -347,18 +308,12 @@ def dump_this_task(outfile, d):
|
|||||||
import bb.parse
|
import bb.parse
|
||||||
fn = d.getVar("BB_FILENAME", True)
|
fn = d.getVar("BB_FILENAME", True)
|
||||||
task = "do_" + d.getVar("BB_CURRENTTASK", True)
|
task = "do_" + d.getVar("BB_CURRENTTASK", True)
|
||||||
referencestamp = bb.build.stamp_internal(task, d, None, True)
|
bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile")
|
||||||
bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile:" + referencestamp)
|
|
||||||
|
|
||||||
def clean_basepath(a):
|
def clean_basepath(a):
|
||||||
mc = None
|
|
||||||
if a.startswith("multiconfig:"):
|
|
||||||
_, mc, a = a.split(":", 2)
|
|
||||||
b = a.rsplit("/", 2)[1] + a.rsplit("/", 2)[2]
|
b = a.rsplit("/", 2)[1] + a.rsplit("/", 2)[2]
|
||||||
if a.startswith("virtual:"):
|
if a.startswith("virtual:"):
|
||||||
b = b + ":" + a.rsplit(":", 1)[0]
|
b = b + ":" + a.rsplit(":", 1)[0]
|
||||||
if mc:
|
|
||||||
b = b + ":multiconfig:" + mc
|
|
||||||
return b
|
return b
|
||||||
|
|
||||||
def clean_basepaths(a):
|
def clean_basepaths(a):
|
||||||
@@ -376,12 +331,10 @@ def clean_basepaths_list(a):
|
|||||||
def compare_sigfiles(a, b, recursecb = None):
|
def compare_sigfiles(a, b, recursecb = None):
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
with open(a, 'rb') as f:
|
p1 = pickle.Unpickler(open(a, "rb"))
|
||||||
p1 = pickle.Unpickler(f)
|
a_data = p1.load()
|
||||||
a_data = p1.load()
|
p2 = pickle.Unpickler(open(b, "rb"))
|
||||||
with open(b, 'rb') as f:
|
b_data = p2.load()
|
||||||
p2 = pickle.Unpickler(f)
|
|
||||||
b_data = p2.load()
|
|
||||||
|
|
||||||
def dict_diff(a, b, whitelist=set()):
|
def dict_diff(a, b, whitelist=set()):
|
||||||
sa = set(a.keys())
|
sa = set(a.keys())
|
||||||
@@ -463,11 +416,6 @@ def compare_sigfiles(a, b, recursecb = None):
|
|||||||
for dep in changed:
|
for dep in changed:
|
||||||
output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
|
output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
|
||||||
|
|
||||||
if not 'file_checksum_values' in a_data:
|
|
||||||
a_data['file_checksum_values'] = {}
|
|
||||||
if not 'file_checksum_values' in b_data:
|
|
||||||
b_data['file_checksum_values'] = {}
|
|
||||||
|
|
||||||
changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
|
changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
|
||||||
if changed:
|
if changed:
|
||||||
for f, old, new in changed:
|
for f, old, new in changed:
|
||||||
@@ -479,10 +427,6 @@ def compare_sigfiles(a, b, recursecb = None):
|
|||||||
for f in removed:
|
for f in removed:
|
||||||
output.append("Dependency on checksum of file %s was removed" % (f))
|
output.append("Dependency on checksum of file %s was removed" % (f))
|
||||||
|
|
||||||
if not 'runtaskdeps' in a_data:
|
|
||||||
a_data['runtaskdeps'] = {}
|
|
||||||
if not 'runtaskdeps' in b_data:
|
|
||||||
b_data['runtaskdeps'] = {}
|
|
||||||
|
|
||||||
if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
|
if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
|
||||||
changed = ["Number of task dependencies changed"]
|
changed = ["Number of task dependencies changed"]
|
||||||
@@ -541,47 +485,11 @@ def compare_sigfiles(a, b, recursecb = None):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def calc_basehash(sigdata):
|
|
||||||
task = sigdata['task']
|
|
||||||
basedata = sigdata['varvals'][task]
|
|
||||||
|
|
||||||
if basedata is None:
|
|
||||||
basedata = ''
|
|
||||||
|
|
||||||
alldeps = sigdata['taskdeps']
|
|
||||||
for dep in alldeps:
|
|
||||||
basedata = basedata + dep
|
|
||||||
val = sigdata['varvals'][dep]
|
|
||||||
if val is not None:
|
|
||||||
basedata = basedata + str(val)
|
|
||||||
|
|
||||||
return hashlib.md5(basedata.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
def calc_taskhash(sigdata):
|
|
||||||
data = sigdata['basehash']
|
|
||||||
|
|
||||||
for dep in sigdata['runtaskdeps']:
|
|
||||||
data = data + sigdata['runtaskhashes'][dep]
|
|
||||||
|
|
||||||
for c in sigdata['file_checksum_values']:
|
|
||||||
if c[1]:
|
|
||||||
data = data + c[1]
|
|
||||||
|
|
||||||
if 'taint' in sigdata:
|
|
||||||
if 'nostamp:' in sigdata['taint']:
|
|
||||||
data = data + sigdata['taint'][8:]
|
|
||||||
else:
|
|
||||||
data = data + sigdata['taint']
|
|
||||||
|
|
||||||
return hashlib.md5(data.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def dump_sigfile(a):
|
def dump_sigfile(a):
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
with open(a, 'rb') as f:
|
p1 = pickle.Unpickler(open(a, "rb"))
|
||||||
p1 = pickle.Unpickler(f)
|
a_data = p1.load()
|
||||||
a_data = p1.load()
|
|
||||||
|
|
||||||
output.append("basewhitelist: %s" % (a_data['basewhitelist']))
|
output.append("basewhitelist: %s" % (a_data['basewhitelist']))
|
||||||
|
|
||||||
@@ -610,13 +518,17 @@ def dump_sigfile(a):
|
|||||||
if 'taint' in a_data:
|
if 'taint' in a_data:
|
||||||
output.append("Tainted (by forced/invalidated task): %s" % a_data['taint'])
|
output.append("Tainted (by forced/invalidated task): %s" % a_data['taint'])
|
||||||
|
|
||||||
if 'task' in a_data:
|
data = a_data['basehash']
|
||||||
computed_basehash = calc_basehash(a_data)
|
for dep in a_data['runtaskdeps']:
|
||||||
output.append("Computed base hash is %s and from file %s" % (computed_basehash, a_data['basehash']))
|
data = data + a_data['runtaskhashes'][dep]
|
||||||
else:
|
|
||||||
output.append("Unable to compute base hash")
|
|
||||||
|
|
||||||
computed_taskhash = calc_taskhash(a_data)
|
for c in a_data['file_checksum_values']:
|
||||||
output.append("Computed task hash is %s" % computed_taskhash)
|
data = data + c[1]
|
||||||
|
|
||||||
|
if 'taint' in a_data:
|
||||||
|
data = data + a_data['taint']
|
||||||
|
|
||||||
|
h = hashlib.md5(data).hexdigest()
|
||||||
|
output.append("Computed Hash is %s" % h)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -37,24 +37,27 @@ def re_match_strings(target, strings):
|
|||||||
return any(name == target or re.match(name, target)
|
return any(name == target or re.match(name, target)
|
||||||
for name in strings)
|
for name in strings)
|
||||||
|
|
||||||
class TaskEntry:
|
|
||||||
def __init__(self):
|
|
||||||
self.tdepends = []
|
|
||||||
self.idepends = []
|
|
||||||
self.irdepends = []
|
|
||||||
|
|
||||||
class TaskData:
|
class TaskData:
|
||||||
"""
|
"""
|
||||||
BitBake Task Data implementation
|
BitBake Task Data implementation
|
||||||
"""
|
"""
|
||||||
def __init__(self, abort = True, tryaltconfigs = False, skiplist = None, allowincomplete = False):
|
def __init__(self, abort = True, tryaltconfigs = False, skiplist = None, allowincomplete = False):
|
||||||
|
self.build_names_index = []
|
||||||
|
self.run_names_index = []
|
||||||
|
self.fn_index = []
|
||||||
|
|
||||||
self.build_targets = {}
|
self.build_targets = {}
|
||||||
self.run_targets = {}
|
self.run_targets = {}
|
||||||
|
|
||||||
self.external_targets = []
|
self.external_targets = []
|
||||||
|
|
||||||
self.seenfns = []
|
self.tasks_fnid = []
|
||||||
self.taskentries = {}
|
self.tasks_name = []
|
||||||
|
self.tasks_tdepends = []
|
||||||
|
self.tasks_idepends = []
|
||||||
|
self.tasks_irdepends = []
|
||||||
|
# Cache to speed up task ID lookups
|
||||||
|
self.tasks_lookup = {}
|
||||||
|
|
||||||
self.depids = {}
|
self.depids = {}
|
||||||
self.rdepids = {}
|
self.rdepids = {}
|
||||||
@@ -63,7 +66,7 @@ class TaskData:
|
|||||||
|
|
||||||
self.failed_deps = []
|
self.failed_deps = []
|
||||||
self.failed_rdeps = []
|
self.failed_rdeps = []
|
||||||
self.failed_fns = []
|
self.failed_fnids = []
|
||||||
|
|
||||||
self.abort = abort
|
self.abort = abort
|
||||||
self.tryaltconfigs = tryaltconfigs
|
self.tryaltconfigs = tryaltconfigs
|
||||||
@@ -71,6 +74,88 @@ class TaskData:
|
|||||||
|
|
||||||
self.skiplist = skiplist
|
self.skiplist = skiplist
|
||||||
|
|
||||||
|
def getbuild_id(self, name):
|
||||||
|
"""
|
||||||
|
Return an ID number for the build target name.
|
||||||
|
If it doesn't exist, create one.
|
||||||
|
"""
|
||||||
|
if not name in self.build_names_index:
|
||||||
|
self.build_names_index.append(name)
|
||||||
|
return len(self.build_names_index) - 1
|
||||||
|
|
||||||
|
return self.build_names_index.index(name)
|
||||||
|
|
||||||
|
def getrun_id(self, name):
|
||||||
|
"""
|
||||||
|
Return an ID number for the run target name.
|
||||||
|
If it doesn't exist, create one.
|
||||||
|
"""
|
||||||
|
if not name in self.run_names_index:
|
||||||
|
self.run_names_index.append(name)
|
||||||
|
return len(self.run_names_index) - 1
|
||||||
|
|
||||||
|
return self.run_names_index.index(name)
|
||||||
|
|
||||||
|
def getfn_id(self, name):
|
||||||
|
"""
|
||||||
|
Return an ID number for the filename.
|
||||||
|
If it doesn't exist, create one.
|
||||||
|
"""
|
||||||
|
if not name in self.fn_index:
|
||||||
|
self.fn_index.append(name)
|
||||||
|
return len(self.fn_index) - 1
|
||||||
|
|
||||||
|
return self.fn_index.index(name)
|
||||||
|
|
||||||
|
def gettask_ids(self, fnid):
|
||||||
|
"""
|
||||||
|
Return an array of the ID numbers matching a given fnid.
|
||||||
|
"""
|
||||||
|
ids = []
|
||||||
|
if fnid in self.tasks_lookup:
|
||||||
|
for task in self.tasks_lookup[fnid]:
|
||||||
|
ids.append(self.tasks_lookup[fnid][task])
|
||||||
|
return ids
|
||||||
|
|
||||||
|
def gettask_id_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.
|
||||||
|
If it doesn't exist, create one by default.
|
||||||
|
Optionally return None instead.
|
||||||
|
"""
|
||||||
|
fnid = self.getfn_id(fn)
|
||||||
|
|
||||||
|
if fnid in self.tasks_lookup:
|
||||||
|
if task in self.tasks_lookup[fnid]:
|
||||||
|
return self.tasks_lookup[fnid][task]
|
||||||
|
|
||||||
|
if not create:
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.tasks_name.append(task)
|
||||||
|
self.tasks_fnid.append(fnid)
|
||||||
|
self.tasks_tdepends.append([])
|
||||||
|
self.tasks_idepends.append([])
|
||||||
|
self.tasks_irdepends.append([])
|
||||||
|
|
||||||
|
listid = len(self.tasks_name) - 1
|
||||||
|
|
||||||
|
if fnid not in self.tasks_lookup:
|
||||||
|
self.tasks_lookup[fnid] = {}
|
||||||
|
self.tasks_lookup[fnid][task] = listid
|
||||||
|
|
||||||
|
return listid
|
||||||
|
|
||||||
def add_tasks(self, fn, dataCache):
|
def add_tasks(self, fn, dataCache):
|
||||||
"""
|
"""
|
||||||
Add tasks for a given fn to the database
|
Add tasks for a given fn to the database
|
||||||
@@ -78,31 +163,27 @@ class TaskData:
|
|||||||
|
|
||||||
task_deps = dataCache.task_deps[fn]
|
task_deps = dataCache.task_deps[fn]
|
||||||
|
|
||||||
if fn in self.failed_fns:
|
fnid = self.getfn_id(fn)
|
||||||
|
|
||||||
|
if fnid in self.failed_fnids:
|
||||||
bb.msg.fatal("TaskData", "Trying to re-add a failed file? Something is broken...")
|
bb.msg.fatal("TaskData", "Trying to re-add a failed file? Something is broken...")
|
||||||
|
|
||||||
# Check if we've already seen this fn
|
# Check if we've already seen this fn
|
||||||
if fn in self.seenfns:
|
if fnid in self.tasks_fnid:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.seenfns.append(fn)
|
|
||||||
|
|
||||||
self.add_extra_deps(fn, dataCache)
|
|
||||||
|
|
||||||
for task in task_deps['tasks']:
|
for task in task_deps['tasks']:
|
||||||
|
|
||||||
tid = "%s:%s" % (fn, task)
|
|
||||||
self.taskentries[tid] = TaskEntry()
|
|
||||||
|
|
||||||
# Work out task dependencies
|
# Work out task dependencies
|
||||||
parentids = []
|
parentids = []
|
||||||
for dep in task_deps['parents'][task]:
|
for dep in task_deps['parents'][task]:
|
||||||
if dep not in task_deps['tasks']:
|
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))
|
bb.debug(2, "Not adding dependeny of %s on %s since %s does not exist" % (task, dep, dep))
|
||||||
continue
|
continue
|
||||||
parentid = "%s:%s" % (fn, dep)
|
parentid = self.gettask_id(fn, dep)
|
||||||
parentids.append(parentid)
|
parentids.append(parentid)
|
||||||
self.taskentries[tid].tdepends.extend(parentids)
|
taskid = self.gettask_id(fn, task)
|
||||||
|
self.tasks_tdepends[taskid].extend(parentids)
|
||||||
|
|
||||||
# Touch all intertask dependencies
|
# Touch all intertask dependencies
|
||||||
if 'depends' in task_deps and task in task_deps['depends']:
|
if 'depends' in task_deps and task in task_deps['depends']:
|
||||||
@@ -111,30 +192,29 @@ class TaskData:
|
|||||||
if dep:
|
if dep:
|
||||||
if ":" not in dep:
|
if ":" not in dep:
|
||||||
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))
|
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(((dep.split(":")[0]), dep.split(":")[1]))
|
ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
|
||||||
self.seen_build_target(dep.split(":")[0])
|
self.tasks_idepends[taskid].extend(ids)
|
||||||
self.taskentries[tid].idepends.extend(ids)
|
|
||||||
if 'rdepends' in task_deps and task in task_deps['rdepends']:
|
if 'rdepends' in task_deps and task in task_deps['rdepends']:
|
||||||
ids = []
|
ids = []
|
||||||
for dep in task_deps['rdepends'][task].split():
|
for dep in task_deps['rdepends'][task].split():
|
||||||
if dep:
|
if dep:
|
||||||
if ":" not in 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))
|
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(((dep.split(":")[0]), dep.split(":")[1]))
|
ids.append(((self.getrun_id(dep.split(":")[0])), dep.split(":")[1]))
|
||||||
self.seen_run_target(dep.split(":")[0])
|
self.tasks_irdepends[taskid].extend(ids)
|
||||||
self.taskentries[tid].irdepends.extend(ids)
|
|
||||||
|
|
||||||
# Work out build dependencies
|
# Work out build dependencies
|
||||||
if not fn in self.depids:
|
if not fnid in self.depids:
|
||||||
dependids = set()
|
dependids = {}
|
||||||
for depend in dataCache.deps[fn]:
|
for depend in dataCache.deps[fn]:
|
||||||
dependids.add(depend)
|
dependids[self.getbuild_id(depend)] = None
|
||||||
self.depids[fn] = list(dependids)
|
self.depids[fnid] = dependids.keys()
|
||||||
logger.debug(2, "Added dependencies %s for %s", str(dataCache.deps[fn]), fn)
|
logger.debug(2, "Added dependencies %s for %s", str(dataCache.deps[fn]), fn)
|
||||||
|
|
||||||
# Work out runtime dependencies
|
# Work out runtime dependencies
|
||||||
if not fn in self.rdepids:
|
if not fnid in self.rdepids:
|
||||||
rdependids = set()
|
rdependids = {}
|
||||||
rdepends = dataCache.rundeps[fn]
|
rdepends = dataCache.rundeps[fn]
|
||||||
rrecs = dataCache.runrecs[fn]
|
rrecs = dataCache.runrecs[fn]
|
||||||
rdependlist = []
|
rdependlist = []
|
||||||
@@ -142,48 +222,33 @@ class TaskData:
|
|||||||
for package in rdepends:
|
for package in rdepends:
|
||||||
for rdepend in rdepends[package]:
|
for rdepend in rdepends[package]:
|
||||||
rdependlist.append(rdepend)
|
rdependlist.append(rdepend)
|
||||||
rdependids.add(rdepend)
|
rdependids[self.getrun_id(rdepend)] = None
|
||||||
for package in rrecs:
|
for package in rrecs:
|
||||||
for rdepend in rrecs[package]:
|
for rdepend in rrecs[package]:
|
||||||
rreclist.append(rdepend)
|
rreclist.append(rdepend)
|
||||||
rdependids.add(rdepend)
|
rdependids[self.getrun_id(rdepend)] = None
|
||||||
if rdependlist:
|
if rdependlist:
|
||||||
logger.debug(2, "Added runtime dependencies %s for %s", str(rdependlist), fn)
|
logger.debug(2, "Added runtime dependencies %s for %s", str(rdependlist), fn)
|
||||||
if rreclist:
|
if rreclist:
|
||||||
logger.debug(2, "Added runtime recommendations %s for %s", str(rreclist), fn)
|
logger.debug(2, "Added runtime recommendations %s for %s", str(rreclist), fn)
|
||||||
self.rdepids[fn] = list(rdependids)
|
self.rdepids[fnid] = rdependids.keys()
|
||||||
|
|
||||||
for dep in self.depids[fn]:
|
for dep in self.depids[fnid]:
|
||||||
self.seen_build_target(dep)
|
|
||||||
if dep in self.failed_deps:
|
if dep in self.failed_deps:
|
||||||
self.fail_fn(fn)
|
self.fail_fnid(fnid)
|
||||||
return
|
return
|
||||||
for dep in self.rdepids[fn]:
|
for dep in self.rdepids[fnid]:
|
||||||
self.seen_run_target(dep)
|
|
||||||
if dep in self.failed_rdeps:
|
if dep in self.failed_rdeps:
|
||||||
self.fail_fn(fn)
|
self.fail_fnid(fnid)
|
||||||
return
|
return
|
||||||
|
|
||||||
def add_extra_deps(self, fn, dataCache):
|
|
||||||
func = dataCache.extradepsfunc.get(fn, None)
|
|
||||||
if func:
|
|
||||||
bb.providers.buildWorldTargetList(dataCache)
|
|
||||||
pn = dataCache.pkg_fn[fn]
|
|
||||||
params = {'deps': dataCache.deps[fn],
|
|
||||||
'world_target': dataCache.world_target,
|
|
||||||
'pkg_pn': dataCache.pkg_pn,
|
|
||||||
'self_pn': pn}
|
|
||||||
funcname = '_%s_calculate_extra_depends' % pn.replace('-', '_')
|
|
||||||
paramlist = ','.join(params.keys())
|
|
||||||
func = 'def %s(%s):\n%s\n\n%s(%s)' % (funcname, paramlist, func, funcname, paramlist)
|
|
||||||
bb.utils.better_exec(func, params)
|
|
||||||
|
|
||||||
|
|
||||||
def have_build_target(self, target):
|
def have_build_target(self, target):
|
||||||
"""
|
"""
|
||||||
Have we a build target matching this name?
|
Have we a build target matching this name?
|
||||||
"""
|
"""
|
||||||
if target in self.build_targets and self.build_targets[target]:
|
targetid = self.getbuild_id(target)
|
||||||
|
|
||||||
|
if targetid in self.build_targets:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -191,54 +256,50 @@ class TaskData:
|
|||||||
"""
|
"""
|
||||||
Have we a runtime target matching this name?
|
Have we a runtime target matching this name?
|
||||||
"""
|
"""
|
||||||
if target in self.run_targets and self.run_targets[target]:
|
targetid = self.getrun_id(target)
|
||||||
|
|
||||||
|
if targetid in self.run_targets:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def seen_build_target(self, name):
|
|
||||||
"""
|
|
||||||
Maintain a list of build targets
|
|
||||||
"""
|
|
||||||
if name not in self.build_targets:
|
|
||||||
self.build_targets[name] = []
|
|
||||||
|
|
||||||
def add_build_target(self, fn, item):
|
def add_build_target(self, fn, item):
|
||||||
"""
|
"""
|
||||||
Add a build target.
|
Add a build target.
|
||||||
If already present, append the provider fn to the list
|
If already present, append the provider fn to the list
|
||||||
"""
|
"""
|
||||||
if item in self.build_targets:
|
targetid = self.getbuild_id(item)
|
||||||
if fn in self.build_targets[item]:
|
fnid = self.getfn_id(fn)
|
||||||
return
|
|
||||||
self.build_targets[item].append(fn)
|
|
||||||
return
|
|
||||||
self.build_targets[item] = [fn]
|
|
||||||
|
|
||||||
def seen_run_target(self, name):
|
if targetid in self.build_targets:
|
||||||
"""
|
if fnid in self.build_targets[targetid]:
|
||||||
Maintain a list of runtime build targets
|
return
|
||||||
"""
|
self.build_targets[targetid].append(fnid)
|
||||||
if name not in self.run_targets:
|
return
|
||||||
self.run_targets[name] = []
|
self.build_targets[targetid] = [fnid]
|
||||||
|
|
||||||
def add_runtime_target(self, fn, item):
|
def add_runtime_target(self, fn, item):
|
||||||
"""
|
"""
|
||||||
Add a runtime target.
|
Add a runtime target.
|
||||||
If already present, append the provider fn to the list
|
If already present, append the provider fn to the list
|
||||||
"""
|
"""
|
||||||
if item in self.run_targets:
|
targetid = self.getrun_id(item)
|
||||||
if fn in self.run_targets[item]:
|
fnid = self.getfn_id(fn)
|
||||||
return
|
|
||||||
self.run_targets[item].append(fn)
|
|
||||||
return
|
|
||||||
self.run_targets[item] = [fn]
|
|
||||||
|
|
||||||
def mark_external_target(self, target):
|
if targetid in self.run_targets:
|
||||||
|
if fnid in self.run_targets[targetid]:
|
||||||
|
return
|
||||||
|
self.run_targets[targetid].append(fnid)
|
||||||
|
return
|
||||||
|
self.run_targets[targetid] = [fnid]
|
||||||
|
|
||||||
|
def mark_external_target(self, item):
|
||||||
"""
|
"""
|
||||||
Mark a build target as being externally requested
|
Mark a build target as being externally requested
|
||||||
"""
|
"""
|
||||||
if target not in self.external_targets:
|
targetid = self.getbuild_id(item)
|
||||||
self.external_targets.append(target)
|
|
||||||
|
if targetid not in self.external_targets:
|
||||||
|
self.external_targets.append(targetid)
|
||||||
|
|
||||||
def get_unresolved_build_targets(self, dataCache):
|
def get_unresolved_build_targets(self, dataCache):
|
||||||
"""
|
"""
|
||||||
@@ -246,12 +307,12 @@ class TaskData:
|
|||||||
are unknown.
|
are unknown.
|
||||||
"""
|
"""
|
||||||
unresolved = []
|
unresolved = []
|
||||||
for target in self.build_targets:
|
for target in self.build_names_index:
|
||||||
if re_match_strings(target, dataCache.ignored_dependencies):
|
if re_match_strings(target, dataCache.ignored_dependencies):
|
||||||
continue
|
continue
|
||||||
if target in self.failed_deps:
|
if self.build_names_index.index(target) in self.failed_deps:
|
||||||
continue
|
continue
|
||||||
if not self.build_targets[target]:
|
if not self.have_build_target(target):
|
||||||
unresolved.append(target)
|
unresolved.append(target)
|
||||||
return unresolved
|
return unresolved
|
||||||
|
|
||||||
@@ -261,12 +322,12 @@ class TaskData:
|
|||||||
are unknown.
|
are unknown.
|
||||||
"""
|
"""
|
||||||
unresolved = []
|
unresolved = []
|
||||||
for target in self.run_targets:
|
for target in self.run_names_index:
|
||||||
if re_match_strings(target, dataCache.ignored_dependencies):
|
if re_match_strings(target, dataCache.ignored_dependencies):
|
||||||
continue
|
continue
|
||||||
if target in self.failed_rdeps:
|
if self.run_names_index.index(target) in self.failed_rdeps:
|
||||||
continue
|
continue
|
||||||
if not self.run_targets[target]:
|
if not self.have_runtime_target(target):
|
||||||
unresolved.append(target)
|
unresolved.append(target)
|
||||||
return unresolved
|
return unresolved
|
||||||
|
|
||||||
@@ -274,26 +335,50 @@ class TaskData:
|
|||||||
"""
|
"""
|
||||||
Return a list of providers of item
|
Return a list of providers of item
|
||||||
"""
|
"""
|
||||||
return self.build_targets[item]
|
targetid = self.getbuild_id(item)
|
||||||
|
|
||||||
def get_dependees(self, item):
|
return self.build_targets[targetid]
|
||||||
|
|
||||||
|
def get_dependees(self, itemid):
|
||||||
"""
|
"""
|
||||||
Return a list of targets which depend on item
|
Return a list of targets which depend on item
|
||||||
"""
|
"""
|
||||||
dependees = []
|
dependees = []
|
||||||
for fn in self.depids:
|
for fnid in self.depids:
|
||||||
if item in self.depids[fn]:
|
if itemid in self.depids[fnid]:
|
||||||
dependees.append(fn)
|
dependees.append(fnid)
|
||||||
return dependees
|
return dependees
|
||||||
|
|
||||||
def get_rdependees(self, item):
|
def get_dependees_str(self, item):
|
||||||
|
"""
|
||||||
|
Return a list of targets which depend on item as a user readable string
|
||||||
|
"""
|
||||||
|
itemid = self.getbuild_id(item)
|
||||||
|
dependees = []
|
||||||
|
for fnid in self.depids:
|
||||||
|
if itemid in self.depids[fnid]:
|
||||||
|
dependees.append(self.fn_index[fnid])
|
||||||
|
return dependees
|
||||||
|
|
||||||
|
def get_rdependees(self, itemid):
|
||||||
"""
|
"""
|
||||||
Return a list of targets which depend on runtime item
|
Return a list of targets which depend on runtime item
|
||||||
"""
|
"""
|
||||||
dependees = []
|
dependees = []
|
||||||
for fn in self.rdepids:
|
for fnid in self.rdepids:
|
||||||
if item in self.rdepids[fn]:
|
if itemid in self.rdepids[fnid]:
|
||||||
dependees.append(fn)
|
dependees.append(fnid)
|
||||||
|
return dependees
|
||||||
|
|
||||||
|
def get_rdependees_str(self, item):
|
||||||
|
"""
|
||||||
|
Return a list of targets which depend on runtime item as a user readable string
|
||||||
|
"""
|
||||||
|
itemid = self.getrun_id(item)
|
||||||
|
dependees = []
|
||||||
|
for fnid in self.rdepids:
|
||||||
|
if itemid in self.rdepids[fnid]:
|
||||||
|
dependees.append(self.fn_index[fnid])
|
||||||
return dependees
|
return dependees
|
||||||
|
|
||||||
def get_reasons(self, item, runtime=False):
|
def get_reasons(self, item, runtime=False):
|
||||||
@@ -329,7 +414,7 @@ class TaskData:
|
|||||||
except bb.providers.NoProvider:
|
except bb.providers.NoProvider:
|
||||||
if self.abort:
|
if self.abort:
|
||||||
raise
|
raise
|
||||||
self.remove_buildtarget(item)
|
self.remove_buildtarget(self.getbuild_id(item))
|
||||||
|
|
||||||
self.mark_external_target(item)
|
self.mark_external_target(item)
|
||||||
|
|
||||||
@@ -344,14 +429,7 @@ class TaskData:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not item in dataCache.providers:
|
if not item in dataCache.providers:
|
||||||
close_matches = self.get_close_matches(item, list(dataCache.providers.keys()))
|
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item), close_matches=self.get_close_matches(item, dataCache.providers.keys())), cfgData)
|
||||||
# Is it in RuntimeProviders ?
|
|
||||||
all_p = bb.providers.getRuntimeProviders(dataCache, item)
|
|
||||||
for fn in all_p:
|
|
||||||
new = dataCache.pkg_fn[fn] + " RPROVIDES " + item
|
|
||||||
if new not in close_matches:
|
|
||||||
close_matches.append(new)
|
|
||||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees(item), reasons=self.get_reasons(item), close_matches=close_matches), cfgData)
|
|
||||||
raise bb.providers.NoProvider(item)
|
raise bb.providers.NoProvider(item)
|
||||||
|
|
||||||
if self.have_build_target(item):
|
if self.have_build_target(item):
|
||||||
@@ -360,10 +438,10 @@ class TaskData:
|
|||||||
all_p = dataCache.providers[item]
|
all_p = dataCache.providers[item]
|
||||||
|
|
||||||
eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
|
eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
|
||||||
eligible = [p for p in eligible if not p in self.failed_fns]
|
eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
|
||||||
|
|
||||||
if not eligible:
|
if not eligible:
|
||||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees(item), reasons=["No eligible PROVIDERs exist for '%s'" % item]), cfgData)
|
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=["No eligible PROVIDERs exist for '%s'" % item]), cfgData)
|
||||||
raise bb.providers.NoProvider(item)
|
raise bb.providers.NoProvider(item)
|
||||||
|
|
||||||
if len(eligible) > 1 and foundUnique == False:
|
if len(eligible) > 1 and foundUnique == False:
|
||||||
@@ -375,7 +453,8 @@ class TaskData:
|
|||||||
self.consider_msgs_cache.append(item)
|
self.consider_msgs_cache.append(item)
|
||||||
|
|
||||||
for fn in eligible:
|
for fn in eligible:
|
||||||
if fn in self.failed_fns:
|
fnid = self.getfn_id(fn)
|
||||||
|
if fnid in self.failed_fnids:
|
||||||
continue
|
continue
|
||||||
logger.debug(2, "adding %s to satisfy %s", fn, item)
|
logger.debug(2, "adding %s to satisfy %s", fn, item)
|
||||||
self.add_build_target(fn, item)
|
self.add_build_target(fn, item)
|
||||||
@@ -399,14 +478,14 @@ class TaskData:
|
|||||||
all_p = bb.providers.getRuntimeProviders(dataCache, item)
|
all_p = bb.providers.getRuntimeProviders(dataCache, item)
|
||||||
|
|
||||||
if not all_p:
|
if not all_p:
|
||||||
bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees(item), reasons=self.get_reasons(item, True)), cfgData)
|
bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item), reasons=self.get_reasons(item, True)), cfgData)
|
||||||
raise bb.providers.NoRProvider(item)
|
raise bb.providers.NoRProvider(item)
|
||||||
|
|
||||||
eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
|
eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
|
||||||
eligible = [p for p in eligible if not p in self.failed_fns]
|
eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
|
||||||
|
|
||||||
if not eligible:
|
if not eligible:
|
||||||
bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees(item), reasons=["No eligible RPROVIDERs exist for '%s'" % item]), cfgData)
|
bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item), reasons=["No eligible RPROVIDERs exist for '%s'" % item]), cfgData)
|
||||||
raise bb.providers.NoRProvider(item)
|
raise bb.providers.NoRProvider(item)
|
||||||
|
|
||||||
if len(eligible) > 1 and numberPreferred == 0:
|
if len(eligible) > 1 and numberPreferred == 0:
|
||||||
@@ -428,80 +507,82 @@ class TaskData:
|
|||||||
|
|
||||||
# run through the list until we find one that we can build
|
# run through the list until we find one that we can build
|
||||||
for fn in eligible:
|
for fn in eligible:
|
||||||
if fn in self.failed_fns:
|
fnid = self.getfn_id(fn)
|
||||||
|
if fnid in self.failed_fnids:
|
||||||
continue
|
continue
|
||||||
logger.debug(2, "adding '%s' to satisfy runtime '%s'", fn, item)
|
logger.debug(2, "adding '%s' to satisfy runtime '%s'", fn, item)
|
||||||
self.add_runtime_target(fn, item)
|
self.add_runtime_target(fn, item)
|
||||||
self.add_tasks(fn, dataCache)
|
self.add_tasks(fn, dataCache)
|
||||||
|
|
||||||
def fail_fn(self, fn, missing_list=None):
|
def fail_fnid(self, fnid, missing_list=None):
|
||||||
"""
|
"""
|
||||||
Mark a file as failed (unbuildable)
|
Mark a file as failed (unbuildable)
|
||||||
Remove any references from build and runtime provider lists
|
Remove any references from build and runtime provider lists
|
||||||
|
|
||||||
missing_list, A list of missing requirements for this target
|
missing_list, A list of missing requirements for this target
|
||||||
"""
|
"""
|
||||||
if fn in self.failed_fns:
|
if fnid in self.failed_fnids:
|
||||||
return
|
return
|
||||||
if not missing_list:
|
if not missing_list:
|
||||||
missing_list = []
|
missing_list = []
|
||||||
logger.debug(1, "File '%s' is unbuildable, removing...", fn)
|
logger.debug(1, "File '%s' is unbuildable, removing...", self.fn_index[fnid])
|
||||||
self.failed_fns.append(fn)
|
self.failed_fnids.append(fnid)
|
||||||
for target in self.build_targets:
|
for target in self.build_targets:
|
||||||
if fn in self.build_targets[target]:
|
if fnid in self.build_targets[target]:
|
||||||
self.build_targets[target].remove(fn)
|
self.build_targets[target].remove(fnid)
|
||||||
if len(self.build_targets[target]) == 0:
|
if len(self.build_targets[target]) == 0:
|
||||||
self.remove_buildtarget(target, missing_list)
|
self.remove_buildtarget(target, missing_list)
|
||||||
for target in self.run_targets:
|
for target in self.run_targets:
|
||||||
if fn in self.run_targets[target]:
|
if fnid in self.run_targets[target]:
|
||||||
self.run_targets[target].remove(fn)
|
self.run_targets[target].remove(fnid)
|
||||||
if len(self.run_targets[target]) == 0:
|
if len(self.run_targets[target]) == 0:
|
||||||
self.remove_runtarget(target, missing_list)
|
self.remove_runtarget(target, missing_list)
|
||||||
|
|
||||||
def remove_buildtarget(self, target, missing_list=None):
|
def remove_buildtarget(self, targetid, missing_list=None):
|
||||||
"""
|
"""
|
||||||
Mark a build target as failed (unbuildable)
|
Mark a build target as failed (unbuildable)
|
||||||
Trigger removal of any files that have this as a dependency
|
Trigger removal of any files that have this as a dependency
|
||||||
"""
|
"""
|
||||||
if not missing_list:
|
if not missing_list:
|
||||||
missing_list = [target]
|
missing_list = [self.build_names_index[targetid]]
|
||||||
else:
|
else:
|
||||||
missing_list = [target] + missing_list
|
missing_list = [self.build_names_index[targetid]] + missing_list
|
||||||
logger.verbose("Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", target, missing_list)
|
logger.verbose("Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.build_names_index[targetid], missing_list)
|
||||||
self.failed_deps.append(target)
|
self.failed_deps.append(targetid)
|
||||||
dependees = self.get_dependees(target)
|
dependees = self.get_dependees(targetid)
|
||||||
for fn in dependees:
|
for fnid in dependees:
|
||||||
self.fail_fn(fn, missing_list)
|
self.fail_fnid(fnid, missing_list)
|
||||||
for tid in self.taskentries:
|
for taskid in xrange(len(self.tasks_idepends)):
|
||||||
for (idepend, idependtask) in self.taskentries[tid].idepends:
|
idepends = self.tasks_idepends[taskid]
|
||||||
if idepend == target:
|
for (idependid, idependtask) in idepends:
|
||||||
fn = tid.rsplit(":",1)[0]
|
if idependid == targetid:
|
||||||
self.fail_fn(fn, missing_list)
|
self.fail_fnid(self.tasks_fnid[taskid], missing_list)
|
||||||
|
|
||||||
if self.abort and target in self.external_targets:
|
if self.abort and targetid in self.external_targets:
|
||||||
|
target = self.build_names_index[targetid]
|
||||||
logger.error("Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s", target, missing_list)
|
logger.error("Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s", target, missing_list)
|
||||||
raise bb.providers.NoProvider(target)
|
raise bb.providers.NoProvider(target)
|
||||||
|
|
||||||
def remove_runtarget(self, target, missing_list=None):
|
def remove_runtarget(self, targetid, missing_list=None):
|
||||||
"""
|
"""
|
||||||
Mark a run target as failed (unbuildable)
|
Mark a run target as failed (unbuildable)
|
||||||
Trigger removal of any files that have this as a dependency
|
Trigger removal of any files that have this as a dependency
|
||||||
"""
|
"""
|
||||||
if not missing_list:
|
if not missing_list:
|
||||||
missing_list = [target]
|
missing_list = [self.run_names_index[targetid]]
|
||||||
else:
|
else:
|
||||||
missing_list = [target] + missing_list
|
missing_list = [self.run_names_index[targetid]] + missing_list
|
||||||
|
|
||||||
logger.info("Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", target, missing_list)
|
logger.info("Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.run_names_index[targetid], missing_list)
|
||||||
self.failed_rdeps.append(target)
|
self.failed_rdeps.append(targetid)
|
||||||
dependees = self.get_rdependees(target)
|
dependees = self.get_rdependees(targetid)
|
||||||
for fn in dependees:
|
for fnid in dependees:
|
||||||
self.fail_fn(fn, missing_list)
|
self.fail_fnid(fnid, missing_list)
|
||||||
for tid in self.taskentries:
|
for taskid in xrange(len(self.tasks_irdepends)):
|
||||||
for (idepend, idependtask) in self.taskentries[tid].irdepends:
|
irdepends = self.tasks_irdepends[taskid]
|
||||||
if idepend == target:
|
for (idependid, idependtask) in irdepends:
|
||||||
fn = tid.rsplit(":",1)[0]
|
if idependid == targetid:
|
||||||
self.fail_fn(fn, missing_list)
|
self.fail_fnid(self.tasks_fnid[taskid], missing_list)
|
||||||
|
|
||||||
def add_unresolved(self, cfgData, dataCache):
|
def add_unresolved(self, cfgData, dataCache):
|
||||||
"""
|
"""
|
||||||
@@ -515,68 +596,72 @@ class TaskData:
|
|||||||
self.add_provider_internal(cfgData, dataCache, target)
|
self.add_provider_internal(cfgData, dataCache, target)
|
||||||
added = added + 1
|
added = added + 1
|
||||||
except bb.providers.NoProvider:
|
except bb.providers.NoProvider:
|
||||||
if self.abort and target in self.external_targets and not self.allowincomplete:
|
targetid = self.getbuild_id(target)
|
||||||
|
if self.abort and targetid in self.external_targets and not self.allowincomplete:
|
||||||
raise
|
raise
|
||||||
if not self.allowincomplete:
|
if not self.allowincomplete:
|
||||||
self.remove_buildtarget(target)
|
self.remove_buildtarget(targetid)
|
||||||
for target in self.get_unresolved_run_targets(dataCache):
|
for target in self.get_unresolved_run_targets(dataCache):
|
||||||
try:
|
try:
|
||||||
self.add_rprovider(cfgData, dataCache, target)
|
self.add_rprovider(cfgData, dataCache, target)
|
||||||
added = added + 1
|
added = added + 1
|
||||||
except (bb.providers.NoRProvider, bb.providers.MultipleRProvider):
|
except (bb.providers.NoRProvider, bb.providers.MultipleRProvider):
|
||||||
self.remove_runtarget(target)
|
self.remove_runtarget(self.getrun_id(target))
|
||||||
logger.debug(1, "Resolved " + str(added) + " extra dependencies")
|
logger.debug(1, "Resolved " + str(added) + " extra dependencies")
|
||||||
if added == 0:
|
if added == 0:
|
||||||
break
|
break
|
||||||
# self.dump_data()
|
# self.dump_data()
|
||||||
|
|
||||||
def get_providermap(self, prefix=None):
|
def get_providermap(self):
|
||||||
provmap = {}
|
virts = []
|
||||||
for name in self.build_targets:
|
virtmap = {}
|
||||||
if prefix and not name.startswith(prefix):
|
|
||||||
continue
|
for name in self.build_names_index:
|
||||||
if self.have_build_target(name):
|
if name.startswith("virtual/"):
|
||||||
provider = self.get_provider(name)
|
virts.append(name)
|
||||||
if provider:
|
for v in virts:
|
||||||
provmap[name] = provider[0]
|
if self.have_build_target(v):
|
||||||
return provmap
|
virtmap[v] = self.fn_index[self.get_provider(v)[0]]
|
||||||
|
return virtmap
|
||||||
|
|
||||||
def dump_data(self):
|
def dump_data(self):
|
||||||
"""
|
"""
|
||||||
Dump some debug information on the internal data structures
|
Dump some debug information on the internal data structures
|
||||||
"""
|
"""
|
||||||
logger.debug(3, "build_names:")
|
logger.debug(3, "build_names:")
|
||||||
logger.debug(3, ", ".join(self.build_targets))
|
logger.debug(3, ", ".join(self.build_names_index))
|
||||||
|
|
||||||
logger.debug(3, "run_names:")
|
logger.debug(3, "run_names:")
|
||||||
logger.debug(3, ", ".join(self.run_targets))
|
logger.debug(3, ", ".join(self.run_names_index))
|
||||||
|
|
||||||
logger.debug(3, "build_targets:")
|
logger.debug(3, "build_targets:")
|
||||||
for target in self.build_targets:
|
for buildid in xrange(len(self.build_names_index)):
|
||||||
|
target = self.build_names_index[buildid]
|
||||||
targets = "None"
|
targets = "None"
|
||||||
if target in self.build_targets:
|
if buildid in self.build_targets:
|
||||||
targets = self.build_targets[target]
|
targets = self.build_targets[buildid]
|
||||||
logger.debug(3, " %s: %s", target, targets)
|
logger.debug(3, " (%s)%s: %s", buildid, target, targets)
|
||||||
|
|
||||||
logger.debug(3, "run_targets:")
|
logger.debug(3, "run_targets:")
|
||||||
for target in self.run_targets:
|
for runid in xrange(len(self.run_names_index)):
|
||||||
|
target = self.run_names_index[runid]
|
||||||
targets = "None"
|
targets = "None"
|
||||||
if target in self.run_targets:
|
if runid in self.run_targets:
|
||||||
targets = self.run_targets[target]
|
targets = self.run_targets[runid]
|
||||||
logger.debug(3, " %s: %s", target, targets)
|
logger.debug(3, " (%s)%s: %s", runid, target, targets)
|
||||||
|
|
||||||
logger.debug(3, "tasks:")
|
logger.debug(3, "tasks:")
|
||||||
for tid in self.taskentries:
|
for task in xrange(len(self.tasks_name)):
|
||||||
logger.debug(3, " %s: %s %s %s",
|
logger.debug(3, " (%s)%s - %s: %s",
|
||||||
tid,
|
task,
|
||||||
self.taskentries[tid].idepends,
|
self.fn_index[self.tasks_fnid[task]],
|
||||||
self.taskentries[tid].irdepends,
|
self.tasks_name[task],
|
||||||
self.taskentries[tid].tdepends)
|
self.tasks_tdepends[task])
|
||||||
|
|
||||||
logger.debug(3, "dependency ids (per fn):")
|
logger.debug(3, "dependency ids (per fn):")
|
||||||
for fn in self.depids:
|
for fnid in self.depids:
|
||||||
logger.debug(3, " %s: %s", fn, self.depids[fn])
|
logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.depids[fnid])
|
||||||
|
|
||||||
logger.debug(3, "runtime dependency ids (per fn):")
|
logger.debug(3, "runtime dependency ids (per fn):")
|
||||||
for fn in self.rdepids:
|
for fnid in self.rdepids:
|
||||||
logger.debug(3, " %s: %s", fn, self.rdepids[fn])
|
logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.rdepids[fnid])
|
||||||
|
|||||||
@@ -191,8 +191,8 @@ class PythonReferenceTest(ReferenceTest):
|
|||||||
if hasattr(bb.utils, "_context"):
|
if hasattr(bb.utils, "_context"):
|
||||||
self.context = bb.utils._context
|
self.context = bb.utils._context
|
||||||
else:
|
else:
|
||||||
import builtins
|
import __builtin__
|
||||||
self.context = builtins.__dict__
|
self.context = __builtin__.__dict__
|
||||||
|
|
||||||
def parseExpression(self, exp):
|
def parseExpression(self, exp):
|
||||||
parsedvar = self.d.expandWithRefs(exp, None)
|
parsedvar = self.d.expandWithRefs(exp, None)
|
||||||
@@ -293,16 +293,11 @@ bb.data.getVar(a(), d, False)
|
|||||||
def test_python(self):
|
def test_python(self):
|
||||||
self.d.setVar("FOO", self.pydata)
|
self.d.setVar("FOO", self.pydata)
|
||||||
self.setEmptyVars(["inexpand", "a", "test2", "test"])
|
self.setEmptyVars(["inexpand", "a", "test2", "test"])
|
||||||
self.d.setVarFlags("FOO", {
|
self.d.setVarFlags("FOO", {"func": True, "python": True})
|
||||||
"func": True,
|
|
||||||
"python": True,
|
|
||||||
"lineno": 1,
|
|
||||||
"filename": "example.bb",
|
|
||||||
})
|
|
||||||
|
|
||||||
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
||||||
|
|
||||||
self.assertEqual(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"]))
|
self.assertEquals(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"]))
|
||||||
|
|
||||||
|
|
||||||
shelldata = """
|
shelldata = """
|
||||||
@@ -349,7 +344,7 @@ esac
|
|||||||
|
|
||||||
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
||||||
|
|
||||||
self.assertEqual(deps, set(["somevar", "inverted"] + execs))
|
self.assertEquals(deps, set(["somevar", "inverted"] + execs))
|
||||||
|
|
||||||
|
|
||||||
def test_vardeps(self):
|
def test_vardeps(self):
|
||||||
@@ -359,7 +354,7 @@ esac
|
|||||||
|
|
||||||
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
||||||
|
|
||||||
self.assertEqual(deps, set(["oe_libinstall"]))
|
self.assertEquals(deps, set(["oe_libinstall"]))
|
||||||
|
|
||||||
def test_vardeps_expand(self):
|
def test_vardeps_expand(self):
|
||||||
self.d.setVar("oe_libinstall", "echo test")
|
self.d.setVar("oe_libinstall", "echo test")
|
||||||
@@ -368,7 +363,7 @@ esac
|
|||||||
|
|
||||||
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
||||||
|
|
||||||
self.assertEqual(deps, set(["oe_libinstall"]))
|
self.assertEquals(deps, set(["oe_libinstall"]))
|
||||||
|
|
||||||
#Currently no wildcard support
|
#Currently no wildcard support
|
||||||
#def test_vardeps_wildcards(self):
|
#def test_vardeps_wildcards(self):
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ class COWTestCase(unittest.TestCase):
|
|||||||
from bb.COW import COWDictBase
|
from bb.COW import COWDictBase
|
||||||
a = COWDictBase.copy()
|
a = COWDictBase.copy()
|
||||||
|
|
||||||
self.assertEqual(False, 'a' in a)
|
self.assertEquals(False, a.has_key('a'))
|
||||||
|
|
||||||
a['a'] = 'a'
|
a['a'] = 'a'
|
||||||
a['b'] = 'b'
|
a['b'] = 'b'
|
||||||
self.assertEqual(True, 'a' in a)
|
self.assertEquals(True, a.has_key('a'))
|
||||||
self.assertEqual(True, 'b' in a)
|
self.assertEquals(True, a.has_key('b'))
|
||||||
self.assertEqual('a', a['a'] )
|
self.assertEquals('a', a['a'] )
|
||||||
self.assertEqual('b', a['b'] )
|
self.assertEquals('b', a['b'] )
|
||||||
|
|
||||||
def testCopyCopy(self):
|
def testCopyCopy(self):
|
||||||
"""
|
"""
|
||||||
@@ -60,31 +60,31 @@ class COWTestCase(unittest.TestCase):
|
|||||||
c['a'] = 30
|
c['a'] = 30
|
||||||
|
|
||||||
# test separation of the two instances
|
# test separation of the two instances
|
||||||
self.assertEqual(False, 'c' in c)
|
self.assertEquals(False, c.has_key('c'))
|
||||||
self.assertEqual(30, c['a'])
|
self.assertEquals(30, c['a'])
|
||||||
self.assertEqual(10, b['a'])
|
self.assertEquals(10, b['a'])
|
||||||
|
|
||||||
# test copy
|
# test copy
|
||||||
b_2 = b.copy()
|
b_2 = b.copy()
|
||||||
c_2 = c.copy()
|
c_2 = c.copy()
|
||||||
|
|
||||||
self.assertEqual(False, 'c' in c_2)
|
self.assertEquals(False, c_2.has_key('c'))
|
||||||
self.assertEqual(10, b_2['a'])
|
self.assertEquals(10, b_2['a'])
|
||||||
|
|
||||||
b_2['d'] = 40
|
b_2['d'] = 40
|
||||||
self.assertEqual(False, 'd' in c_2)
|
self.assertEquals(False, c_2.has_key('d'))
|
||||||
self.assertEqual(True, 'd' in b_2)
|
self.assertEquals(True, b_2.has_key('d'))
|
||||||
self.assertEqual(40, b_2['d'])
|
self.assertEquals(40, b_2['d'])
|
||||||
self.assertEqual(False, 'd' in b)
|
self.assertEquals(False, b.has_key('d'))
|
||||||
self.assertEqual(False, 'd' in c)
|
self.assertEquals(False, c.has_key('d'))
|
||||||
|
|
||||||
c_2['d'] = 30
|
c_2['d'] = 30
|
||||||
self.assertEqual(True, 'd' in c_2)
|
self.assertEquals(True, c_2.has_key('d'))
|
||||||
self.assertEqual(True, 'd' in b_2)
|
self.assertEquals(True, b_2.has_key('d'))
|
||||||
self.assertEqual(30, c_2['d'])
|
self.assertEquals(30, c_2['d'])
|
||||||
self.assertEqual(40, b_2['d'])
|
self.assertEquals(40, b_2['d'])
|
||||||
self.assertEqual(False, 'd' in b)
|
self.assertEquals(False, b.has_key('d'))
|
||||||
self.assertEqual(False, 'd' in c)
|
self.assertEquals(False, c.has_key('d'))
|
||||||
|
|
||||||
# test copy of the copy
|
# test copy of the copy
|
||||||
c_3 = c_2.copy()
|
c_3 = c_2.copy()
|
||||||
@@ -92,19 +92,19 @@ class COWTestCase(unittest.TestCase):
|
|||||||
b_3_2 = b_2.copy()
|
b_3_2 = b_2.copy()
|
||||||
|
|
||||||
c_3['e'] = 4711
|
c_3['e'] = 4711
|
||||||
self.assertEqual(4711, c_3['e'])
|
self.assertEquals(4711, c_3['e'])
|
||||||
self.assertEqual(False, 'e' in c_2)
|
self.assertEquals(False, c_2.has_key('e'))
|
||||||
self.assertEqual(False, 'e' in b_3)
|
self.assertEquals(False, b_3.has_key('e'))
|
||||||
self.assertEqual(False, 'e' in b_3_2)
|
self.assertEquals(False, b_3_2.has_key('e'))
|
||||||
self.assertEqual(False, 'e' in b_2)
|
self.assertEquals(False, b_2.has_key('e'))
|
||||||
|
|
||||||
b_3['e'] = 'viel'
|
b_3['e'] = 'viel'
|
||||||
self.assertEqual('viel', b_3['e'])
|
self.assertEquals('viel', b_3['e'])
|
||||||
self.assertEqual(4711, c_3['e'])
|
self.assertEquals(4711, c_3['e'])
|
||||||
self.assertEqual(False, 'e' in c_2)
|
self.assertEquals(False, c_2.has_key('e'))
|
||||||
self.assertEqual(True, 'e' in b_3)
|
self.assertEquals(True, b_3.has_key('e'))
|
||||||
self.assertEqual(False, 'e' in b_3_2)
|
self.assertEquals(False, b_3_2.has_key('e'))
|
||||||
self.assertEqual(False, 'e' in b_2)
|
self.assertEquals(False, b_2.has_key('e'))
|
||||||
|
|
||||||
def testCow(self):
|
def testCow(self):
|
||||||
from bb.COW import COWDictBase
|
from bb.COW import COWDictBase
|
||||||
@@ -115,12 +115,12 @@ class COWTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
copy = c.copy()
|
copy = c.copy()
|
||||||
|
|
||||||
self.assertEqual(1027, c['123'])
|
self.assertEquals(1027, c['123'])
|
||||||
self.assertEqual(4711, c['other'])
|
self.assertEquals(4711, c['other'])
|
||||||
self.assertEqual({'abc':10, 'bcd':20}, c['d'])
|
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
|
||||||
self.assertEqual(1027, copy['123'])
|
self.assertEquals(1027, copy['123'])
|
||||||
self.assertEqual(4711, copy['other'])
|
self.assertEquals(4711, copy['other'])
|
||||||
self.assertEqual({'abc':10, 'bcd':20}, copy['d'])
|
self.assertEquals({'abc':10, 'bcd':20}, copy['d'])
|
||||||
|
|
||||||
# cow it now
|
# cow it now
|
||||||
copy['123'] = 1028
|
copy['123'] = 1028
|
||||||
@@ -128,9 +128,9 @@ class COWTestCase(unittest.TestCase):
|
|||||||
copy['d']['abc'] = 20
|
copy['d']['abc'] = 20
|
||||||
|
|
||||||
|
|
||||||
self.assertEqual(1027, c['123'])
|
self.assertEquals(1027, c['123'])
|
||||||
self.assertEqual(4711, c['other'])
|
self.assertEquals(4711, c['other'])
|
||||||
self.assertEqual({'abc':10, 'bcd':20}, c['d'])
|
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
|
||||||
self.assertEqual(1028, copy['123'])
|
self.assertEquals(1028, copy['123'])
|
||||||
self.assertEqual(4712, copy['other'])
|
self.assertEquals(4712, copy['other'])
|
||||||
self.assertEqual({'abc':20, 'bcd':20}, copy['d'])
|
self.assertEquals({'abc':20, 'bcd':20}, copy['d'])
|
||||||
|
|||||||
@@ -80,11 +80,6 @@ class DataExpansions(unittest.TestCase):
|
|||||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
||||||
self.assertEqual(str(val), "value_of_foo value_of_bar")
|
self.assertEqual(str(val), "value_of_foo value_of_bar")
|
||||||
|
|
||||||
def test_python_unexpanded(self):
|
|
||||||
self.d.setVar("bar", "${unsetvar}")
|
|
||||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
|
||||||
self.assertEqual(str(val), "${@d.getVar('foo', True) + ' ${unsetvar}'}")
|
|
||||||
|
|
||||||
def test_python_snippet_syntax_error(self):
|
def test_python_snippet_syntax_error(self):
|
||||||
self.d.setVar("FOO", "${@foo = 5}")
|
self.d.setVar("FOO", "${@foo = 5}")
|
||||||
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
|
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
|
||||||
@@ -147,14 +142,14 @@ class DataExpansions(unittest.TestCase):
|
|||||||
self.assertEqual(self.d.getVar("foo", False), None)
|
self.assertEqual(self.d.getVar("foo", False), None)
|
||||||
|
|
||||||
def test_keys(self):
|
def test_keys(self):
|
||||||
keys = list(self.d.keys())
|
keys = self.d.keys()
|
||||||
self.assertCountEqual(keys, ['value_of_foo', 'foo', 'bar'])
|
self.assertEqual(keys, ['value_of_foo', 'foo', 'bar'])
|
||||||
|
|
||||||
def test_keys_deletion(self):
|
def test_keys_deletion(self):
|
||||||
newd = bb.data.createCopy(self.d)
|
newd = bb.data.createCopy(self.d)
|
||||||
newd.delVar("bar")
|
newd.delVar("bar")
|
||||||
keys = list(newd.keys())
|
keys = newd.keys()
|
||||||
self.assertCountEqual(keys, ['value_of_foo', 'foo'])
|
self.assertEqual(keys, ['value_of_foo', 'foo'])
|
||||||
|
|
||||||
class TestNestedExpansions(unittest.TestCase):
|
class TestNestedExpansions(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -334,7 +329,7 @@ class TestOverrides(unittest.TestCase):
|
|||||||
self.d.setVar("TEST2_bar", "testvalue2")
|
self.d.setVar("TEST2_bar", "testvalue2")
|
||||||
bb.data.update_data(self.d)
|
bb.data.update_data(self.d)
|
||||||
self.assertEqual(self.d.getVar("TEST2", True), "testvalue2")
|
self.assertEqual(self.d.getVar("TEST2", True), "testvalue2")
|
||||||
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
|
self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
|
||||||
|
|
||||||
def test_multiple_override(self):
|
def test_multiple_override(self):
|
||||||
self.d.setVar("TEST_bar", "testvalue2")
|
self.d.setVar("TEST_bar", "testvalue2")
|
||||||
@@ -342,7 +337,7 @@ class TestOverrides(unittest.TestCase):
|
|||||||
self.d.setVar("TEST_foo", "testvalue4")
|
self.d.setVar("TEST_foo", "testvalue4")
|
||||||
bb.data.update_data(self.d)
|
bb.data.update_data(self.d)
|
||||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
||||||
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
|
self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
|
||||||
|
|
||||||
def test_multiple_combined_overrides(self):
|
def test_multiple_combined_overrides(self):
|
||||||
self.d.setVar("TEST_local_foo_bar", "testvalue3")
|
self.d.setVar("TEST_local_foo_bar", "testvalue3")
|
||||||
@@ -399,13 +394,13 @@ class TestFlags(unittest.TestCase):
|
|||||||
self.d.setVarFlag("foo", "flag2", "value of flag2")
|
self.d.setVarFlag("foo", "flag2", "value of flag2")
|
||||||
|
|
||||||
def test_setflag(self):
|
def test_setflag(self):
|
||||||
self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
|
self.assertEqual(self.d.getVarFlag("foo", "flag1"), "value of flag1")
|
||||||
self.assertEqual(self.d.getVarFlag("foo", "flag2", False), "value of flag2")
|
self.assertEqual(self.d.getVarFlag("foo", "flag2"), "value of flag2")
|
||||||
|
|
||||||
def test_delflag(self):
|
def test_delflag(self):
|
||||||
self.d.delVarFlag("foo", "flag2")
|
self.d.delVarFlag("foo", "flag2")
|
||||||
self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
|
self.assertEqual(self.d.getVarFlag("foo", "flag1"), "value of flag1")
|
||||||
self.assertEqual(self.d.getVarFlag("foo", "flag2", False), None)
|
self.assertEqual(self.d.getVarFlag("foo", "flag2"), None)
|
||||||
|
|
||||||
|
|
||||||
class Contains(unittest.TestCase):
|
class Contains(unittest.TestCase):
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
import collections
|
|
||||||
import os
|
import os
|
||||||
from bb.fetch2 import URI
|
from bb.fetch2 import URI
|
||||||
from bb.fetch2 import FetchMethod
|
from bb.fetch2 import FetchMethod
|
||||||
@@ -134,10 +133,10 @@ class URITest(unittest.TestCase):
|
|||||||
'userinfo': 'anoncvs:anonymous',
|
'userinfo': 'anoncvs:anonymous',
|
||||||
'username': 'anoncvs',
|
'username': 'anoncvs',
|
||||||
'password': 'anonymous',
|
'password': 'anonymous',
|
||||||
'params': collections.OrderedDict([
|
'params': {
|
||||||
('tag', 'V0-99-81'),
|
'tag': 'V0-99-81',
|
||||||
('module', 'familiar/dist/ipkg')
|
'module': 'familiar/dist/ipkg'
|
||||||
]),
|
},
|
||||||
'query': {},
|
'query': {},
|
||||||
'relative': False
|
'relative': False
|
||||||
},
|
},
|
||||||
@@ -229,38 +228,7 @@ class URITest(unittest.TestCase):
|
|||||||
'params': {},
|
'params': {},
|
||||||
'query': {},
|
'query': {},
|
||||||
'relative': False
|
'relative': False
|
||||||
},
|
|
||||||
"http://somesite.net;someparam=1": {
|
|
||||||
'uri': 'http://somesite.net;someparam=1',
|
|
||||||
'scheme': 'http',
|
|
||||||
'hostname': 'somesite.net',
|
|
||||||
'port': None,
|
|
||||||
'hostport': 'somesite.net',
|
|
||||||
'path': '',
|
|
||||||
'userinfo': '',
|
|
||||||
'userinfo': '',
|
|
||||||
'username': '',
|
|
||||||
'password': '',
|
|
||||||
'params': {"someparam" : "1"},
|
|
||||||
'query': {},
|
|
||||||
'relative': False
|
|
||||||
},
|
|
||||||
"file://somelocation;someparam=1": {
|
|
||||||
'uri': 'file:somelocation;someparam=1',
|
|
||||||
'scheme': 'file',
|
|
||||||
'hostname': '',
|
|
||||||
'port': None,
|
|
||||||
'hostport': '',
|
|
||||||
'path': 'somelocation',
|
|
||||||
'userinfo': '',
|
|
||||||
'userinfo': '',
|
|
||||||
'username': '',
|
|
||||||
'password': '',
|
|
||||||
'params': {"someparam" : "1"},
|
|
||||||
'query': {},
|
|
||||||
'relative': True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_uri(self):
|
def test_uri(self):
|
||||||
@@ -360,10 +328,7 @@ class FetcherTest(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
os.chdir(self.origdir)
|
os.chdir(self.origdir)
|
||||||
if os.environ.get("BB_TMPDIR_NOCLEAN") == "yes":
|
bb.utils.prunedir(self.tempdir)
|
||||||
print("Not cleaning up %s. Please remove manually." % self.tempdir)
|
|
||||||
else:
|
|
||||||
bb.utils.prunedir(self.tempdir)
|
|
||||||
|
|
||||||
class MirrorUriTest(FetcherTest):
|
class MirrorUriTest(FetcherTest):
|
||||||
|
|
||||||
@@ -454,7 +419,7 @@ class MirrorUriTest(FetcherTest):
|
|||||||
class FetcherLocalTest(FetcherTest):
|
class FetcherLocalTest(FetcherTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
def touch(fn):
|
def touch(fn):
|
||||||
with open(fn, 'a'):
|
with file(fn, 'a'):
|
||||||
os.utime(fn, None)
|
os.utime(fn, None)
|
||||||
|
|
||||||
super(FetcherLocalTest, self).setUp()
|
super(FetcherLocalTest, self).setUp()
|
||||||
@@ -486,7 +451,9 @@ class FetcherLocalTest(FetcherTest):
|
|||||||
|
|
||||||
def test_local_wildcard(self):
|
def test_local_wildcard(self):
|
||||||
tree = self.fetchUnpack(['file://a', 'file://dir/*'])
|
tree = self.fetchUnpack(['file://a', 'file://dir/*'])
|
||||||
self.assertEqual(tree, ['a', 'dir/c', 'dir/d', 'dir/subdir/e'])
|
# FIXME: this is broken - it should return ['a', 'dir/c', 'dir/d', 'dir/subdir/e']
|
||||||
|
# see https://bugzilla.yoctoproject.org/show_bug.cgi?id=6128
|
||||||
|
self.assertEqual(tree, ['a', 'b', 'dir/c', 'dir/d', 'dir/subdir/e'])
|
||||||
|
|
||||||
def test_local_dir(self):
|
def test_local_dir(self):
|
||||||
tree = self.fetchUnpack(['file://a', 'file://dir'])
|
tree = self.fetchUnpack(['file://a', 'file://dir'])
|
||||||
@@ -494,29 +461,22 @@ class FetcherLocalTest(FetcherTest):
|
|||||||
|
|
||||||
def test_local_subdir(self):
|
def test_local_subdir(self):
|
||||||
tree = self.fetchUnpack(['file://dir/subdir'])
|
tree = self.fetchUnpack(['file://dir/subdir'])
|
||||||
self.assertEqual(tree, ['dir/subdir/e'])
|
# FIXME: this is broken - it should return ['dir/subdir/e']
|
||||||
|
# see https://bugzilla.yoctoproject.org/show_bug.cgi?id=6129
|
||||||
|
self.assertEqual(tree, ['subdir/e'])
|
||||||
|
|
||||||
def test_local_subdir_file(self):
|
def test_local_subdir_file(self):
|
||||||
tree = self.fetchUnpack(['file://dir/subdir/e'])
|
tree = self.fetchUnpack(['file://dir/subdir/e'])
|
||||||
self.assertEqual(tree, ['dir/subdir/e'])
|
self.assertEqual(tree, ['dir/subdir/e'])
|
||||||
|
|
||||||
def test_local_subdirparam(self):
|
def test_local_subdirparam(self):
|
||||||
tree = self.fetchUnpack(['file://a;subdir=bar', 'file://dir;subdir=foo/moo'])
|
tree = self.fetchUnpack(['file://a;subdir=bar'])
|
||||||
self.assertEqual(tree, ['bar/a', 'foo/moo/dir/c', 'foo/moo/dir/d', 'foo/moo/dir/subdir/e'])
|
self.assertEqual(tree, ['bar/a'])
|
||||||
|
|
||||||
def test_local_deepsubdirparam(self):
|
def test_local_deepsubdirparam(self):
|
||||||
tree = self.fetchUnpack(['file://dir/subdir/e;subdir=bar'])
|
tree = self.fetchUnpack(['file://dir/subdir/e;subdir=bar'])
|
||||||
self.assertEqual(tree, ['bar/dir/subdir/e'])
|
self.assertEqual(tree, ['bar/dir/subdir/e'])
|
||||||
|
|
||||||
def test_local_absolutedir(self):
|
|
||||||
# Unpacking to an absolute path that is a subdirectory of the root
|
|
||||||
# should work
|
|
||||||
tree = self.fetchUnpack(['file://a;subdir=%s' % os.path.join(self.unpackdir, 'bar')])
|
|
||||||
|
|
||||||
# Unpacking to an absolute path outside of the root should fail
|
|
||||||
with self.assertRaises(bb.fetch2.UnpackError):
|
|
||||||
self.fetchUnpack(['file://a;subdir=/bin/sh'])
|
|
||||||
|
|
||||||
class FetcherNetworkTest(FetcherTest):
|
class FetcherNetworkTest(FetcherTest):
|
||||||
|
|
||||||
if os.environ.get("BB_SKIP_NETTESTS") == "yes":
|
if os.environ.get("BB_SKIP_NETTESTS") == "yes":
|
||||||
@@ -597,36 +557,6 @@ class FetcherNetworkTest(FetcherTest):
|
|||||||
url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;tag=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
|
url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;tag=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
|
||||||
self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
|
self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
|
||||||
|
|
||||||
def test_gitfetch_localusehead(self):
|
|
||||||
# Create dummy local Git repo
|
|
||||||
src_dir = tempfile.mkdtemp(dir=self.tempdir,
|
|
||||||
prefix='gitfetch_localusehead_')
|
|
||||||
src_dir = os.path.abspath(src_dir)
|
|
||||||
bb.process.run("git init", cwd=src_dir)
|
|
||||||
bb.process.run("git commit --allow-empty -m'Dummy commit'",
|
|
||||||
cwd=src_dir)
|
|
||||||
# Use other branch than master
|
|
||||||
bb.process.run("git checkout -b my-devel", cwd=src_dir)
|
|
||||||
bb.process.run("git commit --allow-empty -m'Dummy commit 2'",
|
|
||||||
cwd=src_dir)
|
|
||||||
stdout = bb.process.run("git rev-parse HEAD", cwd=src_dir)
|
|
||||||
orig_rev = stdout[0].strip()
|
|
||||||
|
|
||||||
# Fetch and check revision
|
|
||||||
self.d.setVar("SRCREV", "AUTOINC")
|
|
||||||
url = "git://" + src_dir + ";protocol=file;usehead=1"
|
|
||||||
fetcher = bb.fetch.Fetch([url], self.d)
|
|
||||||
fetcher.download()
|
|
||||||
fetcher.unpack(self.unpackdir)
|
|
||||||
stdout = bb.process.run("git rev-parse HEAD",
|
|
||||||
cwd=os.path.join(self.unpackdir, 'git'))
|
|
||||||
unpack_rev = stdout[0].strip()
|
|
||||||
self.assertEqual(orig_rev, unpack_rev)
|
|
||||||
|
|
||||||
def test_gitfetch_remoteusehead(self):
|
|
||||||
url = "git://git.openembedded.org/bitbake;usehead=1"
|
|
||||||
self.assertRaises(bb.fetch.ParameterError, self.gitfetcher, url, url)
|
|
||||||
|
|
||||||
def test_gitfetch_premirror(self):
|
def test_gitfetch_premirror(self):
|
||||||
url1 = "git://git.openembedded.org/bitbake"
|
url1 = "git://git.openembedded.org/bitbake"
|
||||||
url2 = "git://someserver.org/bitbake"
|
url2 = "git://someserver.org/bitbake"
|
||||||
@@ -654,68 +584,54 @@ class FetcherNetworkTest(FetcherTest):
|
|||||||
os.chdir(os.path.dirname(self.unpackdir))
|
os.chdir(os.path.dirname(self.unpackdir))
|
||||||
fetcher.unpack(self.unpackdir)
|
fetcher.unpack(self.unpackdir)
|
||||||
|
|
||||||
|
def test_trusted_network(self):
|
||||||
|
# Ensure trusted_network returns False when the host IS in the list.
|
||||||
|
url = "git://Someserver.org/foo;rev=1"
|
||||||
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org someserver.org server2.org server3.org")
|
||||||
|
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
||||||
|
|
||||||
class TrustedNetworksTest(FetcherTest):
|
def test_wild_trusted_network(self):
|
||||||
def test_trusted_network(self):
|
# Ensure trusted_network returns true when the *.host IS in the list.
|
||||||
# Ensure trusted_network returns False when the host IS in the list.
|
url = "git://Someserver.org/foo;rev=1"
|
||||||
url = "git://Someserver.org/foo;rev=1"
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org someserver.org server2.org server3.org")
|
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
||||||
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
|
||||||
|
|
||||||
def test_wild_trusted_network(self):
|
def test_prefix_wild_trusted_network(self):
|
||||||
# Ensure trusted_network returns true when the *.host IS in the list.
|
# Ensure trusted_network returns true when the prefix matches *.host.
|
||||||
url = "git://Someserver.org/foo;rev=1"
|
url = "git://git.Someserver.org/foo;rev=1"
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
||||||
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
||||||
|
|
||||||
def test_prefix_wild_trusted_network(self):
|
def test_two_prefix_wild_trusted_network(self):
|
||||||
# Ensure trusted_network returns true when the prefix matches *.host.
|
# Ensure trusted_network returns true when the prefix matches *.host.
|
||||||
url = "git://git.Someserver.org/foo;rev=1"
|
url = "git://something.git.Someserver.org/foo;rev=1"
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
||||||
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
||||||
|
|
||||||
def test_two_prefix_wild_trusted_network(self):
|
def test_untrusted_network(self):
|
||||||
# Ensure trusted_network returns true when the prefix matches *.host.
|
# Ensure trusted_network returns False when the host is NOT in the list.
|
||||||
url = "git://something.git.Someserver.org/foo;rev=1"
|
url = "git://someserver.org/foo;rev=1"
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
|
||||||
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
self.assertFalse(bb.fetch.trusted_network(self.d, url))
|
||||||
|
|
||||||
def test_port_trusted_network(self):
|
def test_wild_untrusted_network(self):
|
||||||
# Ensure trusted_network returns True, even if the url specifies a port.
|
# Ensure trusted_network returns False when the host is NOT in the list.
|
||||||
url = "git://someserver.org:8080/foo;rev=1"
|
url = "git://*.someserver.org/foo;rev=1"
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "someserver.org")
|
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
|
||||||
self.assertTrue(bb.fetch.trusted_network(self.d, url))
|
self.assertFalse(bb.fetch.trusted_network(self.d, url))
|
||||||
|
|
||||||
def test_untrusted_network(self):
|
|
||||||
# Ensure trusted_network returns False when the host is NOT in the list.
|
|
||||||
url = "git://someserver.org/foo;rev=1"
|
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
|
|
||||||
self.assertFalse(bb.fetch.trusted_network(self.d, url))
|
|
||||||
|
|
||||||
def test_wild_untrusted_network(self):
|
|
||||||
# Ensure trusted_network returns False when the host is NOT in the list.
|
|
||||||
url = "git://*.someserver.org/foo;rev=1"
|
|
||||||
self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
|
|
||||||
self.assertFalse(bb.fetch.trusted_network(self.d, url))
|
|
||||||
|
|
||||||
class URLHandle(unittest.TestCase):
|
class URLHandle(unittest.TestCase):
|
||||||
|
|
||||||
datatable = {
|
datatable = {
|
||||||
"http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}),
|
"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@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', collections.OrderedDict([('tag', 'V0-99-81'), ('module', 'familiar/dist/ipkg')])),
|
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}),
|
||||||
"git://git.openembedded.org/bitbake;branch=@foo" : ('git', 'git.openembedded.org', '/bitbake', '', '', {'branch': '@foo'}),
|
"git://git.openembedded.org/bitbake;branch=@foo" : ('git', 'git.openembedded.org', '/bitbake', '', '', {'branch': '@foo'})
|
||||||
"file://somelocation;someparam=1": ('file', '', 'somelocation', '', '', {'someparam': '1'}),
|
|
||||||
}
|
}
|
||||||
# we require a pathname to encodeurl but users can still pass such urls to
|
|
||||||
# decodeurl and we need to handle them
|
|
||||||
decodedata = datatable.copy()
|
|
||||||
decodedata.update({
|
|
||||||
"http://somesite.net;someparam=1": ('http', 'somesite.net', '', '', '', {'someparam': '1'}),
|
|
||||||
})
|
|
||||||
|
|
||||||
def test_decodeurl(self):
|
def test_decodeurl(self):
|
||||||
for k, v in self.decodedata.items():
|
for k, v in self.datatable.items():
|
||||||
result = bb.fetch.decodeurl(k)
|
result = bb.fetch.decodeurl(k)
|
||||||
self.assertEqual(result, v)
|
self.assertEqual(result, v)
|
||||||
|
|
||||||
@@ -747,7 +663,7 @@ class FetchLatestVersionTest(FetcherTest):
|
|||||||
# version pattern "yyyymmdd"
|
# version pattern "yyyymmdd"
|
||||||
("mobile-broadband-provider-info", "git://git.gnome.org/mobile-broadband-provider-info", "4ed19e11c2975105b71b956440acdb25d46a347d", "")
|
("mobile-broadband-provider-info", "git://git.gnome.org/mobile-broadband-provider-info", "4ed19e11c2975105b71b956440acdb25d46a347d", "")
|
||||||
: "20120614",
|
: "20120614",
|
||||||
# packages with a valid UPSTREAM_CHECK_GITTAGREGEX
|
# packages with a valid GITTAGREGEX
|
||||||
("xf86-video-omap", "git://anongit.freedesktop.org/xorg/driver/xf86-video-omap", "ae0394e687f1a77e966cf72f895da91840dffb8f", "(?P<pver>(\d+\.(\d\.?)*))")
|
("xf86-video-omap", "git://anongit.freedesktop.org/xorg/driver/xf86-video-omap", "ae0394e687f1a77e966cf72f895da91840dffb8f", "(?P<pver>(\d+\.(\d\.?)*))")
|
||||||
: "0.4.3",
|
: "0.4.3",
|
||||||
("build-appliance-image", "git://git.yoctoproject.org/poky", "b37dd451a52622d5b570183a81583cc34c2ff555", "(?P<pver>(([0-9][\.|_]?)+[0-9]))")
|
("build-appliance-image", "git://git.yoctoproject.org/poky", "b37dd451a52622d5b570183a81583cc34c2ff555", "(?P<pver>(([0-9][\.|_]?)+[0-9]))")
|
||||||
@@ -789,7 +705,7 @@ class FetchLatestVersionTest(FetcherTest):
|
|||||||
for k, v in self.test_git_uris.items():
|
for k, v in self.test_git_uris.items():
|
||||||
self.d.setVar("PN", k[0])
|
self.d.setVar("PN", k[0])
|
||||||
self.d.setVar("SRCREV", k[2])
|
self.d.setVar("SRCREV", k[2])
|
||||||
self.d.setVar("UPSTREAM_CHECK_GITTAGREGEX", k[3])
|
self.d.setVar("GITTAGREGEX", k[3])
|
||||||
ud = bb.fetch2.FetchData(k[1], self.d)
|
ud = bb.fetch2.FetchData(k[1], self.d)
|
||||||
pupver= ud.method.latest_versionstring(ud, self.d)
|
pupver= ud.method.latest_versionstring(ud, self.d)
|
||||||
verstring = pupver[0]
|
verstring = pupver[0]
|
||||||
@@ -799,8 +715,8 @@ class FetchLatestVersionTest(FetcherTest):
|
|||||||
def test_wget_latest_versionstring(self):
|
def test_wget_latest_versionstring(self):
|
||||||
for k, v in self.test_wget_uris.items():
|
for k, v in self.test_wget_uris.items():
|
||||||
self.d.setVar("PN", k[0])
|
self.d.setVar("PN", k[0])
|
||||||
self.d.setVar("UPSTREAM_CHECK_URI", k[2])
|
self.d.setVar("REGEX_URI", k[2])
|
||||||
self.d.setVar("UPSTREAM_CHECK_REGEX", k[3])
|
self.d.setVar("REGEX", k[3])
|
||||||
ud = bb.fetch2.FetchData(k[1], self.d)
|
ud = bb.fetch2.FetchData(k[1], self.d)
|
||||||
pupver = ud.method.latest_versionstring(ud, self.d)
|
pupver = ud.method.latest_versionstring(ud, self.d)
|
||||||
verstring = pupver[0]
|
verstring = pupver[0]
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ C = "3"
|
|||||||
def parsehelper(self, content, suffix = ".bb"):
|
def parsehelper(self, content, suffix = ".bb"):
|
||||||
|
|
||||||
f = tempfile.NamedTemporaryFile(suffix = suffix)
|
f = tempfile.NamedTemporaryFile(suffix = suffix)
|
||||||
f.write(bytes(content, "utf-8"))
|
f.write(content)
|
||||||
f.flush()
|
f.flush()
|
||||||
os.chdir(os.path.dirname(f.name))
|
os.chdir(os.path.dirname(f.name))
|
||||||
return f
|
return f
|
||||||
@@ -68,23 +68,6 @@ C = "3"
|
|||||||
with self.assertRaises(bb.parse.ParseError):
|
with self.assertRaises(bb.parse.ParseError):
|
||||||
d = bb.parse.handle(f.name, self.d)['']
|
d = bb.parse.handle(f.name, self.d)['']
|
||||||
|
|
||||||
unsettest = """
|
|
||||||
A = "1"
|
|
||||||
B = "2"
|
|
||||||
B[flag] = "3"
|
|
||||||
|
|
||||||
unset A
|
|
||||||
unset B[flag]
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_parse_unset(self):
|
|
||||||
f = self.parsehelper(self.unsettest)
|
|
||||||
d = bb.parse.handle(f.name, self.d)['']
|
|
||||||
self.assertEqual(d.getVar("A", True), None)
|
|
||||||
self.assertEqual(d.getVarFlag("A","flag", True), None)
|
|
||||||
self.assertEqual(d.getVar("B", True), "2")
|
|
||||||
|
|
||||||
|
|
||||||
overridetest = """
|
overridetest = """
|
||||||
RRECOMMENDS_${PN} = "a"
|
RRECOMMENDS_${PN} = "a"
|
||||||
RRECOMMENDS_${PN}_libc = "b"
|
RRECOMMENDS_${PN}_libc = "b"
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import unittest
|
|||||||
import bb
|
import bb
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import re
|
|
||||||
|
|
||||||
class VerCmpString(unittest.TestCase):
|
class VerCmpString(unittest.TestCase):
|
||||||
|
|
||||||
@@ -177,7 +176,7 @@ do_functionname() {
|
|||||||
# Test file doesn't get modified with some the same values
|
# Test file doesn't get modified with some the same values
|
||||||
self._testeditfile({'THIS': ('that', None, 0, True),
|
self._testeditfile({'THIS': ('that', None, 0, True),
|
||||||
'OTHER': ('anothervalue', None, 0, True),
|
'OTHER': ('anothervalue', None, 0, True),
|
||||||
'MULTILINE3': (' c1 c2 c3 ', None, 4, False)}, self._origfile)
|
'MULTILINE3': (' c1 c2 c3', None, 4, False)}, self._origfile)
|
||||||
|
|
||||||
def test_edit_metadata_file_1(self):
|
def test_edit_metadata_file_1(self):
|
||||||
|
|
||||||
@@ -378,27 +377,6 @@ do_functionname() {
|
|||||||
self.assertTrue(updated, 'List should be updated but isn\'t')
|
self.assertTrue(updated, 'List should be updated but isn\'t')
|
||||||
self.assertEqual(newlines, newfile5.splitlines(True))
|
self.assertEqual(newlines, newfile5.splitlines(True))
|
||||||
|
|
||||||
# Make sure the orig value matches what we expect it to be
|
|
||||||
def test_edit_metadata_origvalue(self):
|
|
||||||
origfile = """
|
|
||||||
MULTILINE = " stuff \\
|
|
||||||
morestuff"
|
|
||||||
"""
|
|
||||||
expected_value = "stuff morestuff"
|
|
||||||
global value_in_callback
|
|
||||||
value_in_callback = ""
|
|
||||||
|
|
||||||
def handle_var(varname, origvalue, op, newlines):
|
|
||||||
global value_in_callback
|
|
||||||
value_in_callback = origvalue
|
|
||||||
return (origvalue, op, -1, False)
|
|
||||||
|
|
||||||
bb.utils.edit_metadata(origfile.splitlines(True),
|
|
||||||
['MULTILINE'],
|
|
||||||
handle_var)
|
|
||||||
|
|
||||||
testvalue = re.sub('\s+', ' ', value_in_callback.strip())
|
|
||||||
self.assertEqual(expected_value, testvalue)
|
|
||||||
|
|
||||||
class EditBbLayersConf(unittest.TestCase):
|
class EditBbLayersConf(unittest.TestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -51,23 +51,14 @@ class Tinfoil:
|
|||||||
features = []
|
features = []
|
||||||
if tracking:
|
if tracking:
|
||||||
features.append(CookerFeatures.BASEDATASTORE_TRACKING)
|
features.append(CookerFeatures.BASEDATASTORE_TRACKING)
|
||||||
cleanedvars = bb.utils.clean_environment()
|
|
||||||
self.cooker = BBCooker(self.config, features)
|
self.cooker = BBCooker(self.config, features)
|
||||||
self.config_data = self.cooker.data
|
self.config_data = self.cooker.data
|
||||||
bb.providers.logger.setLevel(logging.ERROR)
|
bb.providers.logger.setLevel(logging.ERROR)
|
||||||
self.cooker_data = None
|
self.cooker_data = None
|
||||||
for k in cleanedvars:
|
|
||||||
os.environ[k] = cleanedvars[k]
|
|
||||||
|
|
||||||
def register_idle_function(self, function, data):
|
def register_idle_function(self, function, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.shutdown()
|
|
||||||
|
|
||||||
def parseRecipes(self):
|
def parseRecipes(self):
|
||||||
sys.stderr.write("Parsing recipes..")
|
sys.stderr.write("Parsing recipes..")
|
||||||
self.logger.setLevel(logging.WARNING)
|
self.logger.setLevel(logging.WARNING)
|
||||||
@@ -83,52 +74,16 @@ class Tinfoil:
|
|||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
sys.stderr.write("done.\n")
|
sys.stderr.write("done.\n")
|
||||||
|
|
||||||
self.cooker_data = self.cooker.recipecaches['']
|
self.cooker_data = self.cooker.recipecache
|
||||||
|
|
||||||
def prepare(self, config_only = False):
|
def prepare(self, config_only = False):
|
||||||
if not self.cooker_data:
|
if not self.cooker_data:
|
||||||
if config_only:
|
if config_only:
|
||||||
self.cooker.parseConfiguration()
|
self.cooker.parseConfiguration()
|
||||||
self.cooker_data = self.cooker.recipecaches['']
|
self.cooker_data = self.cooker.recipecache
|
||||||
else:
|
else:
|
||||||
self.parseRecipes()
|
self.parseRecipes()
|
||||||
|
|
||||||
def parse_recipe_file(self, fn, appends=True, appendlist=None, config_data=None):
|
|
||||||
"""
|
|
||||||
Parse the specified recipe file (with or without bbappends)
|
|
||||||
and return a datastore object representing the environment
|
|
||||||
for the recipe.
|
|
||||||
Parameters:
|
|
||||||
fn: recipe file to parse - can be a file path or virtual
|
|
||||||
specification
|
|
||||||
appends: True to apply bbappends, False otherwise
|
|
||||||
appendlist: optional list of bbappend files to apply, if you
|
|
||||||
want to filter them
|
|
||||||
config_data: custom config datastore to use. NOTE: if you
|
|
||||||
specify config_data then you cannot use a virtual
|
|
||||||
specification for fn.
|
|
||||||
"""
|
|
||||||
if appends and appendlist == []:
|
|
||||||
appends = False
|
|
||||||
if appends:
|
|
||||||
if appendlist:
|
|
||||||
appendfiles = appendlist
|
|
||||||
else:
|
|
||||||
if not hasattr(self.cooker, 'collection'):
|
|
||||||
raise Exception('You must call tinfoil.prepare() with config_only=False in order to get bbappends')
|
|
||||||
appendfiles = self.cooker.collection.get_file_appends(fn)
|
|
||||||
else:
|
|
||||||
appendfiles = None
|
|
||||||
if config_data:
|
|
||||||
# We have to use a different function here if we're passing in a datastore
|
|
||||||
localdata = bb.data.createCopy(config_data)
|
|
||||||
envdata = bb.cache.parse_recipe(localdata, fn, appendfiles)['']
|
|
||||||
else:
|
|
||||||
# Use the standard path
|
|
||||||
parser = bb.cache.NoCache(self.cooker.databuilder)
|
|
||||||
envdata = parser.loadDataFull(fn, appendfiles)
|
|
||||||
return envdata
|
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.cooker.shutdown(force=True)
|
self.cooker.shutdown(force=True)
|
||||||
self.cooker.post_serve()
|
self.cooker.post_serve()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
17
bitbake/lib/bb/ui/crumbs/__init__.py
Normal file
17
bitbake/lib/bb/ui/crumbs/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
# Gtk+ UI pieces for BitBake
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006-2007 Richard Purdie
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
437
bitbake/lib/bb/ui/crumbs/builddetailspage.py
Executable file
437
bitbake/lib/bb/ui/crumbs/builddetailspage.py
Executable file
@@ -0,0 +1,437 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012 Intel Corporation
|
||||||
|
#
|
||||||
|
# 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 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.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):
|
||||||
|
gtk.TreeView.__init__(self)
|
||||||
|
self.set_rules_hint(False)
|
||||||
|
self.set_headers_visible(False)
|
||||||
|
self.set_property("hover-expand", True)
|
||||||
|
self.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
||||||
|
|
||||||
|
# The icon that indicates whether we're building or failed.
|
||||||
|
renderer0 = gtk.CellRendererText()
|
||||||
|
renderer0.set_property('font-desc', pango.FontDescription('courier bold 12'))
|
||||||
|
col0 = gtk.TreeViewColumn ("Name", renderer0, text=0)
|
||||||
|
self.append_column (col0)
|
||||||
|
|
||||||
|
# The message of configuration.
|
||||||
|
renderer1 = HobWarpCellRendererText(col_number=1)
|
||||||
|
col1 = gtk.TreeViewColumn ("Values", renderer1, text=1)
|
||||||
|
self.append_column (col1)
|
||||||
|
|
||||||
|
def set_vars(self, key="", var=[""]):
|
||||||
|
d = {}
|
||||||
|
if type(var) == str:
|
||||||
|
d = {key: [var]}
|
||||||
|
elif type(var) == list and len(var) > 1:
|
||||||
|
#create the sub item line
|
||||||
|
l = []
|
||||||
|
text = ""
|
||||||
|
for item in var:
|
||||||
|
text = " - " + item
|
||||||
|
l.append(text)
|
||||||
|
d = {key: var}
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
def set_config_model(self, show_vars):
|
||||||
|
listmodel = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
|
||||||
|
parent = None
|
||||||
|
for var in show_vars:
|
||||||
|
for subitem in var.items():
|
||||||
|
name = subitem[0]
|
||||||
|
is_parent = True
|
||||||
|
for value in subitem[1]:
|
||||||
|
if is_parent:
|
||||||
|
parent = listmodel.append(parent, (name, value))
|
||||||
|
is_parent = False
|
||||||
|
else:
|
||||||
|
listmodel.append(parent, (None, value))
|
||||||
|
name = " - "
|
||||||
|
parent = None
|
||||||
|
# renew the tree model after get the configuration messages
|
||||||
|
self.set_model(listmodel)
|
||||||
|
|
||||||
|
def show(self, src_config_info, src_params):
|
||||||
|
vars = []
|
||||||
|
vars.append(self.set_vars("BB version:", src_params.bb_version))
|
||||||
|
vars.append(self.set_vars("Target arch:", src_params.target_arch))
|
||||||
|
vars.append(self.set_vars("Target OS:", src_params.target_os))
|
||||||
|
vars.append(self.set_vars("Machine:", src_config_info.curr_mach))
|
||||||
|
vars.append(self.set_vars("Distro:", src_config_info.curr_distro))
|
||||||
|
vars.append(self.set_vars("Distro version:", src_params.distro_version))
|
||||||
|
vars.append(self.set_vars("SDK machine:", src_config_info.curr_sdk_machine))
|
||||||
|
vars.append(self.set_vars("Tune features:", src_params.tune_pkgarch))
|
||||||
|
vars.append(self.set_vars("Layers:", src_config_info.layers))
|
||||||
|
|
||||||
|
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')
|
||||||
|
vars.append(self.set_vars("Branch:", branch))
|
||||||
|
break
|
||||||
|
|
||||||
|
self.set_config_model(vars)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.set_model(None)
|
||||||
|
|
||||||
|
#
|
||||||
|
# BuildDetailsPage
|
||||||
|
#
|
||||||
|
|
||||||
|
class BuildDetailsPage (HobPage):
|
||||||
|
|
||||||
|
def __init__(self, builder):
|
||||||
|
super(BuildDetailsPage, self).__init__(builder, "Building ...")
|
||||||
|
|
||||||
|
self.num_of_issues = 0
|
||||||
|
self.endpath = (0,)
|
||||||
|
# create visual elements
|
||||||
|
self.create_visual_elements()
|
||||||
|
|
||||||
|
def create_visual_elements(self):
|
||||||
|
# create visual elements
|
||||||
|
self.vbox = gtk.VBox(False, 12)
|
||||||
|
|
||||||
|
self.progress_box = gtk.VBox(False, 12)
|
||||||
|
self.task_status = gtk.Label("\n") # to ensure layout is correct
|
||||||
|
self.task_status.set_alignment(0.0, 0.5)
|
||||||
|
self.progress_box.pack_start(self.task_status, expand=False, fill=False)
|
||||||
|
self.progress_hbox = gtk.HBox(False, 6)
|
||||||
|
self.progress_box.pack_end(self.progress_hbox, expand=True, fill=True)
|
||||||
|
self.progress_bar = HobProgressBar()
|
||||||
|
self.progress_hbox.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.stop_button.set_sensitive(False)
|
||||||
|
self.progress_hbox.pack_end(self.stop_button, expand=False, fill=False)
|
||||||
|
|
||||||
|
self.notebook = HobNotebook()
|
||||||
|
self.config_tv = BuildConfigurationTreeView()
|
||||||
|
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.failure_tv = BuildFailureTreeView()
|
||||||
|
self.failure_model = self.builder.handler.build.model.failure_model()
|
||||||
|
self.failure_tv.set_model(self.failure_model)
|
||||||
|
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.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.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('<< Back')
|
||||||
|
self.back_button.connect("clicked", self.back_button_clicked_cb)
|
||||||
|
self.button_box.pack_start(self.back_button, expand=False, fill=False)
|
||||||
|
|
||||||
|
def update_build_status(self, current, total, task):
|
||||||
|
recipe_path, recipe_task = task.split(", ")
|
||||||
|
recipe = os.path.basename(recipe_path).rstrip(".bb")
|
||||||
|
tsk_msg = "<b>Running task %s of %s:</b> %s\n<b>Recipe:</b> %s" % (current, total, recipe_task, recipe)
|
||||||
|
self.task_status.set_markup(tsk_msg)
|
||||||
|
self.stop_button.set_sensitive(True)
|
||||||
|
|
||||||
|
def reset_build_status(self):
|
||||||
|
self.task_status.set_markup("\n") # to ensure layout is correct
|
||||||
|
self.endpath = (0,)
|
||||||
|
|
||||||
|
def show_issues(self):
|
||||||
|
self.num_of_issues += 1
|
||||||
|
self.notebook.show_indicator_icon("Issues", self.num_of_issues)
|
||||||
|
self.notebook.queue_draw()
|
||||||
|
|
||||||
|
def reset_issues(self):
|
||||||
|
self.num_of_issues = 0
|
||||||
|
self.notebook.hide_indicator_icon("Issues")
|
||||||
|
|
||||||
|
def _remove_all_widget(self):
|
||||||
|
children = self.vbox.get_children() or []
|
||||||
|
for child in children:
|
||||||
|
self.vbox.remove(child)
|
||||||
|
children = self.box_group_area.get_children() or []
|
||||||
|
for child in children:
|
||||||
|
self.box_group_area.remove(child)
|
||||||
|
children = self.get_children() or []
|
||||||
|
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:
|
||||||
|
self.title = "Building packages ..."
|
||||||
|
else:
|
||||||
|
self.title = "Building image ..."
|
||||||
|
self.build_details_top = self.add_onto_top_bar(None)
|
||||||
|
self.pack_start(self.build_details_top, expand=False, fill=False)
|
||||||
|
self.pack_start(self.group_align, expand=True, fill=True)
|
||||||
|
|
||||||
|
self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
|
||||||
|
|
||||||
|
self.progress_bar.reset()
|
||||||
|
self.config_tv.reset()
|
||||||
|
self.vbox.pack_start(self.progress_box, expand=False, fill=False)
|
||||||
|
|
||||||
|
self.vbox.pack_start(self.notebook, expand=True, fill=True)
|
||||||
|
|
||||||
|
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()
|
||||||
|
self.reset_issues()
|
||||||
|
|
||||||
|
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 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()
|
||||||
|
|
||||||
|
def stop_button_clicked_cb(self, button):
|
||||||
|
self.builder.stop_build()
|
||||||
|
|
||||||
|
def hide_stop_button(self):
|
||||||
|
self.stop_button.set_sensitive(False)
|
||||||
|
self.stop_button.hide()
|
||||||
|
|
||||||
|
def scroll_to_present_row(self, model, path, iter, v_adj, treeview):
|
||||||
|
if treeview and v_adj:
|
||||||
|
if path[0] > self.endpath[0]: # check the event is a new row append or not
|
||||||
|
self.endpath = path
|
||||||
|
# check the gtk.adjustment position is at end boundary or not
|
||||||
|
if (v_adj.upper <= v_adj.page_size) or (v_adj.value == v_adj.upper - v_adj.page_size):
|
||||||
|
treeview.scroll_to_cell(path)
|
||||||
|
|
||||||
|
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()
|
||||||
|
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")
|
||||||
1475
bitbake/lib/bb/ui/crumbs/builder.py
Executable file
1475
bitbake/lib/bb/ui/crumbs/builder.py
Executable file
File diff suppressed because it is too large
Load Diff
455
bitbake/lib/bb/ui/crumbs/buildmanager.py
Normal file
455
bitbake/lib/bb/ui/crumbs/buildmanager.py
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2008 Intel Corporation
|
||||||
|
#
|
||||||
|
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
class BuildConfiguration:
|
||||||
|
""" Represents a potential *or* historic *or* concrete build. It
|
||||||
|
encompasses all the things that we need to tell bitbake to do to make it
|
||||||
|
build what we want it to build.
|
||||||
|
|
||||||
|
It also stored the metadata URL and the set of possible machines (and the
|
||||||
|
distros / images / uris for these. Apart from the metdata URL these are
|
||||||
|
not serialised to file (since they may be transient). In some ways this
|
||||||
|
functionality might be shifted to the loader class."""
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
self.metadata_url = None
|
||||||
|
|
||||||
|
# Tuple of (distros, image, urls)
|
||||||
|
self.machine_options = {}
|
||||||
|
|
||||||
|
self.machine = None
|
||||||
|
self.distro = None
|
||||||
|
self.image = None
|
||||||
|
self.urls = []
|
||||||
|
self.extra_urls = []
|
||||||
|
self.extra_pkgs = []
|
||||||
|
|
||||||
|
def get_machines_model (self):
|
||||||
|
model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
for machine in self.machine_options.keys():
|
||||||
|
model.append ([machine])
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
def get_distro_and_images_models (self, machine):
|
||||||
|
distro_model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
|
||||||
|
for distro in self.machine_options[machine][0]:
|
||||||
|
distro_model.append ([distro])
|
||||||
|
|
||||||
|
image_model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
|
||||||
|
for image in self.machine_options[machine][1]:
|
||||||
|
image_model.append ([image])
|
||||||
|
|
||||||
|
return (distro_model, image_model)
|
||||||
|
|
||||||
|
def get_repos (self):
|
||||||
|
self.urls = self.machine_options[self.machine][2]
|
||||||
|
return self.urls
|
||||||
|
|
||||||
|
# It might be a lot lot better if we stored these in like, bitbake conf
|
||||||
|
# file format.
|
||||||
|
@staticmethod
|
||||||
|
def load_from_file (filename):
|
||||||
|
|
||||||
|
conf = BuildConfiguration()
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
data = line.split (";")[1]
|
||||||
|
if (line.startswith ("metadata-url;")):
|
||||||
|
conf.metadata_url = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("url;")):
|
||||||
|
conf.urls += [data.strip()]
|
||||||
|
continue
|
||||||
|
if (line.startswith ("extra-url;")):
|
||||||
|
conf.extra_urls += [data.strip()]
|
||||||
|
continue
|
||||||
|
if (line.startswith ("machine;")):
|
||||||
|
conf.machine = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("distribution;")):
|
||||||
|
conf.distro = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("image;")):
|
||||||
|
conf.image = data.strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
return conf
|
||||||
|
|
||||||
|
# Serialise to a file. This is part of the build process and we use this
|
||||||
|
# to be able to repeat a given build (using the same set of parameters)
|
||||||
|
# but also so that we can include the details of the image / machine /
|
||||||
|
# distro in the build manager tree view.
|
||||||
|
def write_to_file (self, filename):
|
||||||
|
f = open (filename, "w")
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
if (self.metadata_url):
|
||||||
|
lines += ["metadata-url;%s\n" % (self.metadata_url)]
|
||||||
|
|
||||||
|
for url in self.urls:
|
||||||
|
lines += ["url;%s\n" % (url)]
|
||||||
|
|
||||||
|
for url in self.extra_urls:
|
||||||
|
lines += ["extra-url;%s\n" % (url)]
|
||||||
|
|
||||||
|
if (self.machine):
|
||||||
|
lines += ["machine;%s\n" % (self.machine)]
|
||||||
|
|
||||||
|
if (self.distro):
|
||||||
|
lines += ["distribution;%s\n" % (self.distro)]
|
||||||
|
|
||||||
|
if (self.image):
|
||||||
|
lines += ["image;%s\n" % (self.image)]
|
||||||
|
|
||||||
|
f.writelines (lines)
|
||||||
|
f.close ()
|
||||||
|
|
||||||
|
class BuildResult(gobject.GObject):
|
||||||
|
""" Represents an historic build. Perhaps not successful. But it includes
|
||||||
|
things such as the files that are in the directory (the output from the
|
||||||
|
build) as well as a deserialised BuildConfiguration file that is stored in
|
||||||
|
".conf" in the directory for the build.
|
||||||
|
|
||||||
|
This is GObject so that it can be included in the TreeStore."""
|
||||||
|
|
||||||
|
(STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \
|
||||||
|
(0, 1, 2)
|
||||||
|
|
||||||
|
def __init__ (self, parent, identifier):
|
||||||
|
gobject.GObject.__init__ (self)
|
||||||
|
self.date = None
|
||||||
|
|
||||||
|
self.files = []
|
||||||
|
self.status = None
|
||||||
|
self.identifier = identifier
|
||||||
|
self.path = os.path.join (parent, identifier)
|
||||||
|
|
||||||
|
# Extract the date, since the directory name is of the
|
||||||
|
# format build-<year><month><day>-<ordinal> we can easily
|
||||||
|
# pull it out.
|
||||||
|
# TODO: Better to stat a file?
|
||||||
|
(_, date, revision) = identifier.split ("-")
|
||||||
|
print(date)
|
||||||
|
|
||||||
|
year = int (date[0:4])
|
||||||
|
month = int (date[4:6])
|
||||||
|
day = int (date[6:8])
|
||||||
|
|
||||||
|
self.date = datetime.date (year, month, day)
|
||||||
|
|
||||||
|
self.conf = None
|
||||||
|
|
||||||
|
# By default builds are STATE_FAILED unless we find a "complete" file
|
||||||
|
# in which case they are STATE_COMPLETE
|
||||||
|
self.state = BuildResult.STATE_FAILED
|
||||||
|
for file in os.listdir (self.path):
|
||||||
|
if (file.startswith (".conf")):
|
||||||
|
conffile = os.path.join (self.path, file)
|
||||||
|
self.conf = BuildConfiguration.load_from_file (conffile)
|
||||||
|
elif (file.startswith ("complete")):
|
||||||
|
self.state = BuildResult.STATE_COMPLETE
|
||||||
|
else:
|
||||||
|
self.add_file (file)
|
||||||
|
|
||||||
|
def add_file (self, file):
|
||||||
|
# Just add the file for now. Don't care about the type.
|
||||||
|
self.files += [(file, None)]
|
||||||
|
|
||||||
|
class BuildManagerModel (gtk.TreeStore):
|
||||||
|
""" Model for the BuildManagerTreeView. This derives from gtk.TreeStore
|
||||||
|
but it abstracts nicely what the columns mean and the setup of the columns
|
||||||
|
in the model. """
|
||||||
|
|
||||||
|
(COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \
|
||||||
|
(0, 1, 2, 3, 4, 5, 6)
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.TreeStore.__init__ (self,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_OBJECT,
|
||||||
|
gobject.TYPE_INT64,
|
||||||
|
gobject.TYPE_INT)
|
||||||
|
|
||||||
|
class BuildManager (gobject.GObject):
|
||||||
|
""" This class manages the historic builds that have been found in the
|
||||||
|
"results" directory but is also used for starting a new build."""
|
||||||
|
|
||||||
|
__gsignals__ = {
|
||||||
|
'population-finished' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
()),
|
||||||
|
'populate-error' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
())
|
||||||
|
}
|
||||||
|
|
||||||
|
def update_build_result (self, result, iter):
|
||||||
|
# Convert the date into something we can sort by.
|
||||||
|
date = long (time.mktime (result.date.timetuple()))
|
||||||
|
|
||||||
|
# Add a top level entry for the build
|
||||||
|
|
||||||
|
self.model.set (iter,
|
||||||
|
BuildManagerModel.COL_IDENT, result.identifier,
|
||||||
|
BuildManagerModel.COL_DESC, result.conf.image,
|
||||||
|
BuildManagerModel.COL_MACHINE, result.conf.machine,
|
||||||
|
BuildManagerModel.COL_DISTRO, result.conf.distro,
|
||||||
|
BuildManagerModel.COL_BUILD_RESULT, result,
|
||||||
|
BuildManagerModel.COL_DATE, date,
|
||||||
|
BuildManagerModel.COL_STATE, result.state)
|
||||||
|
|
||||||
|
# And then we use the files in the directory as the children for the
|
||||||
|
# top level iter.
|
||||||
|
for file in result.files:
|
||||||
|
self.model.append (iter, (None, file[0], None, None, None, date, -1))
|
||||||
|
|
||||||
|
# This function is called as an idle by the BuildManagerPopulaterThread
|
||||||
|
def add_build_result (self, result):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.known_builds += [result]
|
||||||
|
|
||||||
|
self.update_build_result (result, self.model.append (None))
|
||||||
|
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
def notify_build_finished (self):
|
||||||
|
# This is a bit of a hack. If we have a running build running then we
|
||||||
|
# will have a row in the model in STATE_ONGOING. Find it and make it
|
||||||
|
# as if it was a proper historic build (well, it is completed now....)
|
||||||
|
|
||||||
|
# We need to use the iters here rather than the Python iterator
|
||||||
|
# interface to the model since we need to pass it into
|
||||||
|
# update_build_result
|
||||||
|
|
||||||
|
iter = self.model.get_iter_first()
|
||||||
|
|
||||||
|
while (iter):
|
||||||
|
(ident, state) = self.model.get(iter,
|
||||||
|
BuildManagerModel.COL_IDENT,
|
||||||
|
BuildManagerModel.COL_STATE)
|
||||||
|
|
||||||
|
if state == BuildResult.STATE_ONGOING:
|
||||||
|
result = BuildResult (self.results_directory, ident)
|
||||||
|
self.update_build_result (result, iter)
|
||||||
|
iter = self.model.iter_next(iter)
|
||||||
|
|
||||||
|
def notify_build_succeeded (self):
|
||||||
|
# Write the "complete" file so that when we create the BuildResult
|
||||||
|
# object we put into the model
|
||||||
|
|
||||||
|
complete_file_path = os.path.join (self.cur_build_directory, "complete")
|
||||||
|
f = file (complete_file_path, "w")
|
||||||
|
f.close()
|
||||||
|
self.notify_build_finished()
|
||||||
|
|
||||||
|
def notify_build_failed (self):
|
||||||
|
# Without a "complete" file then this will mark the build as failed:
|
||||||
|
self.notify_build_finished()
|
||||||
|
|
||||||
|
# This function is called as an idle
|
||||||
|
def emit_population_finished_signal (self):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.emit ("population-finished")
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
class BuildManagerPopulaterThread (threading.Thread):
|
||||||
|
def __init__ (self, manager, directory):
|
||||||
|
threading.Thread.__init__ (self)
|
||||||
|
self.manager = manager
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
def run (self):
|
||||||
|
# For each of the "build-<...>" directories ..
|
||||||
|
|
||||||
|
if os.path.exists (self.directory):
|
||||||
|
for directory in os.listdir (self.directory):
|
||||||
|
|
||||||
|
if not directory.startswith ("build-"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
build_result = BuildResult (self.directory, directory)
|
||||||
|
self.manager.add_build_result (build_result)
|
||||||
|
|
||||||
|
gobject.idle_add (BuildManager.emit_population_finished_signal,
|
||||||
|
self.manager)
|
||||||
|
|
||||||
|
def __init__ (self, server, results_directory):
|
||||||
|
gobject.GObject.__init__ (self)
|
||||||
|
|
||||||
|
# The builds that we've found from walking the result directory
|
||||||
|
self.known_builds = []
|
||||||
|
|
||||||
|
# Save out the bitbake server, we need this for issuing commands to
|
||||||
|
# the cooker:
|
||||||
|
self.server = server
|
||||||
|
|
||||||
|
# The TreeStore that we use
|
||||||
|
self.model = BuildManagerModel ()
|
||||||
|
|
||||||
|
# The results directory is where we create (and look for) the
|
||||||
|
# build-<xyz>-<n> directories. We need to populate ourselves from
|
||||||
|
# directory
|
||||||
|
self.results_directory = results_directory
|
||||||
|
self.populate_from_directory (self.results_directory)
|
||||||
|
|
||||||
|
def populate_from_directory (self, directory):
|
||||||
|
thread = BuildManager.BuildManagerPopulaterThread (self, directory)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
# Come up with the name for the next build ident by combining "build-"
|
||||||
|
# with the date formatted as yyyymmdd and then an ordinal. We do this by
|
||||||
|
# an optimistic algorithm incrementing the ordinal if we find that it
|
||||||
|
# already exists.
|
||||||
|
def get_next_build_ident (self):
|
||||||
|
today = datetime.date.today ()
|
||||||
|
datestr = str (today.year) + str (today.month) + str (today.day)
|
||||||
|
|
||||||
|
revision = 0
|
||||||
|
test_name = "build-%s-%d" % (datestr, revision)
|
||||||
|
test_path = os.path.join (self.results_directory, test_name)
|
||||||
|
|
||||||
|
while (os.path.exists (test_path)):
|
||||||
|
revision += 1
|
||||||
|
test_name = "build-%s-%d" % (datestr, revision)
|
||||||
|
test_path = os.path.join (self.results_directory, test_name)
|
||||||
|
|
||||||
|
return test_name
|
||||||
|
|
||||||
|
# Take a BuildConfiguration and then try and build it based on the
|
||||||
|
# parameters of that configuration. S
|
||||||
|
def do_build (self, conf):
|
||||||
|
server = self.server
|
||||||
|
|
||||||
|
# Work out the build directory. Note we actually create the
|
||||||
|
# directories here since we need to write the ".conf" file. Otherwise
|
||||||
|
# we could have relied on bitbake's builder thread to actually make
|
||||||
|
# the directories as it proceeds with the build.
|
||||||
|
ident = self.get_next_build_ident ()
|
||||||
|
build_directory = os.path.join (self.results_directory,
|
||||||
|
ident)
|
||||||
|
self.cur_build_directory = build_directory
|
||||||
|
os.makedirs (build_directory)
|
||||||
|
|
||||||
|
conffile = os.path.join (build_directory, ".conf")
|
||||||
|
conf.write_to_file (conffile)
|
||||||
|
|
||||||
|
# Add a row to the model representing this ongoing build. It's kinda a
|
||||||
|
# fake entry. If this build completes or fails then this gets updated
|
||||||
|
# with the real stuff like the historic builds
|
||||||
|
date = long (time.time())
|
||||||
|
self.model.append (None, (ident, conf.image, conf.machine, conf.distro,
|
||||||
|
None, date, BuildResult.STATE_ONGOING))
|
||||||
|
try:
|
||||||
|
server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1])
|
||||||
|
server.runCommand(["setVariable", "MACHINE", conf.machine])
|
||||||
|
server.runCommand(["setVariable", "DISTRO", conf.distro])
|
||||||
|
server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"])
|
||||||
|
server.runCommand(["setVariable", "BBFILES", \
|
||||||
|
"""${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""])
|
||||||
|
server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"])
|
||||||
|
server.runCommand(["setVariable", "IPK_FEED_URIS", \
|
||||||
|
" ".join(conf.get_repos())])
|
||||||
|
server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE",
|
||||||
|
build_directory])
|
||||||
|
server.runCommand(["buildTargets", [conf.image], "rootfs"])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
class BuildManagerTreeView (gtk.TreeView):
|
||||||
|
""" The tree view for the build manager. This shows the historic builds
|
||||||
|
and so forth. """
|
||||||
|
|
||||||
|
# We use this function to control what goes in the cell since we store
|
||||||
|
# the date in the model as seconds since the epoch (for sorting) and so we
|
||||||
|
# need to make it human readable.
|
||||||
|
def date_format_custom_cell_data_func (self, col, cell, model, iter):
|
||||||
|
date = model.get (iter, BuildManagerModel.COL_DATE)[0]
|
||||||
|
datestr = time.strftime("%A %d %B %Y", time.localtime(date))
|
||||||
|
cell.set_property ("text", datestr)
|
||||||
|
|
||||||
|
# This format function controls what goes in the cell. We use this to map
|
||||||
|
# the integer state to a string and also to colourise the text
|
||||||
|
def state_format_custom_cell_data_fun (self, col, cell, model, iter):
|
||||||
|
state = model.get (iter, BuildManagerModel.COL_STATE)[0]
|
||||||
|
|
||||||
|
if (state == BuildResult.STATE_ONGOING):
|
||||||
|
cell.set_property ("text", "Active")
|
||||||
|
cell.set_property ("foreground", "#000000")
|
||||||
|
elif (state == BuildResult.STATE_FAILED):
|
||||||
|
cell.set_property ("text", "Failed")
|
||||||
|
cell.set_property ("foreground", "#ff0000")
|
||||||
|
elif (state == BuildResult.STATE_COMPLETE):
|
||||||
|
cell.set_property ("text", "Complete")
|
||||||
|
cell.set_property ("foreground", "#00ff00")
|
||||||
|
else:
|
||||||
|
cell.set_property ("text", "")
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.TreeView.__init__(self)
|
||||||
|
|
||||||
|
# Misc descriptiony thing
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn (None, renderer,
|
||||||
|
text=BuildManagerModel.COL_DESC)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# Machine
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Machine", renderer,
|
||||||
|
text=BuildManagerModel.COL_MACHINE)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# distro
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Distribution", renderer,
|
||||||
|
text=BuildManagerModel.COL_DISTRO)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# date (using a custom function for formatting the cell contents it
|
||||||
|
# takes epoch -> human readable string)
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Date", renderer,
|
||||||
|
text=BuildManagerModel.COL_DATE)
|
||||||
|
self.append_column (col)
|
||||||
|
col.set_cell_data_func (renderer,
|
||||||
|
self.date_format_custom_cell_data_func)
|
||||||
|
|
||||||
|
# For status.
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Status", renderer,
|
||||||
|
text = BuildManagerModel.COL_STATE)
|
||||||
|
self.append_column (col)
|
||||||
|
col.set_cell_data_func (renderer,
|
||||||
|
self.state_format_custom_cell_data_fun)
|
||||||
341
bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py
Normal file
341
bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
#
|
||||||
|
# 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.sdk_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>"
|
||||||
|
msg = "You need to select at least one image type."
|
||||||
|
dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
|
||||||
|
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,"<b>Distro</b>" + "*" + 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("<b>Image types</b>" + "*" + 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"
|
||||||
|
if image_type == "live":
|
||||||
|
self.image_types_checkbuttons[image_type].set_tooltip_text("Build iso and hddimg images")
|
||||||
|
else:
|
||||||
|
self.image_types_checkbuttons[image_type].set_tooltip_text("Build a%s %s image" % (article, image_type))
|
||||||
|
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,"<b>Root file system package format</b>" + "*" + tooltip_combo,"<b>Additional package formats</b>" + "*" + tooltip_extra)
|
||||||
|
sub_vbox.pack_start(pkgfmt_widget, expand=False, fill=False)
|
||||||
|
|
||||||
|
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Image size</span>'), expand=False, fill=False)
|
||||||
|
sub_vbox = gtk.VBox(False, 6)
|
||||||
|
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||||
|
label = self.gen_label_widget("Image basic size (in MB)")
|
||||||
|
tooltip = "Defines the size for the generated image. The OpenEmbedded build system determines the final size for the generated image using an algorithm that takes into account the initial disk space used for the generated image, the Image basic size value, and the Additional free space value.\n\nFor more information, check the <a href=\"http://www.yoctoproject.org/docs/current/poky-ref-manual/poky-ref-manual.html#var-IMAGE_ROOTFS_SIZE\">Yocto Project Reference Manual</a>."
|
||||||
|
rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536,"<b>Image basic size</b>" + "*" + tooltip)
|
||||||
|
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||||
|
sub_vbox.pack_start(rootfs_size_widget, expand=False, fill=False)
|
||||||
|
|
||||||
|
sub_vbox = gtk.VBox(False, 6)
|
||||||
|
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||||
|
label = self.gen_label_widget("Additional free space (in MB)")
|
||||||
|
tooltip = "Sets extra free disk space to be added to the generated image. Use this variable when you want to ensure that a specific amount of free disk space is available on a device after an image is installed and running."
|
||||||
|
extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536,"<b>Additional free space</b>" + "*" + tooltip)
|
||||||
|
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">SDK</span>'), expand=False, fill=False)
|
||||||
|
sub_hbox = gtk.HBox(False, 6)
|
||||||
|
advanced_vbox.pack_start(sub_hbox, expand=False, fill=False)
|
||||||
|
self.sdk_checkbox = gtk.CheckButton("Populate SDK")
|
||||||
|
tooltip = "Check this box to generate an SDK tarball that consists of the cross-toolchain and a sysroot that contains development packages for your image."
|
||||||
|
self.sdk_checkbox.set_tooltip_text(tooltip)
|
||||||
|
self.sdk_checkbox.set_active(self.configuration.toolchain_build)
|
||||||
|
sub_hbox.pack_start(self.sdk_checkbox, expand=False, fill=False)
|
||||||
|
|
||||||
|
tooltip = "Select the host platform for which you want to run the toolchain contained in the SDK tarball."
|
||||||
|
sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines,"<b>Populate SDK</b>" + "*" + tooltip)
|
||||||
|
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.sdk_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)
|
||||||
44
bitbake/lib/bb/ui/crumbs/hig/crumbsdialog.py
Normal file
44
bitbake/lib/bb/ui/crumbs/hig/crumbsdialog.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#
|
||||||
|
# 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)
|
||||||
70
bitbake/lib/bb/ui/crumbs/hig/crumbsmessagedialog.py
Normal file
70
bitbake/lib/bb/ui/crumbs/hig/crumbsmessagedialog.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#
|
||||||
|
# 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(gtk.MessageDialog):
|
||||||
|
"""
|
||||||
|
A GNOME HIG compliant dialog widget.
|
||||||
|
Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
|
||||||
|
"""
|
||||||
|
def __init__(self, parent = None, label="", dialog_type = gtk.MESSAGE_QUESTION, msg=""):
|
||||||
|
super(CrumbsMessageDialog, self).__init__(None,
|
||||||
|
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
dialog_type,
|
||||||
|
gtk.BUTTONS_NONE,
|
||||||
|
None)
|
||||||
|
|
||||||
|
self.set_skip_taskbar_hint(False)
|
||||||
|
|
||||||
|
self.set_markup(label)
|
||||||
|
|
||||||
|
if 0 <= len(msg) < 300:
|
||||||
|
self.format_secondary_markup(msg)
|
||||||
|
else:
|
||||||
|
vbox = self.get_message_area()
|
||||||
|
vbox.set_border_width(1)
|
||||||
|
vbox.set_property("spacing", 12)
|
||||||
|
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()
|
||||||
|
vbox.add(self.textWindow)
|
||||||
|
self.textWindow.show()
|
||||||
219
bitbake/lib/bb/ui/crumbs/hig/deployimagedialog.py
Normal file
219
bitbake/lib/bb/ui/crumbs/hig/deployimagedialog.py
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
#
|
||||||
|
# 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 = ''
|
||||||
|
msg = ''
|
||||||
|
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 + " && sync; 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>"
|
||||||
|
msg = "Please 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>"
|
||||||
|
msg = "You have not selected an image to deploy."
|
||||||
|
else:
|
||||||
|
lbl = "<b>No selection made.</b>"
|
||||||
|
msg = "You have not selected a USB device."
|
||||||
|
if len(lbl):
|
||||||
|
crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
|
||||||
|
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()
|
||||||
172
bitbake/lib/bb/ui/crumbs/hig/imageselectiondialog.py
Normal file
172
bitbake/lib/bb/ui/crumbs/hig/imageselectiondialog.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
#
|
||||||
|
# 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__, "Images")
|
||||||
|
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)
|
||||||
298
bitbake/lib/bb/ui/crumbs/hig/layerselectiondialog.py
Normal file
298
bitbake/lib/bb/ui/crumbs/hig/layerselectiondialog.py
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
#
|
||||||
|
# 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>"
|
||||||
|
msg = "Unable 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:
|
||||||
|
msg += "it is an invalid path."
|
||||||
|
elif not os.path.exists(path+"/conf/layer.conf"):
|
||||||
|
msg += "there is no layer.conf inside the directory."
|
||||||
|
elif path in layers:
|
||||||
|
msg += "it is already in loaded layers."
|
||||||
|
else:
|
||||||
|
layer_store.append([path])
|
||||||
|
return
|
||||||
|
dialog = CrumbsMessageDialog(parent, lbl, gtk.MESSAGE_ERROR, msg)
|
||||||
|
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
|
||||||
|
dnd_internal_target = ''
|
||||||
|
dnd_targets = [(dnd_internal_target, gtk.TARGET_SAME_WIDGET, 0)]
|
||||||
|
layer_tv.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
|
||||||
|
dnd_targets,
|
||||||
|
gtk.gdk.ACTION_MOVE)
|
||||||
|
layer_tv.enable_model_drag_dest(dnd_targets,
|
||||||
|
gtk.gdk.ACTION_MOVE)
|
||||||
|
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)
|
||||||
163
bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py
Normal file
163
bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#
|
||||||
|
# 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, 0)
|
||||||
|
self.warning_label = gtk.Label()
|
||||||
|
self.warning_label.set_selectable(True)
|
||||||
|
self.warning_label.set_alignment(0, 0)
|
||||||
|
self.textWindow = gtk.ScrolledWindow()
|
||||||
|
|
||||||
|
table = gtk.Table(1, 10, False)
|
||||||
|
|
||||||
|
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("Close", gtk.RESPONSE_CANCEL)
|
||||||
|
HobAltButton.style_button(cancel_button)
|
||||||
|
|
||||||
|
self.refresh_components()
|
||||||
437
bitbake/lib/bb/ui/crumbs/hig/propertydialog.py
Normal file
437
bitbake/lib/bb/ui/crumbs/hig/propertydialog.py
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011-2013 Intel Corporation
|
||||||
|
#
|
||||||
|
# Authored by Andrei Dinu <andrei.adrianx.dinu@intel.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import string
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import glib
|
||||||
|
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||||
|
from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
|
||||||
|
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
|
||||||
|
from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
|
||||||
|
|
||||||
|
"""
|
||||||
|
The following are convenience classes for implementing GNOME HIG compliant
|
||||||
|
BitBake GUI's
|
||||||
|
In summary: spacing = 12px, border-width = 6px
|
||||||
|
"""
|
||||||
|
|
||||||
|
class PropertyDialog(CrumbsDialog):
|
||||||
|
|
||||||
|
def __init__(self, title, parent, information, flags, buttons=None):
|
||||||
|
|
||||||
|
super(PropertyDialog, self).__init__(title, parent, flags, buttons)
|
||||||
|
|
||||||
|
self.properties = information
|
||||||
|
|
||||||
|
if len(self.properties) == 10:
|
||||||
|
self.create_recipe_visual_elements()
|
||||||
|
elif len(self.properties) == 5:
|
||||||
|
self.create_package_visual_elements()
|
||||||
|
else:
|
||||||
|
self.create_information_visual_elements()
|
||||||
|
|
||||||
|
|
||||||
|
def create_information_visual_elements(self):
|
||||||
|
|
||||||
|
HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("icons/"))
|
||||||
|
ICON_PACKAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
|
||||||
|
|
||||||
|
self.set_resizable(False)
|
||||||
|
|
||||||
|
self.table = gtk.Table(1,1,False)
|
||||||
|
self.table.set_row_spacings(0)
|
||||||
|
self.table.set_col_spacings(0)
|
||||||
|
|
||||||
|
self.image = gtk.Image()
|
||||||
|
self.image.set_from_file(ICON_PACKAGES_DISPLAY_FILE)
|
||||||
|
self.image.set_property("xalign",0)
|
||||||
|
#self.vbox.add(self.image)
|
||||||
|
|
||||||
|
image_info = self.properties.split("*")[0]
|
||||||
|
info = self.properties.split("*")[1]
|
||||||
|
|
||||||
|
vbox = gtk.VBox(True, spacing=30)
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_line_wrap(False)
|
||||||
|
self.label_short.set_markup(image_info)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.info_label = gtk.Label()
|
||||||
|
self.info_label.set_line_wrap(True)
|
||||||
|
self.info_label.set_markup(info)
|
||||||
|
self.info_label.set_property("yalign", 0.5)
|
||||||
|
|
||||||
|
self.table.attach(self.image, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=5,ypadding=5)
|
||||||
|
self.table.attach(self.label_short, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=5)
|
||||||
|
self.table.attach(self.info_label, 0,1,1,2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=10)
|
||||||
|
|
||||||
|
self.vbox.add(self.table)
|
||||||
|
self.connect('delete-event', lambda w, e: self.destroy() or True)
|
||||||
|
|
||||||
|
def treeViewTooltip( self, widget, e, tooltips, cell, emptyText="" ):
|
||||||
|
try:
|
||||||
|
(path,col,x,y) = widget.get_path_at_pos( int(e.x), int(e.y) )
|
||||||
|
it = widget.get_model().get_iter(path)
|
||||||
|
value = widget.get_model().get_value(it,cell)
|
||||||
|
if value in self.tooltip_items:
|
||||||
|
tooltips.set_tip(widget, self.tooltip_items[value])
|
||||||
|
tooltips.enable()
|
||||||
|
else:
|
||||||
|
tooltips.set_tip(widget, emptyText)
|
||||||
|
except:
|
||||||
|
tooltips.set_tip(widget, emptyText)
|
||||||
|
|
||||||
|
|
||||||
|
def create_package_visual_elements(self):
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
name = self.properties['name']
|
||||||
|
binb = self.properties['binb']
|
||||||
|
size = self.properties['size']
|
||||||
|
recipe = self.properties['recipe']
|
||||||
|
file_list = json.loads(self.properties['files_list'])
|
||||||
|
|
||||||
|
files_temp = ''
|
||||||
|
paths_temp = ''
|
||||||
|
files_binb = []
|
||||||
|
paths_binb = []
|
||||||
|
|
||||||
|
self.tooltip_items = {}
|
||||||
|
|
||||||
|
self.set_resizable(False)
|
||||||
|
|
||||||
|
#cleaning out the recipe variable
|
||||||
|
recipe = recipe.split("+")[0]
|
||||||
|
|
||||||
|
vbox = gtk.VBox(True,spacing = 0)
|
||||||
|
|
||||||
|
###################################### NAME ROW + COL #################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_size_request(300,-1)
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
###################################### SIZE ROW + COL ######################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_size_request(300,-1)
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Size: </span>" + size)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
##################################### RECIPE ROW + COL #########################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_size_request(300,-1)
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Recipe: </span>" + recipe)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
##################################### BINB ROW + COL #######################################
|
||||||
|
|
||||||
|
if binb != '':
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.label_info = gtk.Label()
|
||||||
|
self.label_info.set_size_request(300,-1)
|
||||||
|
self.label_info.set_selectable(True)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
self.label_info.set_markup(binb)
|
||||||
|
self.label_info.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
self.vbox.add(self.label_info)
|
||||||
|
|
||||||
|
#################################### FILES BROUGHT BY PACKAGES ###################################
|
||||||
|
|
||||||
|
if file_list:
|
||||||
|
|
||||||
|
self.textWindow = gtk.ScrolledWindow()
|
||||||
|
self.textWindow.set_shadow_type(gtk.SHADOW_IN)
|
||||||
|
self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||||
|
self.textWindow.set_size_request(100, 170)
|
||||||
|
|
||||||
|
packagefiles_store = gtk.ListStore(str)
|
||||||
|
|
||||||
|
self.packagefiles_tv = gtk.TreeView()
|
||||||
|
self.packagefiles_tv.set_rules_hint(True)
|
||||||
|
self.packagefiles_tv.set_headers_visible(True)
|
||||||
|
self.textWindow.add(self.packagefiles_tv)
|
||||||
|
|
||||||
|
self.cell1 = gtk.CellRendererText()
|
||||||
|
col1 = gtk.TreeViewColumn('Package files', self.cell1)
|
||||||
|
col1.set_cell_data_func(self.cell1, self.regex_field)
|
||||||
|
self.packagefiles_tv.append_column(col1)
|
||||||
|
|
||||||
|
items = file_list.keys()
|
||||||
|
items.sort()
|
||||||
|
for item in items:
|
||||||
|
fullpath = item
|
||||||
|
while len(item) > 35:
|
||||||
|
item = item[:len(item)/2] + "" + item[len(item)/2+1:]
|
||||||
|
if len(item) == 35:
|
||||||
|
item = item[:len(item)/2] + "..." + item[len(item)/2+3:]
|
||||||
|
self.tooltip_items[item] = fullpath
|
||||||
|
|
||||||
|
packagefiles_store.append([str(item)])
|
||||||
|
|
||||||
|
self.packagefiles_tv.set_model(packagefiles_store)
|
||||||
|
|
||||||
|
tips = gtk.Tooltips()
|
||||||
|
tips.set_tip(self.packagefiles_tv, "")
|
||||||
|
self.packagefiles_tv.connect("motion-notify-event", self.treeViewTooltip, tips, 0)
|
||||||
|
self.packagefiles_tv.set_events(gtk.gdk.POINTER_MOTION_MASK)
|
||||||
|
|
||||||
|
self.vbox.add(self.textWindow)
|
||||||
|
|
||||||
|
self.vbox.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
def regex_field(self, column, cell, model, iter):
|
||||||
|
cell.set_property('text', model.get_value(iter, 0))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def create_recipe_visual_elements(self):
|
||||||
|
|
||||||
|
summary = self.properties['summary']
|
||||||
|
name = self.properties['name']
|
||||||
|
version = self.properties['version']
|
||||||
|
revision = self.properties['revision']
|
||||||
|
binb = self.properties['binb']
|
||||||
|
group = self.properties['group']
|
||||||
|
license = self.properties['license']
|
||||||
|
homepage = self.properties['homepage']
|
||||||
|
bugtracker = self.properties['bugtracker']
|
||||||
|
description = self.properties['description']
|
||||||
|
|
||||||
|
self.set_resizable(False)
|
||||||
|
|
||||||
|
#cleaning out the version variable and also the summary
|
||||||
|
version = version.split(":")[1]
|
||||||
|
if len(version) > 30:
|
||||||
|
version = version.split("+")[0]
|
||||||
|
else:
|
||||||
|
version = version.split("-")[0]
|
||||||
|
license = license.replace("&" , "and")
|
||||||
|
if (homepage == ''):
|
||||||
|
homepage = 'unknown'
|
||||||
|
if (bugtracker == ''):
|
||||||
|
bugtracker = 'unknown'
|
||||||
|
summary = summary.split("+")[0]
|
||||||
|
|
||||||
|
#calculating the rows needed for the table
|
||||||
|
binb_items_count = len(binb.split(','))
|
||||||
|
binb_items = binb.split(',')
|
||||||
|
|
||||||
|
vbox = gtk.VBox(False,spacing = 0)
|
||||||
|
|
||||||
|
######################################## SUMMARY LABEL #########################################
|
||||||
|
|
||||||
|
if summary != '':
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_width_chars(37)
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<b>" + summary + "</b>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
########################################## NAME ROW + COL #######################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
####################################### VERSION ROW + COL ####################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Version: </span>" + version)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
##################################### REVISION ROW + COL #####################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Revision: </span>" + revision)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
################################## GROUP ROW + COL ############################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Group: </span>" + group)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
################################# HOMEPAGE ROW + COL ############################################
|
||||||
|
|
||||||
|
if homepage != 'unknown':
|
||||||
|
self.label_info = gtk.Label()
|
||||||
|
self.label_info.set_selectable(True)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
if len(homepage) > 35:
|
||||||
|
self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:35] + "..." + "</a>")
|
||||||
|
else:
|
||||||
|
self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:60] + "</a>")
|
||||||
|
|
||||||
|
self.label_info.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<b>Homepage: </b>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
self.vbox.add(self.label_info)
|
||||||
|
|
||||||
|
################################# BUGTRACKER ROW + COL ###########################################
|
||||||
|
|
||||||
|
if bugtracker != 'unknown':
|
||||||
|
self.label_info = gtk.Label()
|
||||||
|
self.label_info.set_selectable(True)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
if len(bugtracker) > 35:
|
||||||
|
self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:35] + "..." + "</a>")
|
||||||
|
else:
|
||||||
|
self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:60] + "</a>")
|
||||||
|
self.label_info.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<b>Bugtracker: </b>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
self.vbox.add(self.label_info)
|
||||||
|
|
||||||
|
################################# LICENSE ROW + COL ############################################
|
||||||
|
|
||||||
|
self.label_info = gtk.Label()
|
||||||
|
self.label_info.set_selectable(True)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
self.label_info.set_markup(license)
|
||||||
|
self.label_info.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">License: </span>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
self.vbox.add(self.label_info)
|
||||||
|
|
||||||
|
################################### BINB ROW+COL #############################################
|
||||||
|
|
||||||
|
if binb != '':
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
self.label_info = gtk.Label()
|
||||||
|
self.label_info.set_selectable(True)
|
||||||
|
self.label_info.set_width_chars(36)
|
||||||
|
if len(binb) > 200:
|
||||||
|
scrolled_window = gtk.ScrolledWindow()
|
||||||
|
scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
|
||||||
|
scrolled_window.set_size_request(100,100)
|
||||||
|
self.label_info.set_markup(binb)
|
||||||
|
self.label_info.set_padding(6,6)
|
||||||
|
self.label_info.set_alignment(0,0)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
scrolled_window.add_with_viewport(self.label_info)
|
||||||
|
self.vbox.add(scrolled_window)
|
||||||
|
else:
|
||||||
|
self.label_info.set_markup(binb)
|
||||||
|
self.label_info.set_property("xalign", 0)
|
||||||
|
self.label_info.set_line_wrap(True)
|
||||||
|
self.vbox.add(self.label_info)
|
||||||
|
|
||||||
|
################################ DESCRIPTION TAG ROW #################################################
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.label_short.set_markup("<span weight=\"bold\">Description </span>")
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
################################ DESCRIPTION INFORMATION ROW ##########################################
|
||||||
|
|
||||||
|
hbox = gtk.HBox(True,spacing = 0)
|
||||||
|
|
||||||
|
self.label_short = gtk.Label()
|
||||||
|
self.label_short.set_selectable(True)
|
||||||
|
self.label_short.set_width_chars(36)
|
||||||
|
if len(description) > 200:
|
||||||
|
scrolled_window = gtk.ScrolledWindow()
|
||||||
|
scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
|
||||||
|
scrolled_window.set_size_request(100,100)
|
||||||
|
self.label_short.set_markup(description)
|
||||||
|
self.label_short.set_padding(6,6)
|
||||||
|
self.label_short.set_alignment(0,0)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
scrolled_window.add_with_viewport(self.label_short)
|
||||||
|
self.vbox.add(scrolled_window)
|
||||||
|
else:
|
||||||
|
self.label_short.set_markup(description)
|
||||||
|
self.label_short.set_property("xalign", 0)
|
||||||
|
self.label_short.set_line_wrap(True)
|
||||||
|
self.vbox.add(self.label_short)
|
||||||
|
|
||||||
|
self.vbox.show_all()
|
||||||
90
bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py
Normal file
90
bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
51
bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py
Normal file
51
bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Intel Corporation
|
||||||
|
#
|
||||||
|
# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import gtk
|
||||||
|
|
||||||
|
class RetrieveImageDialog (gtk.FileChooserDialog):
|
||||||
|
"""
|
||||||
|
This class is used to create a dialog that permits to retrieve
|
||||||
|
a custom image saved previously from Hob.
|
||||||
|
"""
|
||||||
|
def __init__(self, directory,title, parent, flags, buttons=None):
|
||||||
|
super(RetrieveImageDialog, self).__init__(title, None, gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
# create visual elements on the dialog
|
||||||
|
self.create_visual_elements()
|
||||||
|
|
||||||
|
def create_visual_elements(self):
|
||||||
|
self.set_show_hidden(True)
|
||||||
|
self.set_default_response(gtk.RESPONSE_OK)
|
||||||
|
self.set_current_folder(self.directory)
|
||||||
|
|
||||||
|
vbox = self.get_children()[0].get_children()[0].get_children()[0]
|
||||||
|
for child in vbox.get_children()[0].get_children()[0].get_children()[0].get_children():
|
||||||
|
vbox.get_children()[0].get_children()[0].get_children()[0].remove(child)
|
||||||
|
|
||||||
|
label1 = gtk.Label()
|
||||||
|
label1.set_text("File system" + self.directory)
|
||||||
|
label1.show()
|
||||||
|
vbox.get_children()[0].get_children()[0].get_children()[0].pack_start(label1, expand=False, fill=False, padding=0)
|
||||||
|
vbox.get_children()[0].get_children()[1].get_children()[0].hide()
|
||||||
|
|
||||||
|
self.get_children()[0].get_children()[1].get_children()[0].set_label("Select")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user