mirror of
https://git.yoctoproject.org/poky
synced 2026-01-31 13:58:42 +01:00
Compare commits
468 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73cc31c11a | ||
|
|
444dc2e99b | ||
|
|
bddb60b101 | ||
|
|
1083d90888 | ||
|
|
54e3f82bd7 | ||
|
|
426bc4c357 | ||
|
|
3ca9f90dff | ||
|
|
ccc964cf9f | ||
|
|
50fdd78423 | ||
|
|
3cf0e09348 | ||
|
|
4515fc9529 | ||
|
|
628aea354d | ||
|
|
3565a9697f | ||
|
|
fe7fb00221 | ||
|
|
7241042b70 | ||
|
|
546c0cffca | ||
|
|
224e04d6ce | ||
|
|
172105c1ef | ||
|
|
0fa93e1412 | ||
|
|
d54e1f4ff5 | ||
|
|
b24988bec7 | ||
|
|
a220e2ca34 | ||
|
|
3ac7c847e8 | ||
|
|
80b35ed1a2 | ||
|
|
7b9e031355 | ||
|
|
cb5649cbb8 | ||
|
|
dd20601980 | ||
|
|
d3c0a560a8 | ||
|
|
62685cbff5 | ||
|
|
3d9f6dc163 | ||
|
|
8aea6ad597 | ||
|
|
051883f877 | ||
|
|
0c78f81485 | ||
|
|
98f3e83884 | ||
|
|
819f7c3d03 | ||
|
|
4245995f76 | ||
|
|
0e8fcf8c9c | ||
|
|
13f0eee08d | ||
|
|
6eb266a365 | ||
|
|
577eb635ab | ||
|
|
553d5f65e8 | ||
|
|
47ef871649 | ||
|
|
db0832ead6 | ||
|
|
863bfa81af | ||
|
|
014af27dcb | ||
|
|
ca4703b6cf | ||
|
|
98e368e4b6 | ||
|
|
3c61ee2f68 | ||
|
|
ec00137169 | ||
|
|
11b217d60b | ||
|
|
c71ea3831a | ||
|
|
3428c1db71 | ||
|
|
4cd7b56228 | ||
|
|
5dd02c6db1 | ||
|
|
0ed07f2658 | ||
|
|
c33bac8883 | ||
|
|
c76d565ce2 | ||
|
|
04f04d0d17 | ||
|
|
d8cbc618cc | ||
|
|
1c73e41159 | ||
|
|
212ca3bee1 | ||
|
|
384801e827 | ||
|
|
5c9148ff6a | ||
|
|
cec5e508ec | ||
|
|
ddc6a9f5cd | ||
|
|
8b50a8676b | ||
|
|
12afe3c057 | ||
|
|
f5e807efc7 | ||
|
|
cf7507f8c4 | ||
|
|
eb0dff0c98 | ||
|
|
9a72d46aed | ||
|
|
ad2cce0f1e | ||
|
|
c96936cfd9 | ||
|
|
047e58b4ba | ||
|
|
485e244db8 | ||
|
|
e8676b4f1a | ||
|
|
cef5f86f43 | ||
|
|
1a2ec16ec0 | ||
|
|
035c33c405 | ||
|
|
8aaffcd59a | ||
|
|
73274f258a | ||
|
|
b291829cfc | ||
|
|
1bccc216ee | ||
|
|
8f4b7758b5 | ||
|
|
95dae8b598 | ||
|
|
129060f0b7 | ||
|
|
f69b958176 | ||
|
|
c3c14808dc | ||
|
|
c60a0a51d7 | ||
|
|
e59717e80f | ||
|
|
b4df9df462 | ||
|
|
ae9b341ecf | ||
|
|
3bf928a3b6 | ||
|
|
0742e8a43b | ||
|
|
cca8dd15c8 | ||
|
|
8e4188e274 | ||
|
|
0ad194919f | ||
|
|
49a01fd044 | ||
|
|
0aedf304e5 | ||
|
|
f0f6acac03 | ||
|
|
bae35b3e5f | ||
|
|
2de121703d | ||
|
|
8a12e713f9 | ||
|
|
2b0f105e59 | ||
|
|
c9f172aa5e | ||
|
|
f7e1cd9f85 | ||
|
|
ec240f45ae | ||
|
|
e92679a6eb | ||
|
|
f979c50029 | ||
|
|
a6b8fda00c | ||
|
|
1b9a98f78c | ||
|
|
d72e66f34b | ||
|
|
e2c2d723ed | ||
|
|
478a38187f | ||
|
|
cc811f4992 | ||
|
|
f7ec29ca3f | ||
|
|
0d390bfb5a | ||
|
|
ca9d26a08d | ||
|
|
49de8caab0 | ||
|
|
d672a4cc3c | ||
|
|
be15df5099 | ||
|
|
2cb87d12d2 | ||
|
|
57531002b8 | ||
|
|
c4061a0a68 | ||
|
|
6962ee3689 | ||
|
|
191666022a | ||
|
|
53766fb01f | ||
|
|
3134fb2861 | ||
|
|
b169435134 | ||
|
|
95e3d71080 | ||
|
|
2de1a5cefb | ||
|
|
a7c3e18de0 | ||
|
|
bd2cc670be | ||
|
|
b108f2a6de | ||
|
|
2fcc8d6e52 | ||
|
|
c3c25ac53d | ||
|
|
7343438092 | ||
|
|
8f5becc3ab | ||
|
|
732dd581f3 | ||
|
|
40f4a6d075 | ||
|
|
88b7f1a1e2 | ||
|
|
8e2ab57852 | ||
|
|
204b2bae4a | ||
|
|
e93596fe74 | ||
|
|
56a27c9aad | ||
|
|
4b27738c5e | ||
|
|
529bbe2cc2 | ||
|
|
82641d700d | ||
|
|
118f7a2247 | ||
|
|
5b24e5b39b | ||
|
|
a78dddb624 | ||
|
|
3b3cdfd71a | ||
|
|
ed4ed5313b | ||
|
|
de056577ce | ||
|
|
2ea93e2b1d | ||
|
|
2b330e5439 | ||
|
|
e08094e604 | ||
|
|
5f97311702 | ||
|
|
7026b2b05a | ||
|
|
8e5e92193a | ||
|
|
06ed5c5a10 | ||
|
|
9fa0bc4500 | ||
|
|
82017f2367 | ||
|
|
e1e5b18a5e | ||
|
|
9995a7a144 | ||
|
|
9fd6b093a4 | ||
|
|
b7bb83a4bb | ||
|
|
45bc60015c | ||
|
|
e6c1d03d3d | ||
|
|
d2ca721d31 | ||
|
|
260ff60f93 | ||
|
|
71291ed53e | ||
|
|
5b3af2abd7 | ||
|
|
70c4134e4b | ||
|
|
90dd677528 | ||
|
|
6db9299d9e | ||
|
|
2561b58ac8 | ||
|
|
9e14b83fa4 | ||
|
|
a8ac03fce1 | ||
|
|
8b9b998258 | ||
|
|
76aa0c3d5d | ||
|
|
11c8c8aa15 | ||
|
|
5a8a6a753f | ||
|
|
aa4b7b2257 | ||
|
|
ea62893915 | ||
|
|
990b8e7919 | ||
|
|
db8258864e | ||
|
|
58538b0703 | ||
|
|
96fe15caf6 | ||
|
|
b6e4966874 | ||
|
|
a837c6be8f | ||
|
|
414aad04b6 | ||
|
|
8a7607f470 | ||
|
|
cce2867828 | ||
|
|
0458275013 | ||
|
|
6f60d91adc | ||
|
|
642890f5d0 | ||
|
|
5368cfee9e | ||
|
|
e588da43b0 | ||
|
|
c32c7522e5 | ||
|
|
62696defc0 | ||
|
|
deca0d3736 | ||
|
|
a220c3a1a9 | ||
|
|
ef6ff739c7 | ||
|
|
d9369d1ea0 | ||
|
|
17e4586d6e | ||
|
|
6175bd0930 | ||
|
|
016df260e5 | ||
|
|
5d781f41ff | ||
|
|
d3ee5489c9 | ||
|
|
9a1694e242 | ||
|
|
3cc3ff6244 | ||
|
|
16f046f38f | ||
|
|
7639be6851 | ||
|
|
a10c9109e2 | ||
|
|
6ac72e8be2 | ||
|
|
c594ff73ab | ||
|
|
b9e99832b9 | ||
|
|
440e3cd2c2 | ||
|
|
977dd47c69 | ||
|
|
2b029e56f9 | ||
|
|
b6f4d24fbc | ||
|
|
ab4f42608a | ||
|
|
23aabca217 | ||
|
|
d9d046c28a | ||
|
|
b6bb27c4c9 | ||
|
|
0271b3ab00 | ||
|
|
4cbb398d85 | ||
|
|
66a4366e8f | ||
|
|
b64fa0af89 | ||
|
|
8f300880c4 | ||
|
|
28344dfed4 | ||
|
|
8c69f7d56c | ||
|
|
aad7166704 | ||
|
|
6980d4fa2f | ||
|
|
094a36886f | ||
|
|
7e11efef59 | ||
|
|
8854de1ffd | ||
|
|
cefa06d985 | ||
|
|
ecb5183b9a | ||
|
|
2bb93e3567 | ||
|
|
2a17af9652 | ||
|
|
3831cdc1b1 | ||
|
|
e01993c3d5 | ||
|
|
7d70e67479 | ||
|
|
fc75bea445 | ||
|
|
59ef3c315b | ||
|
|
eef3fb99d0 | ||
|
|
12eb72ee3b | ||
|
|
a8377d1073 | ||
|
|
52e13fb007 | ||
|
|
9f0eaae229 | ||
|
|
cf181cdb52 | ||
|
|
6f9ef13d0a | ||
|
|
4c36d5209e | ||
|
|
e177680fa0 | ||
|
|
365f85179d | ||
|
|
8ffab431a2 | ||
|
|
ebed0191f9 | ||
|
|
a779b36e9c | ||
|
|
ea438b421d | ||
|
|
45f2a20349 | ||
|
|
853db300f5 | ||
|
|
f07fedb2fb | ||
|
|
1969871269 | ||
|
|
a8279122b9 | ||
|
|
9adc11d4ac | ||
|
|
b5a67a2f7b | ||
|
|
bd47f3f3e6 | ||
|
|
1d7983106c | ||
|
|
4cf38836ac | ||
|
|
ffb615a50b | ||
|
|
1931dfc1cb | ||
|
|
2be23abe85 | ||
|
|
9891a867ef | ||
|
|
e1f49c6068 | ||
|
|
046fd3cb83 | ||
|
|
913b4e5910 | ||
|
|
046f1e6b4c | ||
|
|
e5353a9158 | ||
|
|
f5da2a5913 | ||
|
|
e244da150b | ||
|
|
c4d6b277f2 | ||
|
|
e89b6b84d8 | ||
|
|
325515a685 | ||
|
|
c2d93dcf42 | ||
|
|
116ee14fe0 | ||
|
|
b869068751 | ||
|
|
4eeaae772f | ||
|
|
4dc76844a6 | ||
|
|
d85ccb3daf | ||
|
|
4a98ef84b9 | ||
|
|
b6a3c9c298 | ||
|
|
fbe10c86e8 | ||
|
|
c96f149679 | ||
|
|
f73006031e | ||
|
|
c3f90184c7 | ||
|
|
ec0de3b71e | ||
|
|
54086de158 | ||
|
|
6dca3c67c3 | ||
|
|
88023056fe | ||
|
|
37b3e44b9d | ||
|
|
cb7787af8a | ||
|
|
3ee0f6afc8 | ||
|
|
b3acdca9b6 | ||
|
|
7204ed57ed | ||
|
|
7db217e7ac | ||
|
|
553ffcb941 | ||
|
|
ba4d4372b1 | ||
|
|
4f2716218f | ||
|
|
08e0391d9c | ||
|
|
5ddf1463d3 | ||
|
|
5f4369eb2a | ||
|
|
20aae4e5ef | ||
|
|
d4280db033 | ||
|
|
d79f5a98f7 | ||
|
|
22198f07af | ||
|
|
03d9d8e7d3 | ||
|
|
229e3e4e5f | ||
|
|
6ea7b46ef6 | ||
|
|
98c57bb512 | ||
|
|
ae849a348c | ||
|
|
95b2e086cb | ||
|
|
eea30774b4 | ||
|
|
250212eee6 | ||
|
|
e6917603e2 | ||
|
|
320dacf891 | ||
|
|
11ca5f99a7 | ||
|
|
cd0afe151c | ||
|
|
f7b994b752 | ||
|
|
720ae18403 | ||
|
|
ae832446d9 | ||
|
|
ba29029581 | ||
|
|
a4d74c100d | ||
|
|
b8e749ddd6 | ||
|
|
6240f7092e | ||
|
|
b2a6a89a29 | ||
|
|
39d2072ae9 | ||
|
|
8bdaefd8bd | ||
|
|
74f34dc4d2 | ||
|
|
e91e5324d0 | ||
|
|
99f695ac99 | ||
|
|
51e4dabf70 | ||
|
|
ff7c814661 | ||
|
|
ac84a1ce15 | ||
|
|
2bf1e70e3d | ||
|
|
ec2d08375b | ||
|
|
217448b911 | ||
|
|
662840a9ac | ||
|
|
1e583b1eb8 | ||
|
|
682cb00f04 | ||
|
|
726a2bf3bd | ||
|
|
5658888f11 | ||
|
|
6a268a6cf1 | ||
|
|
b2ab8f4321 | ||
|
|
76a4804f2b | ||
|
|
71ee363046 | ||
|
|
28066b3a21 | ||
|
|
4b57c55182 | ||
|
|
073c0ba55e | ||
|
|
727504235c | ||
|
|
6c56ed7b02 | ||
|
|
306cd99e98 | ||
|
|
f2b952fe99 | ||
|
|
b17a009b65 | ||
|
|
426eb13fa9 | ||
|
|
4007b48cf0 | ||
|
|
e506807f54 | ||
|
|
5a70719762 | ||
|
|
7901b12541 | ||
|
|
3674b577f6 | ||
|
|
9f0741a613 | ||
|
|
31351ce146 | ||
|
|
1c89eae86b | ||
|
|
95441efe6e | ||
|
|
5465517c85 | ||
|
|
d38658b784 | ||
|
|
abae515395 | ||
|
|
55b9718bd8 | ||
|
|
97753ee3ad | ||
|
|
fa602e6cc9 | ||
|
|
2806bed309 | ||
|
|
896b2a0a33 | ||
|
|
e576607c2d | ||
|
|
4fe89e0acd | ||
|
|
ff5b6b7eb1 | ||
|
|
83731d04b3 | ||
|
|
5cf3b562c3 | ||
|
|
613a74fcc8 | ||
|
|
4dd2808856 | ||
|
|
f43a689b3c | ||
|
|
9f968375b4 | ||
|
|
b5fcf4ec1b | ||
|
|
386c7c6ff5 | ||
|
|
c29636c369 | ||
|
|
99b89011f1 | ||
|
|
786f7eec78 | ||
|
|
3d6178ed1d | ||
|
|
73761bd8ac | ||
|
|
1ad7757cbb | ||
|
|
720c926271 | ||
|
|
e89f4e531f | ||
|
|
7925e8942f | ||
|
|
3a1b40b685 | ||
|
|
5b6e5ab134 | ||
|
|
613fee3563 | ||
|
|
7b10fd2026 | ||
|
|
5581f5a0f6 | ||
|
|
9825580d79 | ||
|
|
0df81c8485 | ||
|
|
2aae36ea43 | ||
|
|
7691471070 | ||
|
|
d8d2fa887d | ||
|
|
226d54067e | ||
|
|
200f6c8c35 | ||
|
|
7f9a10b861 | ||
|
|
b347363768 | ||
|
|
70918fbf25 | ||
|
|
d089de0d16 | ||
|
|
b3dc50e620 | ||
|
|
64512cbab8 | ||
|
|
8e44c6bff9 | ||
|
|
2fcce54c83 | ||
|
|
1b769a0774 | ||
|
|
be8dfdcb5a | ||
|
|
19f89f76bb | ||
|
|
4376fb8517 | ||
|
|
8f51f6153a | ||
|
|
829706d3c5 | ||
|
|
da4bfbef46 | ||
|
|
eff84a76ac | ||
|
|
e8898f0188 | ||
|
|
5d11ed7162 | ||
|
|
22f8a46d2d | ||
|
|
cc2522771e | ||
|
|
9e01e2ee5c | ||
|
|
f000d11753 | ||
|
|
9930fca92b | ||
|
|
387560a718 | ||
|
|
0e5cbe52b0 | ||
|
|
3261f47b97 | ||
|
|
b275edd305 | ||
|
|
0011368622 | ||
|
|
0461a6d433 | ||
|
|
b1df214a43 | ||
|
|
9c353b0849 | ||
|
|
3be5cc1cb9 | ||
|
|
2956e5ab34 | ||
|
|
de9f4b6982 | ||
|
|
f911299295 | ||
|
|
67454487be | ||
|
|
c0d988e676 | ||
|
|
42a4637a99 | ||
|
|
5b97ffa980 | ||
|
|
93f29f536e | ||
|
|
5a1ac4ea59 | ||
|
|
3aa988ef77 | ||
|
|
75ca532114 | ||
|
|
898a78357e | ||
|
|
4f483c7390 | ||
|
|
bdb4b02a08 | ||
|
|
c914668db2 | ||
|
|
badbddadcd | ||
|
|
5448ecf1a2 | ||
|
|
9b1af2eb0c | ||
|
|
cf88da9ae0 | ||
|
|
f832db4bbd | ||
|
|
9f96ef9d98 |
267
README.hardware
267
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 NUC5 Series - ix-52xx Series SOC (Broadwell)
|
||||
+ Intel NUC6 Series - ix-62xx Series SOC (Skylake)
|
||||
+ Intel Shumway Xeon Server
|
||||
+ Intel Romley Server: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Canoe Pass CRB)
|
||||
+ Intel Romley Server: Ivy Bridge Xeon processor, C600 PCH (Patsburg), (Intel SDP S2R3)
|
||||
+ Intel Crystal Forest Server: Sandy Bridge Xeon processor, DH89xx PCH (Cave Creek), (Stargo CRB)
|
||||
+ Intel Chief River Mobile: Ivy Bridge Mobile processor, QM77 PCH (Panther Point-M), (Emerald Lake II CRB, Sabino Canyon CRB)
|
||||
+ Intel Huron River Mobile: Sandy Bridge processor, QM67 PCH (Cougar Point), (Emerald Lake CRB, EVOC EC7-1817LNAR board)
|
||||
+ Intel Calpella Platform: Core i7 processor, QM57 PCH (Ibex Peak-M), (Red Fort CRB, Emerson MATXM CORE-411-B)
|
||||
+ Intel Nehalem/Westmere-EP Server: Xeon 56xx/55xx processors, 5520 chipset, ICH10R IOH (82801), (Hanlan Creek CRB)
|
||||
+ Intel Nehalem Workstation: Xeon 56xx/55xx processors, System SC5650SCWS (Greencity CRB)
|
||||
+ Intel Picket Post Server: Xeon 56xx/55xx processors (Jasper Forest), 3420 chipset (Ibex Peak), (Osage CRB)
|
||||
+ Intel Storage Platform: Sandy Bridge Xeon processor, C600 PCH (Patsburg), (Oak Creek Canyon CRB)
|
||||
+ Intel Shark Bay Client Platform: Haswell processor, LynxPoint PCH, (Walnut Canyon CRB, Lava Canyon CRB, Basking Ridge CRB, Flathead Creek CRB)
|
||||
+ Intel Shark Bay Ultrabook Platform: Haswell ULT processor, Lynx Point-LP PCH, (WhiteTip Mountain 1 CRB)
|
||||
|
||||
Intel Atom platforms:
|
||||
+ MinnowBoard MAX - E3825 SOC (Bay Trail)
|
||||
+ MinnowBoard MAX - Turbot (ADI Engineering) - E3826 SOC (Bay Trail)
|
||||
- These boards can be either 32bot or 64bit modes depending on firmware
|
||||
- See minnowboard.org for details
|
||||
+ Intel Braswell SOC
|
||||
+ Intel embedded Menlow: Intel Atom Z510/530 CPU, System Controller Hub US15W (Portwell NANO-8044)
|
||||
+ Intel Luna Pier: Intel Atom N4xx/D5xx series CPU (aka: Pineview-D & -M), 82801HM I/O Hub (ICH8M), (Advantech AIMB-212, Moon Creek CRB)
|
||||
+ Intel Queens Bay platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Emerson NITX-315, Crown Bay CRB, Minnow Board)
|
||||
+ Intel Fish River Island platform: Intel Atom E6xx CPU (aka: Tunnel Creek), Topcliff EG20T I/O Hub (Kontron KM2M806)
|
||||
+ Intel Cedar Trail platform: Intel Atom N2000 & D2000 series CPU (aka: Cedarview), NM10 Express Chipset (Norco kit BIS-6630, Cedar Rock CRB)
|
||||
|
||||
and is likely to work on many unlisted Atom/Core/Xeon based devices. The MACHINE
|
||||
type supports ethernet, wifi, sound, and Intel/vesa graphics by default in
|
||||
@@ -127,14 +136,53 @@ USB Device:
|
||||
device, but the idea is to force BIOS to read the Cylinder/Head/Sector
|
||||
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:
|
||||
# dd if=core-image-minimal-genericx86-64.wic of=/dev/sdb
|
||||
a. Determine the geometry of your USB device using fdisk:
|
||||
|
||||
b) Use systemd-boot instead
|
||||
- Build an image with EFI_PROVIDER="systemd-boot" then use the above
|
||||
dd command to write the image to a USB stick.
|
||||
# fdisk /dev/sdb
|
||||
Command (m for help): p
|
||||
|
||||
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)
|
||||
@@ -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
|
||||
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)
|
||||
=====================================
|
||||
@@ -203,75 +293,6 @@ linux/arch/powerpc/boot/dts/mpc8315erdb.dts within the kernel source). If
|
||||
you have left them at the factory default then you shouldn't need to do
|
||||
anything here.
|
||||
|
||||
Note: To boot from USB disk you need u-boot that supports 'ext2load usb'
|
||||
command. You need to setup TFTP server, load u-boot from there and
|
||||
flash it to NOR flash.
|
||||
|
||||
Beware! Flashing bootloader is potentially dangerous operation that can
|
||||
brick your device if done incorrectly. Please, make sure you understand
|
||||
what below commands mean before executing them.
|
||||
|
||||
Load the new u-boot.bin from TFTP server to memory address 200000
|
||||
=> tftp 200000 u-boot.bin
|
||||
|
||||
Disable flash protection
|
||||
=> protect off all
|
||||
|
||||
Erase the old u-boot from fe000000 to fe06ffff in NOR flash.
|
||||
The size is 0x70000 (458752 bytes)
|
||||
=> erase fe000000 fe06ffff
|
||||
|
||||
Copy the new u-boot from address 200000 to fe000000
|
||||
the size is 0x70000. It has to be greater or equal to u-boot.bin size
|
||||
=> cp.b 200000 fe000000 70000
|
||||
|
||||
Enable flash protection again
|
||||
=> protect on all
|
||||
|
||||
Reset the board
|
||||
=> reset
|
||||
|
||||
--- Booting from USB disk ---
|
||||
|
||||
1. Flash partitioned image to the USB disk
|
||||
|
||||
# dd if=core-image-minimal-mpc8315e-rdb.wic of=/dev/sdb
|
||||
|
||||
2. Plug USB disk into the MPC8315 board
|
||||
|
||||
3. Connect the board's first serial port to your workstation and then start up
|
||||
your favourite serial terminal so that you will be able to interact with
|
||||
the serial console. If you don't have a favourite, picocom is suggested:
|
||||
|
||||
$ picocom /dev/ttyUSB0 -b 115200
|
||||
|
||||
4. Power up or reset the board and press a key on the terminal when prompted
|
||||
to get to the U-Boot command line
|
||||
|
||||
5. Optional. Load the u-boot.bin from the USB disk:
|
||||
|
||||
=> usb start
|
||||
=> ext2load usb 0:1 200000 u-boot.bin
|
||||
|
||||
and flash it to NOR flash as described above.
|
||||
|
||||
6. Set fdtaddr and loadaddr. This is not necessary if you set them before.
|
||||
|
||||
=> setenv fdtaddr a00000
|
||||
=> setenv loadaddr 1000000
|
||||
|
||||
7. Load the kernel and dtb from first partition of the USB disk:
|
||||
|
||||
=> usb start
|
||||
=> ext2load usb 0:1 $loadaddr uImage
|
||||
=> ext2load usb 0:1 $fdtaddr dtb
|
||||
|
||||
8. Set bootargs and boot up the device
|
||||
|
||||
=> setenv bootargs root=/dev/sdb2 rw rootwait console=ttyS0,115200
|
||||
=> bootm $loadaddr - $fdtaddr
|
||||
|
||||
|
||||
--- Booting from NFS root ---
|
||||
|
||||
Load the kernel and dtb (device tree blob), and boot the system as follows:
|
||||
@@ -370,25 +391,75 @@ Load the kernel, and boot the system as follows:
|
||||
=> 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)
|
||||
|
||||
--- Booting from USB disk ---
|
||||
--- Booting from USB root ---
|
||||
|
||||
To boot from the USB disk, you either need to remove it from the edgerouter
|
||||
box and populate it from another computer, or use a previously booted NFS
|
||||
image and populate from the edgerouter itself.
|
||||
|
||||
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:
|
||||
|
||||
1. Remove the USB disk from the edgerouter and insert it into a computer
|
||||
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 if=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
|
||||
-----------
|
||||
|
||||
@@ -5,15 +5,8 @@ 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
|
||||
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.
|
||||
|
||||
* 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
|
||||
|
||||
* simplediff is distributed under the zlib license.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- 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.main import bitbake_main, BitBakeConfigParameters, BBMainException
|
||||
|
||||
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.")
|
||||
|
||||
__version__ = "1.34.0"
|
||||
__version__ = "1.30.0"
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __version__ != bb.__version__:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
|
||||
# bitbake-diffsigs
|
||||
# BitBake task signature data comparison utility
|
||||
#
|
||||
# Copyright (C) 2012-2013, 2017 Intel Corporation
|
||||
# Copyright (C) 2012-2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
@@ -22,21 +22,36 @@ import os
|
||||
import sys
|
||||
import warnings
|
||||
import fnmatch
|
||||
import argparse
|
||||
import optparse
|
||||
import logging
|
||||
import pickle
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
|
||||
import bb.tinfoil
|
||||
import bb.siggen
|
||||
import bb.msg
|
||||
|
||||
logger = bb.msg.logger_create('bitbake-diffsigs')
|
||||
def logger_create(name, output=sys.stderr):
|
||||
logger = logging.getLogger(name)
|
||||
console = logging.StreamHandler(output)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
logger.setLevel(logging.INFO)
|
||||
return logger
|
||||
|
||||
def find_compare_task(bbhandler, pn, taskname, sig1=None, sig2=None, color=False):
|
||||
logger = logger_create('bitbake-diffsigs')
|
||||
|
||||
def find_compare_task(bbhandler, pn, taskname):
|
||||
""" Find the most recent signature files for the specified PN/task and compare them """
|
||||
|
||||
def get_hashval(siginfo):
|
||||
if siginfo.endswith('.siginfo'):
|
||||
return siginfo.rpartition(':')[2].partition('_')[0]
|
||||
else:
|
||||
return siginfo.rpartition('.')[2]
|
||||
|
||||
if not hasattr(bb.siggen, 'find_siginfo'):
|
||||
logger.error('Metadata does not support finding signature data files')
|
||||
sys.exit(1)
|
||||
@@ -44,119 +59,80 @@ def find_compare_task(bbhandler, pn, taskname, sig1=None, sig2=None, color=False
|
||||
if not taskname.startswith('do_'):
|
||||
taskname = 'do_%s' % taskname
|
||||
|
||||
if sig1 and sig2:
|
||||
sigfiles = bb.siggen.find_siginfo(pn, taskname, [sig1, sig2], bbhandler.config_data)
|
||||
if len(sigfiles) == 0:
|
||||
logger.error('No sigdata files found matching %s %s matching either %s or %s' % (pn, taskname, sig1, sig2))
|
||||
sys.exit(1)
|
||||
elif not sig1 in sigfiles:
|
||||
logger.error('No sigdata files found matching %s %s with signature %s' % (pn, taskname, sig1))
|
||||
sys.exit(1)
|
||||
elif not sig2 in sigfiles:
|
||||
logger.error('No sigdata files found matching %s %s with signature %s' % (pn, taskname, sig2))
|
||||
sys.exit(1)
|
||||
latestfiles = [sigfiles[sig1], sigfiles[sig2]]
|
||||
filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
|
||||
latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-3:]
|
||||
if not latestfiles:
|
||||
logger.error('No sigdata files found matching %s %s' % (pn, taskname))
|
||||
sys.exit(1)
|
||||
elif len(latestfiles) < 2:
|
||||
logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
|
||||
sys.exit(1)
|
||||
else:
|
||||
filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
|
||||
latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-3:]
|
||||
if not latestfiles:
|
||||
logger.error('No sigdata files found matching %s %s' % (pn, taskname))
|
||||
sys.exit(1)
|
||||
elif len(latestfiles) < 2:
|
||||
logger.error('Only one matching sigdata file found for the specified task (%s %s)' % (pn, taskname))
|
||||
sys.exit(1)
|
||||
# It's possible that latestfiles contain 3 elements and the first two have the same hash value.
|
||||
# In this case, we delete the second element.
|
||||
# The above case is actually the most common one. Because we may have sigdata file and siginfo
|
||||
# file having the same hash value. Comparing such two files makes no sense.
|
||||
if len(latestfiles) == 3:
|
||||
hash0 = get_hashval(latestfiles[0])
|
||||
hash1 = get_hashval(latestfiles[1])
|
||||
if hash0 == hash1:
|
||||
latestfiles.pop(1)
|
||||
|
||||
# Define recursion callback
|
||||
def recursecb(key, hash1, hash2):
|
||||
hashes = [hash1, hash2]
|
||||
hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
|
||||
# Define recursion callback
|
||||
def recursecb(key, hash1, hash2):
|
||||
hashes = [hash1, hash2]
|
||||
hashfiles = bb.siggen.find_siginfo(key, None, hashes, bbhandler.config_data)
|
||||
|
||||
recout = []
|
||||
if len(hashfiles) == 0:
|
||||
recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
|
||||
elif not hash1 in hashfiles:
|
||||
recout.append("Unable to find matching sigdata for %s with hash %s" % (key, hash1))
|
||||
elif not hash2 in hashfiles:
|
||||
recout.append("Unable to find matching sigdata for %s with hash %s" % (key, hash2))
|
||||
else:
|
||||
out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb, color=color)
|
||||
for change in out2:
|
||||
for line in change.splitlines():
|
||||
recout.append(' ' + line)
|
||||
recout = []
|
||||
if len(hashfiles) == 2:
|
||||
out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
|
||||
recout.extend(list(' ' + l for l in out2))
|
||||
else:
|
||||
recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
|
||||
|
||||
return recout
|
||||
return recout
|
||||
|
||||
# Recurse into signature comparison
|
||||
logger.debug("Signature file (previous): %s" % latestfiles[-2])
|
||||
logger.debug("Signature file (latest): %s" % latestfiles[-1])
|
||||
output = bb.siggen.compare_sigfiles(latestfiles[-2], latestfiles[-1], recursecb, color=color)
|
||||
if output:
|
||||
print('\n'.join(output))
|
||||
# Recurse into signature comparison
|
||||
output = bb.siggen.compare_sigfiles(latestfiles[0], latestfiles[1], recursecb)
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Compares siginfo/sigdata files written out by BitBake")
|
||||
parser = optparse.OptionParser(
|
||||
description = "Compares siginfo/sigdata files written out by BitBake",
|
||||
usage = """
|
||||
%prog -t recipename taskname
|
||||
%prog sigdatafile1 sigdatafile2
|
||||
%prog sigdatafile1""")
|
||||
|
||||
parser.add_argument('-d', '--debug',
|
||||
help='Enable debug output',
|
||||
action='store_true')
|
||||
parser.add_option("-t", "--task",
|
||||
help = "find the signature data files for last two runs of the specified task and compare them",
|
||||
action="store", dest="taskargs", nargs=2, metavar='recipename taskname')
|
||||
|
||||
parser.add_argument('--color',
|
||||
help='Colorize output (where %(metavar)s is %(choices)s)',
|
||||
choices=['auto', 'always', 'never'], default='auto', metavar='color')
|
||||
|
||||
parser.add_argument("-t", "--task",
|
||||
help="find the signature data files for last two runs of the specified task and compare them",
|
||||
action="store", dest="taskargs", nargs=2, metavar=('recipename', 'taskname'))
|
||||
|
||||
parser.add_argument("-s", "--signature",
|
||||
help="With -t/--task, specify the signatures to look for instead of taking the last two",
|
||||
action="store", dest="sigargs", nargs=2, metavar=('fromsig', 'tosig'))
|
||||
|
||||
parser.add_argument("sigdatafile1",
|
||||
help="First signature file to compare (or signature file to dump, if second not specified). Not used when using -t/--task.",
|
||||
action="store", nargs='?')
|
||||
|
||||
parser.add_argument("sigdatafile2",
|
||||
help="Second signature file to compare",
|
||||
action="store", nargs='?')
|
||||
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
color = (options.color == 'always' or (options.color == 'auto' and sys.stdout.isatty()))
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
if options.taskargs:
|
||||
with bb.tinfoil.Tinfoil() as tinfoil:
|
||||
tinfoil.prepare(config_only=True)
|
||||
if options.sigargs:
|
||||
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1], options.sigargs[0], options.sigargs[1], color=color)
|
||||
else:
|
||||
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1], color=color)
|
||||
tinfoil = bb.tinfoil.Tinfoil()
|
||||
tinfoil.prepare(config_only = True)
|
||||
find_compare_task(tinfoil, options.taskargs[0], options.taskargs[1])
|
||||
else:
|
||||
if options.sigargs:
|
||||
logger.error('-s/--signature can only be used together with -t/--task')
|
||||
sys.exit(1)
|
||||
try:
|
||||
if options.sigdatafile1 and options.sigdatafile2:
|
||||
output = bb.siggen.compare_sigfiles(options.sigdatafile1, options.sigdatafile2, color=color)
|
||||
elif options.sigdatafile1:
|
||||
output = bb.siggen.dump_sigfile(options.sigdatafile1)
|
||||
else:
|
||||
logger.error('Must specify signature file(s) or -t/--task')
|
||||
parser.print_help()
|
||||
if len(args) == 1:
|
||||
parser.print_help()
|
||||
else:
|
||||
import cPickle
|
||||
try:
|
||||
if len(args) == 2:
|
||||
output = bb.siggen.dump_sigfile(sys.argv[1])
|
||||
else:
|
||||
output = bb.siggen.compare_sigfiles(sys.argv[1], sys.argv[2])
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except cPickle.UnpicklingError, EOFError:
|
||||
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
|
||||
sys.exit(1)
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except (pickle.UnpicklingError, EOFError):
|
||||
logger.error('Invalid signature data - ensure you are specifying sigdata/siginfo files')
|
||||
sys.exit(1)
|
||||
|
||||
if output:
|
||||
print('\n'.join(output))
|
||||
if output:
|
||||
print '\n'.join(output)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
|
||||
# bitbake-dumpsig
|
||||
# BitBake task signature dump utility
|
||||
@@ -23,72 +23,43 @@ import sys
|
||||
import warnings
|
||||
import optparse
|
||||
import logging
|
||||
import pickle
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
|
||||
import bb.tinfoil
|
||||
import bb.siggen
|
||||
import bb.msg
|
||||
|
||||
logger = bb.msg.logger_create('bitbake-dumpsig')
|
||||
def logger_create(name, output=sys.stderr):
|
||||
logger = logging.getLogger(name)
|
||||
console = logging.StreamHandler(output)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
logger.setLevel(logging.INFO)
|
||||
return logger
|
||||
|
||||
def find_siginfo_task(bbhandler, pn, taskname):
|
||||
""" Find the most recent signature file for the specified PN/task """
|
||||
|
||||
if not hasattr(bb.siggen, 'find_siginfo'):
|
||||
logger.error('Metadata does not support finding signature data files')
|
||||
sys.exit(1)
|
||||
|
||||
if not taskname.startswith('do_'):
|
||||
taskname = 'do_%s' % taskname
|
||||
|
||||
filedates = bb.siggen.find_siginfo(pn, taskname, None, bbhandler.config_data)
|
||||
latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-1:]
|
||||
if not latestfiles:
|
||||
logger.error('No sigdata files found matching %s %s' % (pn, taskname))
|
||||
sys.exit(1)
|
||||
|
||||
return latestfiles[0]
|
||||
logger = logger_create('bitbake-dumpsig')
|
||||
|
||||
parser = optparse.OptionParser(
|
||||
description = "Dumps siginfo/sigdata files written out by BitBake",
|
||||
usage = """
|
||||
%prog -t recipename taskname
|
||||
%prog sigdatafile""")
|
||||
|
||||
parser.add_option("-D", "--debug",
|
||||
help = "enable debug",
|
||||
action = "store_true", dest="debug", default = False)
|
||||
|
||||
parser.add_option("-t", "--task",
|
||||
help = "find the signature data file for the specified task",
|
||||
action="store", dest="taskargs", nargs=2, metavar='recipename taskname')
|
||||
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
if options.debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
if options.taskargs:
|
||||
tinfoil = bb.tinfoil.Tinfoil()
|
||||
tinfoil.prepare(config_only = True)
|
||||
file = find_siginfo_task(tinfoil, options.taskargs[0], options.taskargs[1])
|
||||
logger.debug("Signature file: %s" % file)
|
||||
elif len(args) == 1:
|
||||
if len(args) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(0)
|
||||
else:
|
||||
file = args[1]
|
||||
import cPickle
|
||||
try:
|
||||
output = bb.siggen.dump_sigfile(args[1])
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except cPickle.UnpicklingError, EOFError:
|
||||
logger.error('Invalid signature data - ensure you are specifying a sigdata/siginfo file')
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
output = bb.siggen.dump_sigfile(file)
|
||||
except IOError as e:
|
||||
logger.error(str(e))
|
||||
sys.exit(1)
|
||||
except (pickle.UnpicklingError, EOFError):
|
||||
logger.error('Invalid signature data - ensure you are specifying a sigdata/siginfo file')
|
||||
sys.exit(1)
|
||||
|
||||
if output:
|
||||
print('\n'.join(output))
|
||||
if 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 sys,logging
|
||||
import optparse
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2012 Richard Purdie
|
||||
#
|
||||
@@ -25,48 +25,31 @@ try:
|
||||
except RuntimeError as exc:
|
||||
sys.exit(str(exc))
|
||||
|
||||
tests = ["bb.tests.codeparser",
|
||||
"bb.tests.cow",
|
||||
"bb.tests.data",
|
||||
"bb.tests.fetch",
|
||||
"bb.tests.parse",
|
||||
"bb.tests.utils"]
|
||||
def usage():
|
||||
print('usage: [BB_SKIP_NETTESTS=yes] %s [-v] [testname1 [testname2]...]' % os.path.basename(sys.argv[0]))
|
||||
|
||||
verbosity = 1
|
||||
|
||||
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:
|
||||
t = '.'.join(t.split('.')[:3])
|
||||
__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 sys
|
||||
@@ -10,14 +10,7 @@ import bb
|
||||
import select
|
||||
import errno
|
||||
import signal
|
||||
import pickle
|
||||
import traceback
|
||||
import queue
|
||||
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
|
||||
if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
|
||||
@@ -37,16 +30,19 @@ if sys.argv[1].startswith("decafbadbad"):
|
||||
# updates to log files for use with tail
|
||||
try:
|
||||
if sys.stdout.name == '<stdout>':
|
||||
import fcntl
|
||||
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)
|
||||
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||
except:
|
||||
pass
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||
|
||||
|
||||
worker_pipe = sys.stdout.fileno()
|
||||
bb.utils.nonblockingfd(worker_pipe)
|
||||
# Need to guard against multiprocessing being used in child processes
|
||||
@@ -66,54 +62,36 @@ if 0:
|
||||
consolelog.setFormatter(conlogformat)
|
||||
logger.addHandler(consolelog)
|
||||
|
||||
worker_queue = queue.Queue()
|
||||
worker_queue = ""
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = b"<event>" + pickle.dumps(event) + b"</event>"
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_fire_prepickled(data)
|
||||
|
||||
def worker_fire_prepickled(event):
|
||||
global worker_queue
|
||||
|
||||
worker_queue.put(event)
|
||||
worker_queue = worker_queue + event
|
||||
worker_flush()
|
||||
|
||||
#
|
||||
# We can end up with write contention with the cooker, it can be trying to send commands
|
||||
# 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():
|
||||
global worker_queue, worker_pipe
|
||||
|
||||
def worker_flush(worker_queue):
|
||||
worker_queue_int = b""
|
||||
global worker_pipe, worker_thread_exit
|
||||
if not worker_queue:
|
||||
return
|
||||
|
||||
while True:
|
||||
try:
|
||||
worker_queue_int = worker_queue_int + worker_queue.get(True, 1)
|
||||
except queue.Empty:
|
||||
pass
|
||||
while (worker_queue_int or not worker_queue.empty()):
|
||||
try:
|
||||
(_, ready, _) = select.select([], [worker_pipe], [], 1)
|
||||
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()
|
||||
try:
|
||||
written = os.write(worker_pipe, worker_queue)
|
||||
worker_queue = worker_queue[written:]
|
||||
except (IOError, OSError) as e:
|
||||
if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
|
||||
raise
|
||||
|
||||
def worker_child_fire(event, d):
|
||||
global worker_pipe
|
||||
global worker_pipe_lock
|
||||
|
||||
data = b"<event>" + pickle.dumps(event) + b"</event>"
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
try:
|
||||
worker_pipe_lock.acquire()
|
||||
worker_pipe.write(data)
|
||||
@@ -136,7 +114,7 @@ def sigterm_handler(signum, frame):
|
||||
os.killpg(0, signal.SIGTERM)
|
||||
sys.exit()
|
||||
|
||||
def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, appends, taskdepdata, extraconfigdata, quieterrors=False, dry_run_exec=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
|
||||
# a fork() or exec*() activates PSEUDO...
|
||||
|
||||
@@ -152,10 +130,8 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
||||
except TypeError:
|
||||
umask = taskdep['umask'][taskname]
|
||||
|
||||
dry_run = cfg.dry_run or dry_run_exec
|
||||
|
||||
# We can't use the fakeroot environment in a dry run as it possibly hasn't been built
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not dry_run:
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not cfg.dry_run:
|
||||
envvars = (workerdata["fakerootenv"][fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
@@ -183,8 +159,7 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||
pid = os.fork()
|
||||
except OSError as e:
|
||||
logger.critical("fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
|
||||
if pid == 0:
|
||||
def child():
|
||||
@@ -216,58 +191,39 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
||||
if 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:
|
||||
bb_cache = bb.cache.NoCache(databuilder)
|
||||
(realfn, virtual, mc) = bb.cache.virtualfn2realfn(fn)
|
||||
the_data = databuilder.mcdata[mc]
|
||||
the_data.setVar("BB_WORKERCONTEXT", "1")
|
||||
the_data.setVar("BB_TASKDEPDATA", taskdepdata)
|
||||
if cfg.limited_deps:
|
||||
the_data.setVar("BB_LIMITEDDEPS", "1")
|
||||
the_data.setVar("BUILDNAME", workerdata["buildname"])
|
||||
the_data.setVar("DATE", workerdata["date"])
|
||||
the_data.setVar("TIME", workerdata["time"])
|
||||
for varname, value in extraconfigdata.items():
|
||||
the_data.setVar(varname, value)
|
||||
|
||||
bb.parse.siggen.set_taskdata(workerdata["sigdata"])
|
||||
ret = 0
|
||||
|
||||
the_data = bb_cache.loadDataFull(fn, appends)
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, appends, data)
|
||||
the_data.setVar('BB_TASKHASH', workerdata["runq_hash"][task])
|
||||
|
||||
bb.utils.set_process_name("%s:%s" % (the_data.getVar("PN"), taskname.replace("do_", "")))
|
||||
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()
|
||||
# successfully. We also need to unset anything from the environment which shouldn't be there
|
||||
exports = bb.data.exported_vars(the_data)
|
||||
|
||||
bb.utils.empty_environment()
|
||||
for e, v in exports:
|
||||
os.environ[e] = v
|
||||
|
||||
for e in fakeenv:
|
||||
os.environ[e] = fakeenv[e]
|
||||
the_data.setVar(e, fakeenv[e])
|
||||
the_data.setVarFlag(e, 'export', "1")
|
||||
|
||||
task_exports = the_data.getVarFlag(taskname, 'exports')
|
||||
if task_exports:
|
||||
for e in task_exports.split():
|
||||
the_data.setVarFlag(e, 'export', '1')
|
||||
v = the_data.getVar(e)
|
||||
if v is not None:
|
||||
os.environ[e] = v
|
||||
|
||||
if quieterrors:
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
if not quieterrors:
|
||||
logger.critical(traceback.format_exc())
|
||||
logger.critical(str(exc))
|
||||
os._exit(1)
|
||||
try:
|
||||
if dry_run:
|
||||
if cfg.dry_run:
|
||||
return 0
|
||||
return bb.build.exec_task(fn, taskname, the_data, cfg.profile)
|
||||
except:
|
||||
@@ -284,7 +240,7 @@ def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, append
|
||||
bb.utils.process_profilelog(profname)
|
||||
os._exit(ret)
|
||||
else:
|
||||
for key, value in iter(envbackup.items()):
|
||||
for key, value in envbackup.iteritems():
|
||||
if value is None:
|
||||
del os.environ[key]
|
||||
else:
|
||||
@@ -301,22 +257,22 @@ class runQueueWorkerPipe():
|
||||
if pipeout:
|
||||
pipeout.close()
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = b""
|
||||
self.queue = ""
|
||||
|
||||
def read(self):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + (self.input.read(102400) or b"")
|
||||
self.queue = self.queue + self.input.read(102400)
|
||||
except (OSError, IOError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
|
||||
end = len(self.queue)
|
||||
index = self.queue.find(b"</event>")
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1:
|
||||
worker_fire_prepickled(self.queue[:index+8])
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find(b"</event>")
|
||||
index = self.queue.find("</event>")
|
||||
return (end > start)
|
||||
|
||||
def close(self):
|
||||
@@ -332,11 +288,10 @@ class BitbakeWorker(object):
|
||||
def __init__(self, din):
|
||||
self.input = din
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = b""
|
||||
self.queue = ""
|
||||
self.cookercfg = None
|
||||
self.databuilder = None
|
||||
self.data = None
|
||||
self.extraconfigdata = None
|
||||
self.build_pids = {}
|
||||
self.build_pipes = {}
|
||||
|
||||
@@ -370,29 +325,27 @@ class BitbakeWorker(object):
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
if len(self.queue):
|
||||
self.handle_item(b"cookerconfig", self.handle_cookercfg)
|
||||
self.handle_item(b"extraconfigdata", self.handle_extraconfigdata)
|
||||
self.handle_item(b"workerdata", self.handle_workerdata)
|
||||
self.handle_item(b"runtask", self.handle_runtask)
|
||||
self.handle_item(b"finishnow", self.handle_finishnow)
|
||||
self.handle_item(b"ping", self.handle_ping)
|
||||
self.handle_item(b"quit", self.handle_quit)
|
||||
self.handle_item("cookerconfig", self.handle_cookercfg)
|
||||
self.handle_item("workerdata", self.handle_workerdata)
|
||||
self.handle_item("runtask", self.handle_runtask)
|
||||
self.handle_item("finishnow", self.handle_finishnow)
|
||||
self.handle_item("ping", self.handle_ping)
|
||||
self.handle_item("quit", self.handle_quit)
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
if self.build_pipes[pipe].input in ready:
|
||||
self.build_pipes[pipe].read()
|
||||
self.build_pipes[pipe].read()
|
||||
if len(self.build_pids):
|
||||
while self.process_waitpid():
|
||||
continue
|
||||
self.process_waitpid()
|
||||
worker_flush()
|
||||
|
||||
|
||||
def handle_item(self, item, func):
|
||||
if self.queue.startswith(b"<" + item + b">"):
|
||||
index = self.queue.find(b"</" + item + b">")
|
||||
if self.queue.startswith("<" + item + ">"):
|
||||
index = self.queue.find("</" + item + ">")
|
||||
while index != -1:
|
||||
func(self.queue[(len(item) + 2):index])
|
||||
self.queue = self.queue[(index + len(item) + 3):]
|
||||
index = self.queue.find(b"</" + item + b">")
|
||||
index = self.queue.find("</" + item + ">")
|
||||
|
||||
def handle_cookercfg(self, data):
|
||||
self.cookercfg = pickle.loads(data)
|
||||
@@ -400,22 +353,18 @@ class BitbakeWorker(object):
|
||||
self.databuilder.parseBaseConfiguration()
|
||||
self.data = self.databuilder.data
|
||||
|
||||
def handle_extraconfigdata(self, data):
|
||||
self.extraconfigdata = pickle.loads(data)
|
||||
|
||||
def handle_workerdata(self, data):
|
||||
self.workerdata = pickle.loads(data)
|
||||
bb.msg.loggerDefaultDebugLevel = self.workerdata["logdefaultdebug"]
|
||||
bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
|
||||
bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
|
||||
bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
|
||||
for mc in self.databuilder.mcdata:
|
||||
self.databuilder.mcdata[mc].setVar("PRSERV_HOST", self.workerdata["prhost"])
|
||||
self.data.setVar("PRSERV_HOST", self.workerdata["prhost"])
|
||||
|
||||
def handle_ping(self, _):
|
||||
workerlog_write("Handling ping\n")
|
||||
|
||||
logger.warning("Pong from bitbake-worker!")
|
||||
logger.warn("Pong from bitbake-worker!")
|
||||
|
||||
def handle_quit(self, data):
|
||||
workerlog_write("Handling quit\n")
|
||||
@@ -425,10 +374,10 @@ class BitbakeWorker(object):
|
||||
sys.exit(0)
|
||||
|
||||
def handle_runtask(self, data):
|
||||
fn, task, taskname, quieterrors, appends, taskdepdata, dry_run_exec = pickle.loads(data)
|
||||
fn, task, taskname, quieterrors, appends, taskdepdata = pickle.loads(data)
|
||||
workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
|
||||
|
||||
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.databuilder, self.workerdata, fn, task, taskname, appends, taskdepdata, self.extraconfigdata, quieterrors, dry_run_exec)
|
||||
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_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
|
||||
@@ -441,9 +390,9 @@ class BitbakeWorker(object):
|
||||
try:
|
||||
pid, status = os.waitpid(-1, os.WNOHANG)
|
||||
if pid == 0 or os.WIFSTOPPED(status):
|
||||
return False
|
||||
return None
|
||||
except OSError:
|
||||
return False
|
||||
return None
|
||||
|
||||
workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
|
||||
|
||||
@@ -460,14 +409,12 @@ class BitbakeWorker(object):
|
||||
self.build_pipes[pid].close()
|
||||
del self.build_pipes[pid]
|
||||
|
||||
worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
|
||||
|
||||
return True
|
||||
worker_fire_prepickled("<exitcode>" + pickle.dumps((task, status)) + "</exitcode>")
|
||||
|
||||
def handle_finishnow(self, _):
|
||||
if self.build_pids:
|
||||
logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
|
||||
for k, v in iter(self.build_pids.items()):
|
||||
for k, v in self.build_pids.iteritems():
|
||||
try:
|
||||
os.kill(-k, signal.SIGTERM)
|
||||
os.waitpid(-1, 0)
|
||||
@@ -477,7 +424,7 @@ class BitbakeWorker(object):
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
try:
|
||||
worker = BitbakeWorker(os.fdopen(sys.stdin.fileno(), 'rb'))
|
||||
worker = BitbakeWorker(sys.stdin)
|
||||
if not profiling:
|
||||
worker.serve()
|
||||
else:
|
||||
@@ -493,10 +440,8 @@ except BaseException as e:
|
||||
import traceback
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
sys.stderr.write(str(e))
|
||||
|
||||
worker_thread_exit = True
|
||||
worker_thread.join()
|
||||
|
||||
while len(worker_queue):
|
||||
worker_flush()
|
||||
workerlog_write("exitting")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
|
||||
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()
|
||||
@@ -17,35 +17,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
HELP="
|
||||
Usage: source toaster start|stop [webport=<address:port>] [noweb]
|
||||
Optional arguments:
|
||||
[noweb] Setup the environment for building with toaster but don't start the development server
|
||||
[webport] Set the development server (default: localhost:8000)
|
||||
"
|
||||
# Usage: source toaster [start|stop]
|
||||
# [webport=<port>] [noui] [noweb]
|
||||
|
||||
databaseCheck()
|
||||
{
|
||||
retval=0
|
||||
# you can always add a superuser later via
|
||||
# ../bitbake/lib/toaster/manage.py createsuperuser --username=<ME>
|
||||
$MANAGE migrate --noinput || retval=1
|
||||
|
||||
if [ $retval -eq 1 ]; then
|
||||
echo "Failed migrations, aborting system start" 1>&2
|
||||
return $retval
|
||||
fi
|
||||
# Make sure that checksettings can pick up any value for TEMPLATECONF
|
||||
export TEMPLATECONF
|
||||
$MANAGE checksettings --traceback || retval=1
|
||||
|
||||
if [ $retval -eq 1 ]; then
|
||||
printf "\nError while checking settings; aborting\n"
|
||||
return $retval
|
||||
fi
|
||||
|
||||
return $retval
|
||||
}
|
||||
# Helper function to kill a background toaster development server
|
||||
|
||||
webserverKillAll()
|
||||
{
|
||||
@@ -56,6 +31,9 @@ webserverKillAll()
|
||||
while kill -0 $pid 2>/dev/null; do
|
||||
kill -SIGTERM -$pid 2>/dev/null
|
||||
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
|
||||
rm ${pidfile}
|
||||
fi
|
||||
@@ -71,13 +49,25 @@ webserverStartAll()
|
||||
fi
|
||||
|
||||
retval=0
|
||||
# you can always add a superuser later via
|
||||
# ../bitbake/lib/toaster/manage.py createsuperuser --username=<ME>
|
||||
$MANAGE migrate --noinput || retval=1
|
||||
|
||||
# check the database
|
||||
databaseCheck || return 1
|
||||
if [ $retval -eq 1 ]; then
|
||||
echo "Failed migrations, aborting system start" 1>&2
|
||||
return $retval
|
||||
fi
|
||||
|
||||
$MANAGE checksettings --traceback || retval=1
|
||||
|
||||
if [ $retval -eq 1 ]; then
|
||||
printf "\nError while checking settings; aborting\n"
|
||||
return $retval
|
||||
fi
|
||||
|
||||
echo "Starting webserver..."
|
||||
|
||||
$MANAGE runserver "$ADDR_PORT" \
|
||||
$MANAGE runserver "0.0.0.0:$WEB_PORT" \
|
||||
</dev/null >>${BUILDDIR}/toaster_web.log 2>&1 \
|
||||
& echo $! >${BUILDDIR}/.toastermain.pid
|
||||
|
||||
@@ -87,8 +77,7 @@ webserverStartAll()
|
||||
retval=1
|
||||
rm "${BUILDDIR}/.toastermain.pid"
|
||||
else
|
||||
echo "Toaster development webserver started at http://$ADDR_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"
|
||||
echo "Webserver address: http://0.0.0.0:$WEB_PORT/"
|
||||
fi
|
||||
|
||||
return $retval
|
||||
@@ -102,8 +91,14 @@ stop_system()
|
||||
# prevent reentry
|
||||
if [ $INSTOPSYSTEM -eq 1 ]; then return; fi
|
||||
INSTOPSYSTEM=1
|
||||
if [ -f ${BUILDDIR}/.toasterui.pid ]; then
|
||||
kill `cat ${BUILDDIR}/.toasterui.pid` 2>/dev/null
|
||||
rm ${BUILDDIR}/.toasterui.pid
|
||||
fi
|
||||
webserverKillAll
|
||||
# unset exported variables
|
||||
unset DATABASE_URL
|
||||
unset TOASTER_CONF
|
||||
unset TOASTER_DIR
|
||||
unset BITBAKE_UI
|
||||
unset BBBASEDIR
|
||||
@@ -114,14 +109,14 @@ stop_system()
|
||||
|
||||
verify_prereq() {
|
||||
# Verify Django version
|
||||
reqfile=$(python3 -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))")
|
||||
reqfile=$(python -c "import os; print os.path.realpath('$BBBASEDIR/toaster-requirements.txt')")
|
||||
exp='s/Django\([><=]\+\)\([^,]\+\),\([><=]\+\)\(.\+\)/'
|
||||
exp=$exp'import sys,django;version=django.get_version().split(".");'
|
||||
exp=$exp'sys.exit(not (version \1 "\2".split(".") and version \3 "\4".split(".")))/p'
|
||||
if ! sed -n "$exp" $reqfile | python3 - ; then
|
||||
if ! sed -n "$exp" $reqfile | python - ; then
|
||||
req=`grep ^Django $reqfile`
|
||||
echo "This program needs $req"
|
||||
echo "Please install with pip3 install -r $reqfile"
|
||||
echo "Please install with pip install -r $reqfile"
|
||||
return 2
|
||||
fi
|
||||
|
||||
@@ -138,8 +133,8 @@ else
|
||||
fi
|
||||
|
||||
export BBBASEDIR=`dirname $TOASTER`/..
|
||||
MANAGE="python3 $BBBASEDIR/lib/toaster/manage.py"
|
||||
OE_ROOT=`dirname $TOASTER`/../..
|
||||
MANAGE=$BBBASEDIR/lib/toaster/manage.py
|
||||
OEROOT=`dirname $TOASTER`/../..
|
||||
|
||||
# this is the configuraton file we are using for toaster
|
||||
# we are using the same logic that oe-setup-builddir uses
|
||||
@@ -149,22 +144,41 @@ OE_ROOT=`dirname $TOASTER`/../..
|
||||
# in the local layers that currently make using an arbitrary
|
||||
# toasterconf.json difficult.
|
||||
|
||||
. $OE_ROOT/.templateconf
|
||||
. $OEROOT/.templateconf
|
||||
if [ -n "$TEMPLATECONF" ]; then
|
||||
if [ ! -d "$TEMPLATECONF" ]; then
|
||||
# Allow TEMPLATECONF=meta-xyz/conf as a shortcut
|
||||
if [ -d "$OE_ROOT/$TEMPLATECONF" ]; then
|
||||
TEMPLATECONF="$OE_ROOT/$TEMPLATECONF"
|
||||
if [ -d "$OEROOT/$TEMPLATECONF" ]; then
|
||||
TEMPLATECONF="$OEROOT/$TEMPLATECONF"
|
||||
fi
|
||||
if [ ! -d "$TEMPLATECONF" ]; then
|
||||
echo >&2 "Error: '$TEMPLATECONF' must be a directory containing toasterconf.json"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
unset OE_ROOT
|
||||
if [ "$TOASTER_CONF" = "" ]; then
|
||||
TOASTER_CONF="$TEMPLATECONF/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 file or fix .templateconf"
|
||||
return 1
|
||||
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=`pwd`
|
||||
|
||||
WEBSERVER=1
|
||||
ADDR_PORT="localhost:8000"
|
||||
WEB_PORT="8000"
|
||||
unset CMD
|
||||
for param in $*; do
|
||||
case $param in
|
||||
@@ -178,24 +192,7 @@ for param in $*; do
|
||||
CMD=$param
|
||||
;;
|
||||
webport=*)
|
||||
ADDR_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
|
||||
;;
|
||||
|
||||
WEB_PORT="${param#*=}"
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -217,8 +214,10 @@ fi
|
||||
# 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`
|
||||
export BB_ENV_EXTRAWHITE="$BB_ENV_EXTRAWHITE TOASTER_DIR"
|
||||
|
||||
# Determine the action. If specified by arguments, fine, if not, toggle it
|
||||
if [ "$CMD" = "start" ] ; then
|
||||
@@ -227,9 +226,11 @@ if [ "$CMD" = "start" ] ; then
|
||||
return 1
|
||||
fi
|
||||
elif [ "$CMD" = "" ]; then
|
||||
echo "No command specified"
|
||||
echo "$HELP"
|
||||
return 1
|
||||
if [ -z "$BBSERVER" ]; then
|
||||
CMD="start"
|
||||
else
|
||||
CMD="stop"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "The system will $CMD."
|
||||
@@ -240,9 +241,15 @@ case $CMD in
|
||||
start )
|
||||
# check if addr:port is not in use
|
||||
if [ "$CMD" == 'start' ]; then
|
||||
if [ $WEBSERVER -gt 0 ]; then
|
||||
$MANAGE checksocket "$ADDR_PORT" || return 1
|
||||
fi
|
||||
$MANAGE checksocket "0.0.0.0:$WEB_PORT" || return 1
|
||||
fi
|
||||
|
||||
# kill Toaster web server if it's alive
|
||||
if [ -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." 1>&2
|
||||
echo " Something fishy is going on." 1>&2
|
||||
echo "Cleaning up the web server to start from a clean slate."
|
||||
webserverKillAll
|
||||
fi
|
||||
|
||||
# Create configuration file
|
||||
@@ -250,25 +257,13 @@ case $CMD in
|
||||
line='INHERIT+="toaster buildhistory"'
|
||||
grep -q "$line" $conf || echo $line >> $conf
|
||||
|
||||
if [ $WEBSERVER -eq 0 ] ; then
|
||||
# Do not update the database for "noweb" unless
|
||||
# it does not yet exist
|
||||
if [ ! -f "$TOASTER_DIR/toaster.sqlite" ] ; then
|
||||
if ! databaseCheck; then
|
||||
echo "Failed ${CMD}."
|
||||
return 4
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ $WEBSERVER -gt 0 ] && ! webserverStartAll; then
|
||||
echo "Failed ${CMD}."
|
||||
return 4
|
||||
fi
|
||||
export BITBAKE_UI='toasterui'
|
||||
$MANAGE runbuilds \
|
||||
</dev/null >>${BUILDDIR}/toaster_runbuilds.log 2>&1 \
|
||||
& echo $! >${BUILDDIR}/.runbuilds.pid
|
||||
|
||||
export DATABASE_URL=`$MANAGE get-dburl`
|
||||
$MANAGE runbuilds & echo $! >${BUILDDIR}/.runbuilds.pid
|
||||
# set fail safe stop system on terminal exit
|
||||
trap stop_system SIGHUP
|
||||
echo "Successful ${CMD}."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- 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.,
|
||||
# 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 sys
|
||||
import json
|
||||
import pickle
|
||||
import codecs
|
||||
|
||||
from collections import namedtuple
|
||||
import sys, logging
|
||||
|
||||
# mangle syspath to allow easy import of modules
|
||||
from os.path import join, dirname, abspath
|
||||
sys.path.insert(0, join(dirname(dirname(abspath(__file__))), 'lib'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'lib'))
|
||||
|
||||
|
||||
import bb.cooker
|
||||
from bb.ui import toasterui
|
||||
import sys
|
||||
import logging
|
||||
|
||||
class EventPlayer:
|
||||
"""Emulate a connection to a bitbake server."""
|
||||
import json, pickle
|
||||
|
||||
def __init__(self, eventfile, variables):
|
||||
self.eventfile = eventfile
|
||||
self.variables = variables
|
||||
self.eventmask = []
|
||||
|
||||
def waitEvent(self, _timeout):
|
||||
"""Read event from the file."""
|
||||
line = self.eventfile.readline().strip()
|
||||
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
|
||||
class FileReadEventsServerConnection():
|
||||
""" Emulates a connection to a bitbake server that feeds
|
||||
events coming actually read from a saved log file.
|
||||
"""
|
||||
|
||||
def runCommand(self, command_line):
|
||||
"""Emulate running a command on the server."""
|
||||
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):
|
||||
class MockConnection():
|
||||
""" fill-in for the proxy to the server. we just return generic data
|
||||
"""
|
||||
This method is called by toasterui.
|
||||
The return value is passed to self.runCommand but not used there.
|
||||
"""
|
||||
pass
|
||||
def __init__(self, sc):
|
||||
self._sc = sc
|
||||
|
||||
def main(argv):
|
||||
with open(argv[-1]) as eventfile:
|
||||
# load variables from the first line
|
||||
variables = json.loads(eventfile.readline().strip())['allvariables']
|
||||
def runCommand(self, commandArray):
|
||||
""" emulates running a command on the server; only read-only commands are accepted """
|
||||
command_name = commandArray[0]
|
||||
|
||||
params = namedtuple('ConfigParams', ['observe_only'])(True)
|
||||
player = EventPlayer(eventfile, variables)
|
||||
if command_name == "getVariable":
|
||||
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
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: %s <event file>" % os.path.basename(sys.argv[0]))
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s event.log " % sys.argv[0])
|
||||
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
|
||||
# -*- 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'))
|
||||
from bb.cache import CoreRecipeInfo
|
||||
|
||||
import pickle as pickle
|
||||
import cPickle as pickle
|
||||
|
||||
def main(argv=None):
|
||||
"""
|
||||
Get the mapping for the target recipe.
|
||||
"""
|
||||
if len(argv) != 1:
|
||||
print("Error, need one argument!", file=sys.stderr)
|
||||
print >>sys.stderr, "Error, need one argument!"
|
||||
return 2
|
||||
|
||||
cachefile = argv[0]
|
||||
@@ -56,7 +56,7 @@ def main(argv=None):
|
||||
continue
|
||||
|
||||
# 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
|
||||
else:
|
||||
pv = "1.0"
|
||||
|
||||
@@ -596,7 +596,7 @@
|
||||
"<link linkend='checksums'>Checksums (Signatures)</link>"
|
||||
section for information).
|
||||
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.
|
||||
</para>
|
||||
|
||||
@@ -653,8 +653,7 @@
|
||||
</itemizedlist>
|
||||
It is possible to have functions run before and after a task's main
|
||||
function.
|
||||
This is done using the <filename>[prefuncs]</filename>
|
||||
and <filename>[postfuncs]</filename> flags of the task
|
||||
This is done using the "prefuncs" and "postfuncs" flags of the task
|
||||
that lists the functions to run.
|
||||
</para>
|
||||
</section>
|
||||
@@ -828,7 +827,7 @@
|
||||
itself.
|
||||
The simplest parameter to pass is "none", which causes a
|
||||
set of signature information to be written out into
|
||||
<filename>STAMPS_DIR</filename>
|
||||
<filename>STAMP_DIR</filename>
|
||||
corresponding to the targets specified.
|
||||
The other currently available parameter is "printdiff",
|
||||
which causes BitBake to try to establish the closest
|
||||
@@ -916,7 +915,7 @@
|
||||
<para>
|
||||
Finally, after all the setscene tasks have executed, BitBake calls the
|
||||
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".
|
||||
The metadata can then ensure that this list is correct and can
|
||||
inform BitBake that it wants specific tasks to be run regardless
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
The code to execute the first part of this process, a fetch,
|
||||
looks something like the following:
|
||||
<literallayout class='monospaced'>
|
||||
src_uri = (d.getVar('SRC_URI') or "").split()
|
||||
src_uri = (d.getVar('SRC_URI', True) or "").split()
|
||||
fetcher = bb.fetch2.Fetch(src_uri, d)
|
||||
fetcher.download()
|
||||
</literallayout>
|
||||
@@ -52,7 +52,7 @@
|
||||
<para>
|
||||
The instantiation of the fetch class is usually followed by:
|
||||
<literallayout class='monospaced'>
|
||||
rootdir = l.getVar('WORKDIR')
|
||||
rootdir = l.getVar('WORKDIR', True)
|
||||
fetcher.unpack(rootdir)
|
||||
</literallayout>
|
||||
This code unpacks the downloaded files to the
|
||||
@@ -268,6 +268,15 @@
|
||||
<link linkend='var-FILESPATH'><filename>FILESPATH</filename></link>
|
||||
variable is used in the same way
|
||||
<filename>PATH</filename> is used to find executables.
|
||||
Failing that,
|
||||
<link linkend='var-FILESDIR'><filename>FILESDIR</filename></link>
|
||||
is used to find the appropriate relative file.
|
||||
<note>
|
||||
<filename>FILESDIR</filename> is deprecated and can
|
||||
be replaced with <filename>FILESPATH</filename>.
|
||||
Because <filename>FILESDIR</filename> is likely to be
|
||||
removed, you should not use this variable in any new code.
|
||||
</note>
|
||||
If the file cannot be found, it is assumed that it is available in
|
||||
<link linkend='var-DL_DIR'><filename>DL_DIR</filename></link>
|
||||
by the time the <filename>download()</filename> method is called.
|
||||
@@ -376,8 +385,7 @@
|
||||
The supported parameters are as follows:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>"method":</emphasis>
|
||||
The protocol over which to communicate with the CVS
|
||||
server.
|
||||
The protocol over which to communicate with the CVS server.
|
||||
By default, this protocol is "pserver".
|
||||
If "method" is set to "ext", BitBake examines the
|
||||
"rsh" parameter and sets <filename>CVS_RSH</filename>.
|
||||
@@ -461,29 +469,25 @@
|
||||
You can think of this parameter as the top-level
|
||||
directory of the repository data you want.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"path_spec":</emphasis>
|
||||
A specific directory in which to checkout the
|
||||
specified svn module.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"protocol":</emphasis>
|
||||
The protocol to use, which defaults to "svn".
|
||||
If "protocol" is set to "svn+ssh", the "ssh"
|
||||
parameter is also used.
|
||||
Other options are "svn+ssh" and "rsh".
|
||||
For "rsh", the "rsh" parameter is also used.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"rev":</emphasis>
|
||||
The revision of the source code to checkout.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"date":</emphasis>
|
||||
The date of the source code to checkout.
|
||||
Specific revisions are generally much safer to checkout
|
||||
rather than by date as they do not involve timezones
|
||||
(e.g. they are much more deterministic).
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"scmdata":</emphasis>
|
||||
Causes the “.svn” directories to be available during
|
||||
compile-time when set to "keep".
|
||||
By default, these directories are removed.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"ssh":</emphasis>
|
||||
An optional parameter used when "protocol" is set
|
||||
to "svn+ssh".
|
||||
You can use this parameter to specify the ssh
|
||||
program used by svn.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"transportuser":</emphasis>
|
||||
When required, sets the username for the transport.
|
||||
By default, this parameter is empty.
|
||||
@@ -492,11 +496,10 @@
|
||||
command.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
Following are three examples using svn:
|
||||
Following are two examples using svn:
|
||||
<literallayout class='monospaced'>
|
||||
SRC_URI = "svn://myrepos/proj1;module=vip;protocol=http;rev=667"
|
||||
SRC_URI = "svn://myrepos/proj1;module=opie;protocol=svn+ssh"
|
||||
SRC_URI = "svn://myrepos/proj1;module=trunk;protocol=http;path_spec=${MY_DIR}/proj1"
|
||||
SRC_URI = "svn://svn.oe.handhelds.org/svn;module=vip;proto=http;rev=667"
|
||||
SRC_URI = "svn://svn.oe.handhelds.org/svn/;module=opie;proto=svn+ssh;date=20060126"
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
@@ -588,14 +591,6 @@
|
||||
The name of the path in which to place the checkout.
|
||||
By default, the path is <filename>git/</filename>.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"usehead":</emphasis>
|
||||
Enables local <filename>git://</filename> URLs to use the
|
||||
current branch HEAD as the revision for use with
|
||||
<filename>AUTOREV</filename>.
|
||||
The "usehead" parameter implies no branch and only works
|
||||
when the transfer protocol is
|
||||
<filename>file://</filename>.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
Here are some example URLs:
|
||||
<literallayout class='monospaced'>
|
||||
@@ -675,8 +670,8 @@
|
||||
The <filename>module</filename> and <filename>vob</filename>
|
||||
options are combined to create the <filename>load</filename> rule in
|
||||
the view config spec.
|
||||
As an example, consider the <filename>vob</filename> and
|
||||
<filename>module</filename> values from the
|
||||
As an example, consider the <filename>vob</filename> and
|
||||
<filename>module</filename> values from the
|
||||
<filename>SRC_URI</filename> statement at the start of this section.
|
||||
Combining those values results in the following:
|
||||
<literallayout class='monospaced'>
|
||||
@@ -721,68 +716,6 @@
|
||||
</para>
|
||||
</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'>
|
||||
<title>Other Fetchers</title>
|
||||
|
||||
@@ -792,6 +725,9 @@
|
||||
<listitem><para>
|
||||
Bazaar (<filename>bzr://</filename>)
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Perforce (<filename>p4://</filename>)
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Trees using Git Annex (<filename>gitannex://</filename>)
|
||||
</para></listitem>
|
||||
|
||||
@@ -399,7 +399,7 @@ ERROR: Unable to parse base: ParseError in configuration INHERITs: Could not inh
|
||||
<link linkend='var-BBFILES'>BBFILES</link> += "${LAYERDIR}/*.bb"
|
||||
|
||||
<link linkend='var-BBFILE_COLLECTIONS'>BBFILE_COLLECTIONS</link> += "mylayer"
|
||||
<link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR_RE}/"
|
||||
<link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR}/"
|
||||
</literallayout>
|
||||
For information on these variables, click the links
|
||||
to go to the definitions in the glossary.</para>
|
||||
|
||||
@@ -504,19 +504,9 @@
|
||||
Read the specified file before bitbake.conf.
|
||||
-R POSTFILE, --postread=POSTFILE
|
||||
Read the specified file after bitbake.conf.
|
||||
-v, --verbose Enable tracing of shell tasks (with 'set -x').
|
||||
Also print bb.note(...) messages to stdout (in
|
||||
addition to writing them to ${T}/log.do_<task>).
|
||||
-D, --debug Increase the debug level. You can specify this
|
||||
more than once. -D sets the debug level to 1,
|
||||
where only bb.debug(1, ...) messages are printed
|
||||
to stdout; -DD sets the debug level to 2, where
|
||||
both bb.debug(1, ...) and bb.debug(2, ...)
|
||||
messages are printed; etc. Without -D, no debug
|
||||
messages are printed. Note that -D only affects
|
||||
output to stdout. All debug messages are written
|
||||
to ${T}/log.do_taskname, regardless of the debug
|
||||
level.
|
||||
-v, --verbose Output more log message data to the terminal.
|
||||
-D, --debug Increase the debug level. You can specify this more
|
||||
than once.
|
||||
-n, --dry-run Don't execute, just go through the motions.
|
||||
-S SIGNATURE_HANDLER, --dump-signatures=SIGNATURE_HANDLER
|
||||
Dump out the signature construction information, with
|
||||
@@ -539,8 +529,8 @@
|
||||
-l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS
|
||||
Show debug logging for the specified logging domains
|
||||
-P, --profile Profile the command and save reports.
|
||||
-u UI, --ui=UI The user interface to use (taskexp, knotty or
|
||||
ncurses - default knotty).
|
||||
-u UI, --ui=UI The user interface to use (depexp, goggle, hob, knotty
|
||||
or ncurses - default knotty).
|
||||
-t SERVERTYPE, --servertype=SERVERTYPE
|
||||
Choose which server type to use (process or xmlrpc -
|
||||
default process).
|
||||
@@ -553,7 +543,6 @@
|
||||
-B BIND, --bind=BIND The name/address for the bitbake server to bind to.
|
||||
--no-setscene Do not run any setscene tasks. sstate will be ignored
|
||||
and everything needed, built.
|
||||
--setscene-only Only run setscene tasks, don't run any real tasks.
|
||||
--remote-server=REMOTE_SERVER
|
||||
Connect to the specified server.
|
||||
-m, --kill-server Terminate the remote server.
|
||||
@@ -645,25 +634,6 @@
|
||||
</para>
|
||||
</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'>
|
||||
<title>Generating Dependency Graphs</title>
|
||||
|
||||
@@ -676,21 +646,21 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When you generate a dependency graph, BitBake writes three files
|
||||
When you generate a dependency graph, BitBake writes four files
|
||||
to the current working directory:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
<emphasis><filename>recipe-depends.dot</filename>:</emphasis>
|
||||
Shows dependencies between recipes (i.e. a collapsed version of
|
||||
<filename>task-depends.dot</filename>).
|
||||
<listitem><para><emphasis><filename>package-depends.dot</filename>:</emphasis>
|
||||
Shows BitBake's knowledge of dependencies between
|
||||
runtime targets.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis><filename>task-depends.dot</filename>:</emphasis>
|
||||
<listitem><para><emphasis><filename>pn-depends.dot</filename>:</emphasis>
|
||||
Shows dependencies between build-time targets
|
||||
(i.e. recipes).
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis><filename>task-depends.dot</filename>:</emphasis>
|
||||
Shows dependencies between tasks.
|
||||
These dependencies match BitBake's internal task execution list.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis><filename>pn-buildlist</filename>:</emphasis>
|
||||
<listitem><para><emphasis><filename>pn-buildlist</filename>:</emphasis>
|
||||
Shows a simple list of targets that are to be built.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@
|
||||
<link linkend='var-MIRRORS'>M</link>
|
||||
<!-- <link linkend='var-glossary-n'>N</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-RDEPENDS'>R</link>
|
||||
<link linkend='var-SECTION'>S</link>
|
||||
@@ -716,7 +716,7 @@
|
||||
</glossdef>
|
||||
</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>
|
||||
<para>
|
||||
Specifies a function to call that verifies the list of
|
||||
@@ -986,29 +986,6 @@
|
||||
BBCLASSEXTEND =+ "native nativesdk"
|
||||
BBCLASSEXTEND =+ "multilib:<replaceable>multilib_name</replaceable>"
|
||||
</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>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
@@ -1017,7 +994,7 @@
|
||||
<glossdef>
|
||||
<para>
|
||||
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.
|
||||
<note>
|
||||
You must set this variable in the external environment
|
||||
@@ -1539,6 +1516,24 @@
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry id='var-FILESDIR'><glossterm>FILESDIR</glossterm>
|
||||
<glossdef>
|
||||
<para>
|
||||
Specifies directories BitBake uses when searching for
|
||||
patches and files.
|
||||
The "local" fetcher module uses these directories when
|
||||
handling <filename>file://</filename> URLs if the file
|
||||
was not found using
|
||||
<link linkend='var-FILESPATH'><filename>FILESPATH</filename></link>.
|
||||
<note>
|
||||
The <filename>FILESDIR</filename> variable is
|
||||
deprecated and you should use
|
||||
<filename>FILESPATH</filename> in all new code.
|
||||
</note>
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry id='var-FILESPATH'><glossterm>FILESPATH</glossterm>
|
||||
<glossdef>
|
||||
<para>
|
||||
@@ -1641,17 +1636,6 @@
|
||||
</glossdef>
|
||||
</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>
|
||||
<glossdef>
|
||||
<para>Optionally specifies the version of a layer as a single number.
|
||||
@@ -1753,15 +1737,6 @@
|
||||
|
||||
<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>
|
||||
<glossdef>
|
||||
<para>The list of packages the recipe creates.
|
||||
@@ -1958,27 +1933,6 @@
|
||||
The <filename>PROVIDES</filename> statement results in
|
||||
the "libav" recipe also being known as "libpostproc".
|
||||
</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>
|
||||
</glossentry>
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Show debug logging for the specified logging domains
|
||||
profile the command and print a report
|
||||
.TP
|
||||
.B \-uUI, \-\-ui=UI
|
||||
User interface to use. Currently, knotty, taskexp or ncurses can be specified as UI.
|
||||
User interface to use. Currently, hob, depexp, goggle or ncurses can be specified as UI.
|
||||
.TP
|
||||
.B \-tSERVERTYPE, \-\-servertype=SERVERTYPE
|
||||
Choose which server to use, none, process or xmlrpc.
|
||||
|
||||
@@ -23,17 +23,19 @@
|
||||
# Assign a file to __warn__ to get warnings about slow operations.
|
||||
#
|
||||
|
||||
|
||||
from __future__ import print_function
|
||||
import copy
|
||||
import types
|
||||
ImmutableTypes = (
|
||||
types.NoneType,
|
||||
bool,
|
||||
complex,
|
||||
float,
|
||||
int,
|
||||
long,
|
||||
tuple,
|
||||
frozenset,
|
||||
str
|
||||
basestring
|
||||
)
|
||||
|
||||
MUTABLE = "__mutable__"
|
||||
@@ -59,7 +61,7 @@ class COWDictMeta(COWMeta):
|
||||
__call__ = cow
|
||||
|
||||
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):
|
||||
cls.__hasmutable__ = True
|
||||
key += MUTABLE
|
||||
@@ -114,7 +116,7 @@ class COWDictMeta(COWMeta):
|
||||
cls.__setitem__(key, cls.__marker__)
|
||||
|
||||
def __revertitem__(cls, key):
|
||||
if key not in cls.__dict__:
|
||||
if not cls.__dict__.has_key(key):
|
||||
key += MUTABLE
|
||||
delattr(cls, key)
|
||||
|
||||
@@ -181,7 +183,7 @@ class COWSetMeta(COWDictMeta):
|
||||
COWDictMeta.__delitem__(cls, repr(hash(value)))
|
||||
|
||||
def __in__(cls, value):
|
||||
return repr(hash(value)) in COWDictMeta
|
||||
return COWDictMeta.has_key(repr(hash(value)))
|
||||
|
||||
def iterkeys(cls):
|
||||
raise TypeError("sets don't have keys")
|
||||
@@ -190,10 +192,12 @@ class COWSetMeta(COWDictMeta):
|
||||
raise TypeError("sets don't have 'items'")
|
||||
|
||||
# These are the actual classes you use!
|
||||
class COWDictBase(object, metaclass = COWDictMeta):
|
||||
class COWDictBase(object):
|
||||
__metaclass__ = COWDictMeta
|
||||
__count__ = 0
|
||||
|
||||
class COWSetBase(object, metaclass = COWSetMeta):
|
||||
class COWSetBase(object):
|
||||
__metaclass__ = COWSetMeta
|
||||
__count__ = 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -283,7 +287,7 @@ if __name__ == "__main__":
|
||||
except KeyError:
|
||||
print("Yay! deleted key raises error")
|
||||
|
||||
if 'b' in b:
|
||||
if b.has_key('b'):
|
||||
print("Boo!")
|
||||
else:
|
||||
print("Yay - has_key with delete works!")
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
__version__ = "1.34.0"
|
||||
__version__ = "1.30.0"
|
||||
|
||||
import sys
|
||||
if sys.version_info < (3, 4, 0):
|
||||
raise RuntimeError("Sorry, python 3.4.0 or later is required for this version of bitbake")
|
||||
if sys.version_info < (2, 7, 3):
|
||||
raise RuntimeError("Sorry, python 2.7.3 or later is required for this version of bitbake")
|
||||
|
||||
|
||||
class BBHandledException(Exception):
|
||||
@@ -84,8 +84,8 @@ def plain(*args):
|
||||
mainlogger.plain(''.join(args))
|
||||
|
||||
def debug(lvl, *args):
|
||||
if isinstance(lvl, str):
|
||||
mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
|
||||
if isinstance(lvl, basestring):
|
||||
mainlogger.warn("Passed invalid debug level '%s' to bb.debug", lvl)
|
||||
args = (lvl,) + args
|
||||
lvl = 1
|
||||
mainlogger.debug(lvl, ''.join(args))
|
||||
@@ -94,7 +94,7 @@ def note(*args):
|
||||
mainlogger.info(''.join(args))
|
||||
|
||||
def warn(*args):
|
||||
mainlogger.warning(''.join(args))
|
||||
mainlogger.warn(''.join(args))
|
||||
|
||||
def error(*args, **kwargs):
|
||||
mainlogger.error(''.join(args), extra=kwargs)
|
||||
|
||||
@@ -35,8 +35,8 @@ import stat
|
||||
import bb
|
||||
import bb.msg
|
||||
import bb.process
|
||||
import bb.progress
|
||||
from bb import data, event, utils
|
||||
from contextlib import nested
|
||||
from bb import event, utils
|
||||
|
||||
bblogger = logging.getLogger('BitBake')
|
||||
logger = logging.getLogger('BitBake.Build')
|
||||
@@ -61,13 +61,8 @@ def reset_cache():
|
||||
# in all namespaces, hence we add them to __builtins__.
|
||||
# If we do not do this and use the exec globals, they will
|
||||
# not be available to subfunctions.
|
||||
if hasattr(__builtins__, '__setitem__'):
|
||||
builtins = __builtins__
|
||||
else:
|
||||
builtins = __builtins__.__dict__
|
||||
|
||||
builtins['bb'] = bb
|
||||
builtins['os'] = os
|
||||
__builtins__['bb'] = bb
|
||||
__builtins__['os'] = os
|
||||
|
||||
class FuncFailed(Exception):
|
||||
def __init__(self, name = None, logfile = None):
|
||||
@@ -91,14 +86,13 @@ class TaskBase(event.Event):
|
||||
|
||||
def __init__(self, t, logfile, d):
|
||||
self._task = t
|
||||
self._package = d.getVar("PF")
|
||||
self._mc = d.getVar("BB_CURRENT_MC")
|
||||
self.taskfile = d.getVar("FILE")
|
||||
self._package = d.getVar("PF", True)
|
||||
self.taskfile = d.getVar("FILE", True)
|
||||
self.taskname = self._task
|
||||
self.logfile = logfile
|
||||
self.time = time.time()
|
||||
event.Event.__init__(self)
|
||||
self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
|
||||
self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
|
||||
|
||||
def getTask(self):
|
||||
return self._task
|
||||
@@ -139,25 +133,6 @@ class TaskInvalid(TaskBase):
|
||||
super(TaskInvalid, self).__init__(task, None, metadata)
|
||||
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):
|
||||
def __init__(self, logger, outfile):
|
||||
@@ -189,19 +164,20 @@ class LogTee(object):
|
||||
def exec_func(func, d, dirs = None, pythonexception=False):
|
||||
"""Execute a BB 'function'"""
|
||||
|
||||
try:
|
||||
oldcwd = os.getcwd()
|
||||
except:
|
||||
oldcwd = None
|
||||
body = d.getVar(func, False)
|
||||
if not body:
|
||||
if body is None:
|
||||
logger.warn("Function %s doesn't exist", func)
|
||||
return
|
||||
|
||||
flags = d.getVarFlags(func)
|
||||
cleandirs = flags.get('cleandirs') if flags else None
|
||||
cleandirs = flags.get('cleandirs')
|
||||
if cleandirs:
|
||||
for cdir in d.expand(cleandirs).split():
|
||||
bb.utils.remove(cdir, True)
|
||||
bb.utils.mkdirhier(cdir)
|
||||
|
||||
if flags and dirs is None:
|
||||
if dirs is None:
|
||||
dirs = flags.get('dirs')
|
||||
if dirs:
|
||||
dirs = d.expand(dirs).split()
|
||||
@@ -211,13 +187,8 @@ def exec_func(func, d, dirs = None, pythonexception=False):
|
||||
bb.utils.mkdirhier(adir)
|
||||
adir = dirs[-1]
|
||||
else:
|
||||
adir = None
|
||||
|
||||
body = d.getVar(func, False)
|
||||
if not body:
|
||||
if body is None:
|
||||
logger.warning("Function %s doesn't exist", func)
|
||||
return
|
||||
adir = d.getVar('B', True)
|
||||
bb.utils.mkdirhier(adir)
|
||||
|
||||
ispython = flags.get('python')
|
||||
|
||||
@@ -227,17 +198,17 @@ def exec_func(func, d, dirs = None, pythonexception=False):
|
||||
else:
|
||||
lockfiles = None
|
||||
|
||||
tempdir = d.getVar('T')
|
||||
tempdir = d.getVar('T', True)
|
||||
|
||||
# or func allows items to be executed outside of the normal
|
||||
# task set, such as buildhistory
|
||||
task = d.getVar('BB_RUNTASK') or func
|
||||
task = d.getVar('BB_RUNTASK', True) or func
|
||||
if task == func:
|
||||
taskfunc = task
|
||||
else:
|
||||
taskfunc = "%s.%s" % (task, func)
|
||||
|
||||
runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
|
||||
runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
|
||||
runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
|
||||
runfile = os.path.join(tempdir, runfn)
|
||||
bb.utils.mkdirhier(os.path.dirname(runfile))
|
||||
@@ -262,18 +233,6 @@ def exec_func(func, d, dirs = None, pythonexception=False):
|
||||
else:
|
||||
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 = """
|
||||
{function}(d)
|
||||
"""
|
||||
@@ -289,8 +248,7 @@ def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
|
||||
if cwd:
|
||||
try:
|
||||
olddir = os.getcwd()
|
||||
except OSError as e:
|
||||
bb.warn("%s: Cannot get cwd: %s" % (func, e))
|
||||
except OSError:
|
||||
olddir = None
|
||||
os.chdir(cwd)
|
||||
|
||||
@@ -316,8 +274,8 @@ def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
|
||||
if cwd and olddir:
|
||||
try:
|
||||
os.chdir(olddir)
|
||||
except OSError as e:
|
||||
bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def shell_trap_code():
|
||||
return '''#!/bin/sh\n
|
||||
@@ -365,11 +323,11 @@ trap '' 0
|
||||
exit $ret
|
||||
''')
|
||||
|
||||
os.chmod(runfile, 0o775)
|
||||
os.chmod(runfile, 0775)
|
||||
|
||||
cmd = runfile
|
||||
if d.getVarFlag(func, 'fakeroot', False):
|
||||
fakerootcmd = d.getVar('FAKEROOT')
|
||||
fakerootcmd = d.getVar('FAKEROOT', True)
|
||||
if fakerootcmd:
|
||||
cmd = [fakerootcmd, runfile]
|
||||
|
||||
@@ -378,64 +336,41 @@ exit $ret
|
||||
else:
|
||||
logfile = sys.stdout
|
||||
|
||||
progress = d.getVarFlag(func, 'progress')
|
||||
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):
|
||||
nonlocal fifobuffer
|
||||
fifobuffer.extend(data)
|
||||
while fifobuffer:
|
||||
message, token, nextmsg = fifobuffer.partition(b"\00")
|
||||
if token:
|
||||
splitval = message.split(b' ', 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
|
||||
lines = data.split('\0')
|
||||
for line in lines:
|
||||
splitval = line.split(' ', 1)
|
||||
cmd = splitval[0]
|
||||
if len(splitval) > 1:
|
||||
value = splitval[1]
|
||||
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')
|
||||
tempdir = d.getVar('T', True)
|
||||
fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
|
||||
if os.path.exists(fifopath):
|
||||
os.unlink(fifopath)
|
||||
os.mkfifo(fifopath)
|
||||
with open(fifopath, 'r+b', buffering=0) as fifo:
|
||||
with open(fifopath, 'r+') as fifo:
|
||||
try:
|
||||
bb.debug(2, "Executing shell function %s" % func)
|
||||
|
||||
@@ -443,7 +378,7 @@ exit $ret
|
||||
with open(os.devnull, 'r+') as stdin:
|
||||
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
|
||||
except bb.process.CmdError:
|
||||
logfn = d.getVar('BB_LOGFILE')
|
||||
logfn = d.getVar('BB_LOGFILE', True)
|
||||
raise FuncFailed(func, logfn)
|
||||
finally:
|
||||
os.unlink(fifopath)
|
||||
@@ -474,18 +409,18 @@ def _exec_task(fn, task, d, quieterr):
|
||||
logger.debug(1, "Executing task %s", task)
|
||||
|
||||
localdata = _task_data(fn, task, d)
|
||||
tempdir = localdata.getVar('T')
|
||||
tempdir = localdata.getVar('T', True)
|
||||
if not tempdir:
|
||||
bb.fatal("T variable not set, unable to build")
|
||||
|
||||
# Change nice level if we're asked to
|
||||
nice = localdata.getVar("BB_TASK_NICE_LEVEL")
|
||||
nice = localdata.getVar("BB_TASK_NICE_LEVEL", True)
|
||||
if nice:
|
||||
curnice = os.nice(0)
|
||||
nice = int(nice) - curnice
|
||||
newnice = os.nice(nice)
|
||||
logger.debug(1, "Renice to %s " % newnice)
|
||||
ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
|
||||
ionice = localdata.getVar("BB_TASK_IONICE_LEVEL", True)
|
||||
if ionice:
|
||||
try:
|
||||
cls, prio = ionice.split(".", 1)
|
||||
@@ -496,7 +431,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
bb.utils.mkdirhier(tempdir)
|
||||
|
||||
# Determine the logfile to generate
|
||||
logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
|
||||
logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}'
|
||||
logbase = logfmt.format(task=task, pid=os.getpid())
|
||||
|
||||
# Document the order of the tasks...
|
||||
@@ -563,36 +498,24 @@ def _exec_task(fn, task, d, quieterr):
|
||||
|
||||
localdata.setVar('BB_LOGFILE', logfn)
|
||||
localdata.setVar('BB_RUNTASK', task)
|
||||
localdata.setVar('BB_TASK_LOGGER', bblogger)
|
||||
|
||||
flags = localdata.getVarFlags(task)
|
||||
|
||||
event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
|
||||
try:
|
||||
try:
|
||||
event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
|
||||
except (bb.BBHandledException, SystemExit):
|
||||
return 1
|
||||
except FuncFailed as exc:
|
||||
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))
|
||||
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
|
||||
event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
|
||||
return 1
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
@@ -629,7 +552,7 @@ def exec_task(fn, task, d, profile = False):
|
||||
quieterr = True
|
||||
|
||||
if profile:
|
||||
profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
|
||||
profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
|
||||
try:
|
||||
import cProfile as profile
|
||||
except:
|
||||
@@ -652,7 +575,7 @@ def exec_task(fn, task, d, profile = False):
|
||||
event.fire(failedevent, d)
|
||||
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
|
||||
Makes sure the stamp directory exists
|
||||
@@ -669,14 +592,12 @@ def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
|
||||
stamp = d.stamp[file_name]
|
||||
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
|
||||
else:
|
||||
stamp = d.getVar('STAMP')
|
||||
file_name = d.getVar('BB_FILENAME')
|
||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
|
||||
stamp = d.getVar('STAMP', True)
|
||||
file_name = d.getVar('BB_FILENAME', True)
|
||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
||||
|
||||
if baseonly:
|
||||
return stamp
|
||||
if noextra:
|
||||
extrainfo = ""
|
||||
|
||||
if not stamp:
|
||||
return
|
||||
@@ -705,9 +626,9 @@ def stamp_cleanmask_internal(taskname, d, file_name):
|
||||
stamp = d.stampclean[file_name]
|
||||
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
|
||||
else:
|
||||
stamp = d.getVar('STAMPCLEAN')
|
||||
file_name = d.getVar('BB_FILENAME')
|
||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
|
||||
stamp = d.getVar('STAMPCLEAN', True)
|
||||
file_name = d.getVar('BB_FILENAME', True)
|
||||
extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
|
||||
|
||||
if not stamp:
|
||||
return []
|
||||
@@ -743,7 +664,7 @@ def make_stamp(task, d, file_name = None):
|
||||
# as it completes
|
||||
if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
|
||||
stampbase = stamp_internal(task, d, None, True)
|
||||
file_name = d.getVar('BB_FILENAME')
|
||||
file_name = d.getVar('BB_FILENAME', True)
|
||||
bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
|
||||
|
||||
def del_stamp(task, d, file_name = None):
|
||||
@@ -765,19 +686,19 @@ def write_taint(task, d, file_name = None):
|
||||
if file_name:
|
||||
taintfn = d.stamp[file_name] + '.' + task + '.taint'
|
||||
else:
|
||||
taintfn = d.getVar('STAMP') + '.' + task + '.taint'
|
||||
taintfn = d.getVar('STAMP', True) + '.' + task + '.taint'
|
||||
bb.utils.mkdirhier(os.path.dirname(taintfn))
|
||||
# The specific content of the taint file is not really important,
|
||||
# we just need it to be random, so a random UUID is used
|
||||
with open(taintfn, 'w') as taintf:
|
||||
taintf.write(str(uuid.uuid4()))
|
||||
|
||||
def stampfile(taskname, d, file_name = None, noextra=False):
|
||||
def stampfile(taskname, d, file_name = None):
|
||||
"""
|
||||
Return the stamp for a given task
|
||||
(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):
|
||||
task_deps = d.getVar('_task_deps', False)
|
||||
@@ -853,7 +774,6 @@ def deltask(task, d):
|
||||
bbtasks = d.getVar('__BBTASKS', False) or []
|
||||
if task in bbtasks:
|
||||
bbtasks.remove(task)
|
||||
d.delVarFlag(task, 'task')
|
||||
d.setVar('__BBTASKS', bbtasks)
|
||||
|
||||
d.delVarFlag(task, 'deps')
|
||||
@@ -862,46 +782,3 @@ def deltask(task, d):
|
||||
if task in deps:
|
||||
deps.remove(task)
|
||||
d.setVarFlag(bbtask, 'deps', deps)
|
||||
|
||||
def preceedtask(task, with_recrdeptasks, d):
|
||||
"""
|
||||
Returns a set of tasks in the current recipe which were specified as
|
||||
precondition by the task itself ("after") or which listed themselves
|
||||
as precondition ("before"). Preceeding tasks specified via the
|
||||
"recrdeptask" are included in the result only if requested. Beware
|
||||
that this may lead to the task itself being listed.
|
||||
"""
|
||||
preceed = set()
|
||||
preceed.update(d.getVarFlag(task, 'deps') or [])
|
||||
if with_recrdeptasks:
|
||||
recrdeptask = d.getVarFlag(task, 'recrdeptask')
|
||||
if recrdeptask:
|
||||
preceed.update(recrdeptask.split())
|
||||
return preceed
|
||||
|
||||
def tasksbetween(task_start, task_end, d):
|
||||
"""
|
||||
Return the list of tasks between two tasks in the current recipe,
|
||||
where task_start is to start at and task_end is the task to end at
|
||||
(and task_end has a dependency chain back to task_start).
|
||||
"""
|
||||
outtasks = []
|
||||
tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
|
||||
def follow_chain(task, endtask, chain=None):
|
||||
if not chain:
|
||||
chain = []
|
||||
chain.append(task)
|
||||
for othertask in tasks:
|
||||
if othertask == task:
|
||||
continue
|
||||
if task == endtask:
|
||||
for ctask in chain:
|
||||
if ctask not in outtasks:
|
||||
outtasks.append(ctask)
|
||||
else:
|
||||
deps = d.getVarFlag(othertask, 'deps', False)
|
||||
if task in deps:
|
||||
follow_chain(othertask, endtask, chain)
|
||||
chain.pop()
|
||||
follow_chain(task_start, task_end)
|
||||
return outtasks
|
||||
|
||||
@@ -28,16 +28,22 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import pickle
|
||||
from collections import defaultdict
|
||||
import bb.utils
|
||||
|
||||
logger = logging.getLogger("BitBake.Cache")
|
||||
|
||||
__cache_version__ = "151"
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
logger.info("Importing cPickle failed. "
|
||||
"Falling back to a very slow implementation.")
|
||||
|
||||
__cache_version__ = "149"
|
||||
|
||||
def getCacheFile(path, filename, data_hash):
|
||||
return os.path.join(path, filename + "." + data_hash)
|
||||
@@ -71,10 +77,10 @@ class RecipeInfoCommon(object):
|
||||
|
||||
@classmethod
|
||||
def flaglist(cls, flag, varlist, metadata, squash=False):
|
||||
out_dict = dict((var, metadata.getVarFlag(var, flag))
|
||||
out_dict = dict((var, metadata.getVarFlag(var, flag, True))
|
||||
for var in varlist)
|
||||
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:
|
||||
return out_dict
|
||||
|
||||
@@ -234,7 +240,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
||||
cachedata.universe_target.append(self.pn)
|
||||
|
||||
cachedata.hashfn[fn] = self.hashfilename
|
||||
for task, taskhash in self.basetaskhashes.items():
|
||||
for task, taskhash in self.basetaskhashes.iteritems():
|
||||
identifier = '%s.%s' % (fn, task)
|
||||
cachedata.basetaskhash[identifier] = taskhash
|
||||
|
||||
@@ -244,144 +250,23 @@ class CoreRecipeInfo(RecipeInfoCommon):
|
||||
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') 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):
|
||||
|
||||
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):
|
||||
class Cache(object):
|
||||
"""
|
||||
BitBake Cache implementation
|
||||
"""
|
||||
|
||||
def __init__(self, databuilder, data_hash, caches_array):
|
||||
super().__init__(databuilder)
|
||||
data = databuilder.data
|
||||
|
||||
def __init__(self, data, data_hash, caches_array):
|
||||
# Pass caches_array information into Cache Constructor
|
||||
# It will be used later for deciding whether we
|
||||
# need extra cache file dump/load support
|
||||
self.caches_array = caches_array
|
||||
self.cachedir = data.getVar("CACHE")
|
||||
self.cachedir = data.getVar("CACHE", True)
|
||||
self.clean = set()
|
||||
self.checked = set()
|
||||
self.depends_cache = {}
|
||||
self.data = None
|
||||
self.data_fn = None
|
||||
self.cacheclean = True
|
||||
self.data_hash = data_hash
|
||||
@@ -401,78 +286,72 @@ class Cache(NoCache):
|
||||
cache_ok = True
|
||||
if self.caches_array:
|
||||
for cache_class in self.caches_array:
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
cache_ok = cache_ok and os.path.exists(cachefile)
|
||||
cache_class.init_cacheData(self)
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
cache_ok = cache_ok and os.path.exists(cachefile)
|
||||
cache_class.init_cacheData(self)
|
||||
if cache_ok:
|
||||
self.load_cachefile()
|
||||
elif os.path.isfile(self.cachefile):
|
||||
logger.info("Out of date cache found, rebuilding...")
|
||||
|
||||
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
|
||||
previous_progress = 0
|
||||
previous_percent = 0
|
||||
|
||||
# Calculate the correct cachesize of all those cache files
|
||||
for cache_class in self.caches_array:
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
with open(cachefile, "rb") as cachefile:
|
||||
cachesize += os.fstat(cachefile.fileno()).st_size
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
with open(cachefile, "rb") as cachefile:
|
||||
cachesize += os.fstat(cachefile.fileno()).st_size
|
||||
|
||||
bb.event.fire(bb.event.CacheLoadStarted(cachesize), self.data)
|
||||
|
||||
for cache_class in self.caches_array:
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
with open(cachefile, "rb") as cachefile:
|
||||
pickled = pickle.Unpickler(cachefile)
|
||||
# Check cache version information
|
||||
try:
|
||||
cache_ver = pickled.load()
|
||||
bitbake_ver = pickled.load()
|
||||
except Exception:
|
||||
logger.info('Invalid cache, rebuilding...')
|
||||
return
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
with open(cachefile, "rb") as cachefile:
|
||||
pickled = pickle.Unpickler(cachefile)
|
||||
while cachefile:
|
||||
try:
|
||||
key = pickled.load()
|
||||
value = pickled.load()
|
||||
except Exception:
|
||||
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__:
|
||||
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
|
||||
if current_progress > cachesize:
|
||||
# we might have calculated incorrect total size because a file
|
||||
# might've been written out just after we checked its size
|
||||
cachesize = current_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
|
||||
previous_progress += current_progress
|
||||
|
||||
# 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
|
||||
@@ -480,33 +359,69 @@ class Cache(NoCache):
|
||||
len(self.depends_cache)),
|
||||
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"""
|
||||
logger.debug(1, "Parsing %s", filename)
|
||||
infos = []
|
||||
datastores = self.load_bbfile(filename, appends)
|
||||
datastores = cls.load_bbfile(filename, appends, configdata)
|
||||
depends = []
|
||||
variants = []
|
||||
# Process the "real" fn last so we can store variants list
|
||||
for variant, data in sorted(datastores.items(),
|
||||
for variant, data in sorted(datastores.iteritems(),
|
||||
key=lambda i: i[0],
|
||||
reverse=True):
|
||||
virtualfn = variant2virtual(filename, variant)
|
||||
variants.append(variant)
|
||||
virtualfn = cls.realfn2virtual(filename, variant)
|
||||
depends = depends + (data.getVar("__depends", False) or [])
|
||||
if depends and not variant:
|
||||
data.setVar("__depends", depends)
|
||||
if virtualfn == filename:
|
||||
data.setVar("__VARIANTS", " ".join(variants))
|
||||
|
||||
info_array = []
|
||||
for cache_class in self.caches_array:
|
||||
info = cache_class(filename, data)
|
||||
info_array.append(info)
|
||||
for cache_class in caches_array:
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
info = cache_class(filename, data)
|
||||
info_array.append(info)
|
||||
infos.append((virtualfn, info_array))
|
||||
|
||||
return infos
|
||||
|
||||
def load(self, filename, appends):
|
||||
def load(self, filename, appends, configdata):
|
||||
"""Obtain the recipe information for the specified filename,
|
||||
using cached values if available, otherwise parsing.
|
||||
|
||||
@@ -520,20 +435,21 @@ class Cache(NoCache):
|
||||
# info_array item is a list of [CoreRecipeInfo, XXXRecipeInfo]
|
||||
info_array = self.depends_cache[filename]
|
||||
for variant in info_array[0].variants:
|
||||
virtualfn = variant2virtual(filename, variant)
|
||||
virtualfn = self.realfn2virtual(filename, variant)
|
||||
infos.append((virtualfn, self.depends_cache[virtualfn]))
|
||||
else:
|
||||
logger.debug(1, "Parsing %s", filename)
|
||||
return self.parse(filename, appends, configdata, self.caches_array)
|
||||
|
||||
return cached, infos
|
||||
|
||||
def loadData(self, fn, appends, cacheData):
|
||||
def loadData(self, fn, appends, cfgData, cacheData):
|
||||
"""Load the recipe info for the specified filename,
|
||||
parsing and adding to the cache if necessary, and adding
|
||||
the recipe information to the supplied CacheData instance."""
|
||||
skipped, virtuals = 0, 0
|
||||
|
||||
cached, infos = self.load(fn, appends)
|
||||
cached, infos = self.load(fn, appends, cfgData)
|
||||
for virtualfn, info_array in infos:
|
||||
if info_array[0].skipped:
|
||||
logger.debug(1, "Skipping %s: %s", virtualfn, info_array[0].skipreason)
|
||||
@@ -641,19 +557,16 @@ class Cache(NoCache):
|
||||
|
||||
invalid = False
|
||||
for cls in info_array[0].variants:
|
||||
virtualfn = variant2virtual(fn, cls)
|
||||
virtualfn = self.realfn2virtual(fn, cls)
|
||||
self.clean.add(virtualfn)
|
||||
if virtualfn not in self.depends_cache:
|
||||
logger.debug(2, "Cache: %s is not cached", virtualfn)
|
||||
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 invalid:
|
||||
for cls in info_array[0].variants:
|
||||
virtualfn = variant2virtual(fn, cls)
|
||||
virtualfn = self.realfn2virtual(fn, cls)
|
||||
if virtualfn in self.clean:
|
||||
logger.debug(2, "Cache: Removing %s from cache", virtualfn)
|
||||
self.clean.remove(virtualfn)
|
||||
@@ -690,19 +603,30 @@ class Cache(NoCache):
|
||||
logger.debug(2, "Cache is clean, not saving.")
|
||||
return
|
||||
|
||||
file_dict = {}
|
||||
pickler_dict = {}
|
||||
for cache_class in self.caches_array:
|
||||
cache_class_name = cache_class.__name__
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
with open(cachefile, "wb") as f:
|
||||
p = pickle.Pickler(f, pickle.HIGHEST_PROTOCOL)
|
||||
p.dump(__cache_version__)
|
||||
p.dump(bb.__version__)
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
cache_class_name = cache_class.__name__
|
||||
cachefile = getCacheFile(self.cachedir, cache_class.cachefile, self.data_hash)
|
||||
file_dict[cache_class_name] = open(cachefile, "wb")
|
||||
pickler_dict[cache_class_name] = pickle.Pickler(file_dict[cache_class_name], pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
pickler_dict['CoreRecipeInfo'].dump(__cache_version__)
|
||||
pickler_dict['CoreRecipeInfo'].dump(bb.__version__)
|
||||
|
||||
for key, info_array in self.depends_cache.items():
|
||||
for info in info_array:
|
||||
if isinstance(info, RecipeInfoCommon) and info.__class__.__name__ == cache_class_name:
|
||||
p.dump(key)
|
||||
p.dump(info)
|
||||
try:
|
||||
for key, info_array in self.depends_cache.iteritems():
|
||||
for info in info_array:
|
||||
if isinstance(info, RecipeInfoCommon):
|
||||
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
|
||||
|
||||
@@ -730,13 +654,50 @@ class Cache(NoCache):
|
||||
Save data we need into the cache
|
||||
"""
|
||||
|
||||
realfn = virtualfn2realfn(file_name)[0]
|
||||
realfn = self.virtualfn2realfn(file_name)[0]
|
||||
|
||||
info_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)
|
||||
|
||||
@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):
|
||||
"""
|
||||
@@ -766,9 +727,8 @@ class CacheData(object):
|
||||
def __init__(self, caches_array):
|
||||
self.caches_array = caches_array
|
||||
for cache_class in self.caches_array:
|
||||
if not issubclass(cache_class, RecipeInfoCommon):
|
||||
bb.error("Extra cache data class %s should subclass RecipeInfoCommon class" % cache_class)
|
||||
cache_class.init_cacheData(self)
|
||||
if type(cache_class) is type and issubclass(cache_class, RecipeInfoCommon):
|
||||
cache_class.init_cacheData(self)
|
||||
|
||||
# Direct cache variables
|
||||
self.task_queues = {}
|
||||
@@ -796,8 +756,8 @@ class MultiProcessCache(object):
|
||||
self.cachedata_extras = self.create_cachedata()
|
||||
|
||||
def init_cache(self, d, cache_file_name=None):
|
||||
cachedir = (d.getVar("PERSISTENT_DIR") or
|
||||
d.getVar("CACHE"))
|
||||
cachedir = (d.getVar("PERSISTENT_DIR", True) or
|
||||
d.getVar("CACHE", True))
|
||||
if cachedir in [None, '']:
|
||||
return
|
||||
bb.utils.mkdirhier(cachedir)
|
||||
|
||||
@@ -19,13 +19,20 @@ import glob
|
||||
import operator
|
||||
import os
|
||||
import stat
|
||||
import pickle
|
||||
import bb.utils
|
||||
import logging
|
||||
from bb.cache import MultiProcessCache
|
||||
|
||||
logger = logging.getLogger("BitBake.Cache")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
logger.info("Importing cPickle failed. "
|
||||
"Falling back to a very slow implementation.")
|
||||
|
||||
|
||||
# mtime cache (non-persistent)
|
||||
# based upon the assumption that files do not change during bitbake run
|
||||
class FileMtimeCache(object):
|
||||
|
||||
@@ -1,39 +1,21 @@
|
||||
"""
|
||||
BitBake code parser
|
||||
|
||||
Parses actual code (i.e. python and shell) for functions and in-line
|
||||
expressions. Used mainly to determine dependencies on other functions
|
||||
and variables within the BitBake metadata. Also provides a cache for
|
||||
this information in order to speed up processing.
|
||||
|
||||
(Not to be confused with the code that parses the metadata itself,
|
||||
see lib/bb/parse/ for that).
|
||||
|
||||
NOTE: if you change how the parsers gather information you will almost
|
||||
certainly need to increment CodeParserCache.CACHE_VERSION below so that
|
||||
any existing codeparser cache gets invalidated. Additionally you'll need
|
||||
to increment __cache_version__ in cache.py in order to ensure that old
|
||||
recipe caches don't trigger "Taskhash mismatch" errors.
|
||||
|
||||
"""
|
||||
|
||||
import ast
|
||||
import sys
|
||||
import codegen
|
||||
import logging
|
||||
import pickle
|
||||
import bb.pysh as pysh
|
||||
import os.path
|
||||
import bb.utils, bb.data
|
||||
import hashlib
|
||||
from itertools import chain
|
||||
from bb.pysh import pyshyacc, pyshlex, sherrors
|
||||
from pysh import pyshyacc, pyshlex, sherrors
|
||||
from bb.cache import MultiProcessCache
|
||||
|
||||
|
||||
logger = logging.getLogger('BitBake.CodeParser')
|
||||
|
||||
def bbhash(s):
|
||||
return hashlib.md5(s.encode("utf-8")).hexdigest()
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
logger.info('Importing cPickle failed. Falling back to a very slow implementation.')
|
||||
|
||||
|
||||
def check_indent(codestr):
|
||||
"""If the code is indented, add a top level piece of code to 'remove' the indentation"""
|
||||
@@ -86,12 +68,11 @@ class SetCache(object):
|
||||
|
||||
new = []
|
||||
for i in items:
|
||||
new.append(sys.intern(i))
|
||||
new.append(intern(i))
|
||||
s = frozenset(new)
|
||||
h = hash(s)
|
||||
if h in self.setcache:
|
||||
return self.setcache[h]
|
||||
self.setcache[h] = s
|
||||
if hash(s) in self.setcache:
|
||||
return self.setcache[hash(s)]
|
||||
self.setcache[hash(s)] = s
|
||||
return s
|
||||
|
||||
codecache = SetCache()
|
||||
@@ -136,11 +117,7 @@ class shellCacheLine(object):
|
||||
|
||||
class CodeParserCache(MultiProcessCache):
|
||||
cache_file_name = "bb_codeparser.dat"
|
||||
# NOTE: you must increment this if you change how the parsers gather information,
|
||||
# so that an existing cache gets invalidated. Additionally you'll need
|
||||
# to increment __cache_version__ in cache.py in order to ensure that old
|
||||
# recipe caches don't trigger "Taskhash mismatch" errors.
|
||||
CACHE_VERSION = 9
|
||||
CACHE_VERSION = 7
|
||||
|
||||
def __init__(self):
|
||||
MultiProcessCache.__init__(self)
|
||||
@@ -209,15 +186,12 @@ class BufferedLogger(Logger):
|
||||
|
||||
def flush(self):
|
||||
for record in self.buffer:
|
||||
if self.target.isEnabledFor(record.levelno):
|
||||
self.target.handle(record)
|
||||
self.target.handle(record)
|
||||
self.buffer = []
|
||||
|
||||
class PythonParser():
|
||||
getvars = (".getVar", ".appendVar", ".prependVar")
|
||||
getvarflags = (".getVarFlag", ".appendVarFlag", ".prependVarFlag")
|
||||
containsfuncs = ("bb.utils.contains", "base_contains")
|
||||
containsanyfuncs = ("bb.utils.contains_any", "bb.utils.filter")
|
||||
containsfuncs = ("bb.utils.contains", "base_contains", "bb.utils.contains_any")
|
||||
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
|
||||
|
||||
def warn(self, func, arg):
|
||||
@@ -236,24 +210,15 @@ class PythonParser():
|
||||
|
||||
def visit_Call(self, node):
|
||||
name = self.called_node_name(node.func)
|
||||
if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs):
|
||||
if name and name.endswith(self.getvars) or name in self.containsfuncs:
|
||||
if isinstance(node.args[0], ast.Str):
|
||||
varname = node.args[0].s
|
||||
if name in self.containsfuncs and isinstance(node.args[1], ast.Str):
|
||||
if varname not in self.contains:
|
||||
self.contains[varname] = set()
|
||||
self.contains[varname].add(node.args[1].s)
|
||||
elif name in self.containsanyfuncs and isinstance(node.args[1], ast.Str):
|
||||
if varname not in self.contains:
|
||||
self.contains[varname] = set()
|
||||
self.contains[varname].update(node.args[1].s.split())
|
||||
elif name.endswith(self.getvarflags):
|
||||
if isinstance(node.args[1], ast.Str):
|
||||
self.references.add('%s[%s]' % (varname, node.args[1].s))
|
||||
else:
|
||||
self.warn(node.func, node.args[1])
|
||||
else:
|
||||
self.references.add(varname)
|
||||
else:
|
||||
self.references.add(node.args[0].s)
|
||||
else:
|
||||
self.warn(node.func, node.args[0])
|
||||
elif name and name.endswith(".expand"):
|
||||
@@ -303,7 +268,7 @@ class PythonParser():
|
||||
if not node or not node.strip():
|
||||
return
|
||||
|
||||
h = bbhash(str(node))
|
||||
h = hash(str(node))
|
||||
|
||||
if h in codeparsercache.pythoncache:
|
||||
self.references = set(codeparsercache.pythoncache[h].refs)
|
||||
@@ -348,7 +313,7 @@ class ShellParser():
|
||||
commands it executes.
|
||||
"""
|
||||
|
||||
h = bbhash(str(value))
|
||||
h = hash(str(value))
|
||||
|
||||
if h in codeparsercache.shellcache:
|
||||
self.execs = set(codeparsercache.shellcache[h].execs)
|
||||
@@ -371,7 +336,8 @@ class ShellParser():
|
||||
except pyshlex.NeedMore:
|
||||
raise sherrors.ShellSyntaxError("Unexpected EOF")
|
||||
|
||||
self.process_tokens(tokens)
|
||||
for token in tokens:
|
||||
self.process_tokens(token)
|
||||
|
||||
def process_tokens(self, tokens):
|
||||
"""Process a supplied portion of the syntax tree as returned by
|
||||
@@ -417,24 +383,18 @@ class ShellParser():
|
||||
"case_clause": case_clause,
|
||||
}
|
||||
|
||||
def process_token_list(tokens):
|
||||
for token in tokens:
|
||||
if isinstance(token, list):
|
||||
process_token_list(token)
|
||||
continue
|
||||
name, value = token
|
||||
try:
|
||||
more_tokens, words = token_handlers[name](value)
|
||||
except KeyError:
|
||||
raise NotImplementedError("Unsupported token type " + name)
|
||||
for token in tokens:
|
||||
name, value = token
|
||||
try:
|
||||
more_tokens, words = token_handlers[name](value)
|
||||
except KeyError:
|
||||
raise NotImplementedError("Unsupported token type " + name)
|
||||
|
||||
if more_tokens:
|
||||
self.process_tokens(more_tokens)
|
||||
if more_tokens:
|
||||
self.process_tokens(more_tokens)
|
||||
|
||||
if words:
|
||||
self.process_words(words)
|
||||
|
||||
process_token_list(tokens)
|
||||
if words:
|
||||
self.process_words(words)
|
||||
|
||||
def process_words(self, words):
|
||||
"""Process a set of 'words' in pyshyacc parlance, which includes
|
||||
|
||||
@@ -28,15 +28,8 @@ and must not trigger events, directly or indirectly.
|
||||
Commands are queued in a CommandQueue
|
||||
"""
|
||||
|
||||
from collections import OrderedDict, defaultdict
|
||||
|
||||
import bb.event
|
||||
import bb.cooker
|
||||
import bb.remotedata
|
||||
|
||||
class DataStoreConnectionHandle(object):
|
||||
def __init__(self, dsindex=0):
|
||||
self.dsindex = dsindex
|
||||
|
||||
class CommandCompleted(bb.event.Event):
|
||||
pass
|
||||
@@ -62,7 +55,6 @@ class Command:
|
||||
self.cooker = cooker
|
||||
self.cmds_sync = CommandsSync()
|
||||
self.cmds_async = CommandsAsync()
|
||||
self.remotedatastores = bb.remotedata.RemoteDatastores(cooker)
|
||||
|
||||
# FIXME Add lock for this
|
||||
self.currentAsyncCommand = None
|
||||
@@ -118,7 +110,7 @@ class Command:
|
||||
return False
|
||||
except SystemExit as exc:
|
||||
arg = exc.args[0]
|
||||
if isinstance(arg, str):
|
||||
if isinstance(arg, basestring):
|
||||
self.finishAsyncCommand(arg)
|
||||
else:
|
||||
self.finishAsyncCommand("Exited with %s" % arg)
|
||||
@@ -133,20 +125,14 @@ class Command:
|
||||
|
||||
def finishAsyncCommand(self, msg=None, code=None):
|
||||
if msg or msg == "":
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.data)
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.expanded_data)
|
||||
elif code:
|
||||
bb.event.fire(CommandExit(code), self.cooker.data)
|
||||
bb.event.fire(CommandExit(code), self.cooker.expanded_data)
|
||||
else:
|
||||
bb.event.fire(CommandCompleted(), self.cooker.data)
|
||||
bb.event.fire(CommandCompleted(), self.cooker.expanded_data)
|
||||
self.currentAsyncCommand = None
|
||||
self.cooker.finishcommand()
|
||||
|
||||
def split_mc_pn(pn):
|
||||
if pn.startswith("multiconfig:"):
|
||||
_, mc, pn = pn.split(":", 2)
|
||||
return (mc, pn)
|
||||
return ('', pn)
|
||||
|
||||
class CommandsSync:
|
||||
"""
|
||||
A class of synchronous commands
|
||||
@@ -193,7 +179,6 @@ class CommandsSync:
|
||||
"""
|
||||
varname = params[0]
|
||||
value = str(params[1])
|
||||
command.cooker.extraconfigdata[varname] = value
|
||||
command.cooker.data.setVar(varname, value)
|
||||
|
||||
def getSetVariable(self, command, params):
|
||||
@@ -310,274 +295,9 @@ class CommandsSync:
|
||||
def updateConfig(self, command, params):
|
||||
options = params[0]
|
||||
environment = params[1]
|
||||
cmdline = params[2]
|
||||
command.cooker.updateConfigOpts(options, environment, cmdline)
|
||||
command.cooker.updateConfigOpts(options, environment)
|
||||
updateConfig.needconfig = False
|
||||
|
||||
def parseConfiguration(self, command, params):
|
||||
"""Instruct bitbake to parse its configuration
|
||||
NOTE: it is only necessary to call this if you aren't calling any normal action
|
||||
(otherwise parsing is taken care of automatically)
|
||||
"""
|
||||
command.cooker.parseConfiguration()
|
||||
parseConfiguration.needconfig = False
|
||||
|
||||
def getLayerPriorities(self, command, params):
|
||||
ret = []
|
||||
# regex objects cannot be marshalled by xmlrpc
|
||||
for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
|
||||
ret.append((collection, pattern, regex.pattern, pri))
|
||||
return ret
|
||||
getLayerPriorities.readonly = True
|
||||
|
||||
def getRecipes(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return list(command.cooker.recipecaches[mc].pkg_pn.items())
|
||||
getRecipes.readonly = True
|
||||
|
||||
def getRecipeDepends(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return list(command.cooker.recipecaches[mc].deps.items())
|
||||
getRecipeDepends.readonly = True
|
||||
|
||||
def getRecipeVersions(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return command.cooker.recipecaches[mc].pkg_pepvpr
|
||||
getRecipeVersions.readonly = True
|
||||
|
||||
def getRuntimeDepends(self, command, params):
|
||||
ret = []
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
rundeps = command.cooker.recipecaches[mc].rundeps
|
||||
for key, value in rundeps.items():
|
||||
if isinstance(value, defaultdict):
|
||||
value = dict(value)
|
||||
ret.append((key, value))
|
||||
return ret
|
||||
getRuntimeDepends.readonly = True
|
||||
|
||||
def getRuntimeRecommends(self, command, params):
|
||||
ret = []
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
runrecs = command.cooker.recipecaches[mc].runrecs
|
||||
for key, value in runrecs.items():
|
||||
if isinstance(value, defaultdict):
|
||||
value = dict(value)
|
||||
ret.append((key, value))
|
||||
return ret
|
||||
getRuntimeRecommends.readonly = True
|
||||
|
||||
def getRecipeInherits(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return command.cooker.recipecaches[mc].inherits
|
||||
getRecipeInherits.readonly = True
|
||||
|
||||
def getBbFilePriority(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return command.cooker.recipecaches[mc].bbfile_priority
|
||||
getBbFilePriority.readonly = True
|
||||
|
||||
def getDefaultPreference(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return command.cooker.recipecaches[mc].pkg_dp
|
||||
getDefaultPreference.readonly = True
|
||||
|
||||
def getSkippedRecipes(self, command, params):
|
||||
# Return list sorted by reverse priority order
|
||||
import bb.cache
|
||||
skipdict = OrderedDict(sorted(command.cooker.skiplist.items(),
|
||||
key=lambda x: (-command.cooker.collection.calc_bbfile_priority(bb.cache.virtualfn2realfn(x[0])[0]), x[0])))
|
||||
return list(skipdict.items())
|
||||
getSkippedRecipes.readonly = True
|
||||
|
||||
def getOverlayedRecipes(self, command, params):
|
||||
return list(command.cooker.collection.overlayed.items())
|
||||
getOverlayedRecipes.readonly = True
|
||||
|
||||
def getFileAppends(self, command, params):
|
||||
fn = params[0]
|
||||
return command.cooker.collection.get_file_appends(fn)
|
||||
getFileAppends.readonly = True
|
||||
|
||||
def getAllAppends(self, command, params):
|
||||
return command.cooker.collection.bbappends
|
||||
getAllAppends.readonly = True
|
||||
|
||||
def findProviders(self, command, params):
|
||||
return command.cooker.findProviders()
|
||||
findProviders.readonly = True
|
||||
|
||||
def findBestProvider(self, command, params):
|
||||
(mc, pn) = split_mc_pn(params[0])
|
||||
return command.cooker.findBestProvider(pn, mc)
|
||||
findBestProvider.readonly = True
|
||||
|
||||
def allProviders(self, command, params):
|
||||
try:
|
||||
mc = params[0]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
|
||||
allProviders.readonly = True
|
||||
|
||||
def getRuntimeProviders(self, command, params):
|
||||
rprovide = params[0]
|
||||
try:
|
||||
mc = params[1]
|
||||
except IndexError:
|
||||
mc = ''
|
||||
all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
|
||||
if all_p:
|
||||
best = bb.providers.filterProvidersRunTime(all_p, rprovide,
|
||||
command.cooker.data,
|
||||
command.cooker.recipecaches[mc])[0][0]
|
||||
else:
|
||||
best = None
|
||||
return all_p, best
|
||||
getRuntimeProviders.readonly = True
|
||||
|
||||
def dataStoreConnectorFindVar(self, command, params):
|
||||
dsindex = params[0]
|
||||
name = params[1]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
value, overridedata = datastore._findVar(name)
|
||||
|
||||
if value:
|
||||
content = value.get('_content', None)
|
||||
if isinstance(content, bb.data_smart.DataSmart):
|
||||
# Value is a datastore (e.g. BB_ORIGENV) - need to handle this carefully
|
||||
idx = command.remotedatastores.check_store(content, True)
|
||||
return {'_content': DataStoreConnectionHandle(idx),
|
||||
'_connector_origtype': 'DataStoreConnectionHandle',
|
||||
'_connector_overrides': overridedata}
|
||||
elif isinstance(content, set):
|
||||
return {'_content': list(content),
|
||||
'_connector_origtype': 'set',
|
||||
'_connector_overrides': overridedata}
|
||||
else:
|
||||
value['_connector_overrides'] = overridedata
|
||||
else:
|
||||
value = {}
|
||||
value['_connector_overrides'] = overridedata
|
||||
return value
|
||||
dataStoreConnectorFindVar.readonly = True
|
||||
|
||||
def dataStoreConnectorGetKeys(self, command, params):
|
||||
dsindex = params[0]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
return list(datastore.keys())
|
||||
dataStoreConnectorGetKeys.readonly = True
|
||||
|
||||
def dataStoreConnectorGetVarHistory(self, command, params):
|
||||
dsindex = params[0]
|
||||
name = params[1]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
return datastore.varhistory.variable(name)
|
||||
dataStoreConnectorGetVarHistory.readonly = True
|
||||
|
||||
def dataStoreConnectorExpandPythonRef(self, command, params):
|
||||
config_data_dict = params[0]
|
||||
varname = params[1]
|
||||
expr = params[2]
|
||||
|
||||
config_data = command.remotedatastores.receive_datastore(config_data_dict)
|
||||
|
||||
varparse = bb.data_smart.VariableParse(varname, config_data)
|
||||
return varparse.python_sub(expr)
|
||||
|
||||
def dataStoreConnectorRelease(self, command, params):
|
||||
dsindex = params[0]
|
||||
if dsindex <= 0:
|
||||
raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
|
||||
command.remotedatastores.release(dsindex)
|
||||
|
||||
def dataStoreConnectorSetVarFlag(self, command, params):
|
||||
dsindex = params[0]
|
||||
name = params[1]
|
||||
flag = params[2]
|
||||
value = params[3]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
datastore.setVarFlag(name, flag, value)
|
||||
|
||||
def dataStoreConnectorDelVar(self, command, params):
|
||||
dsindex = params[0]
|
||||
name = params[1]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
if len(params) > 2:
|
||||
flag = params[2]
|
||||
datastore.delVarFlag(name, flag)
|
||||
else:
|
||||
datastore.delVar(name)
|
||||
|
||||
def dataStoreConnectorRenameVar(self, command, params):
|
||||
dsindex = params[0]
|
||||
name = params[1]
|
||||
newname = params[2]
|
||||
datastore = command.remotedatastores[dsindex]
|
||||
datastore.renameVar(name, newname)
|
||||
|
||||
def parseRecipeFile(self, command, params):
|
||||
"""
|
||||
Parse the specified recipe file (with or without bbappends)
|
||||
and return a datastore object representing the environment
|
||||
for the recipe.
|
||||
"""
|
||||
fn = params[0]
|
||||
appends = params[1]
|
||||
appendlist = params[2]
|
||||
if len(params) > 3:
|
||||
config_data_dict = params[3]
|
||||
config_data = command.remotedatastores.receive_datastore(config_data_dict)
|
||||
else:
|
||||
config_data = None
|
||||
|
||||
if appends:
|
||||
if appendlist is not None:
|
||||
appendfiles = appendlist
|
||||
else:
|
||||
appendfiles = command.cooker.collection.get_file_appends(fn)
|
||||
else:
|
||||
appendfiles = []
|
||||
# We are calling bb.cache locally here rather than on the server,
|
||||
# but that's OK because it doesn't actually need anything from
|
||||
# the server barring the global datastore (which we have a remote
|
||||
# version of)
|
||||
if config_data:
|
||||
# We have to use a different function here if we're passing in a datastore
|
||||
# NOTE: we took a copy above, so we don't do it here again
|
||||
envdata = bb.cache.parse_recipe(config_data, fn, appendfiles)['']
|
||||
else:
|
||||
# Use the standard path
|
||||
parser = bb.cache.NoCache(command.cooker.databuilder)
|
||||
envdata = parser.loadDataFull(fn, appendfiles)
|
||||
idx = command.remotedatastores.store(envdata)
|
||||
return DataStoreConnectionHandle(idx)
|
||||
parseRecipeFile.readonly = True
|
||||
|
||||
class CommandsAsync:
|
||||
"""
|
||||
A class of asynchronous commands
|
||||
@@ -591,12 +311,8 @@ class CommandsAsync:
|
||||
"""
|
||||
bfile = params[0]
|
||||
task = params[1]
|
||||
if len(params) > 2:
|
||||
hidewarning = params[2]
|
||||
else:
|
||||
hidewarning = False
|
||||
|
||||
command.cooker.buildFile(bfile, task, hidewarning)
|
||||
command.cooker.buildFile(bfile, task)
|
||||
buildFile.needcache = False
|
||||
|
||||
def buildTargets(self, command, params):
|
||||
@@ -756,11 +472,3 @@ class CommandsAsync:
|
||||
command.finishAsyncCommand()
|
||||
resetCooker.needcache = False
|
||||
|
||||
def clientComplete(self, command, params):
|
||||
"""
|
||||
Do the right thing when the controlling client exits
|
||||
"""
|
||||
command.cooker.clientComplete()
|
||||
command.finishAsyncCommand()
|
||||
clientComplete.needcache = False
|
||||
|
||||
|
||||
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.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import os, sys
|
||||
from functools import wraps
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
import bb.parse
|
||||
@@ -79,7 +77,7 @@ class ConfigParameters(object):
|
||||
"prefile", "postfile"]:
|
||||
options[o] = getattr(self.options, o)
|
||||
|
||||
ret, error = server.runCommand(["updateConfig", options, environment, sys.argv])
|
||||
ret, error = server.runCommand(["updateConfig", options, environment])
|
||||
if error:
|
||||
raise Exception("Unable to update the server configuration with local parameters: %s" % error)
|
||||
|
||||
@@ -146,9 +144,6 @@ class CookerConfiguration(object):
|
||||
self.tracking = False
|
||||
self.interface = []
|
||||
self.writeeventlog = False
|
||||
self.server_only = False
|
||||
self.limited_deps = False
|
||||
self.runall = None
|
||||
|
||||
self.env = {}
|
||||
|
||||
@@ -197,8 +192,7 @@ def catch_parse_error(func):
|
||||
fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
|
||||
if not fn.startswith(bbdir):
|
||||
break
|
||||
parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb))
|
||||
sys.exit(1)
|
||||
parselog.critical("Unable to parse %s", fn, exc_info=(exc_class, exc, tb))
|
||||
except bb.parse.ParseError as exc:
|
||||
parselog.critical(str(exc))
|
||||
sys.exit(1)
|
||||
@@ -215,7 +209,7 @@ def _inherit(bbclass, data):
|
||||
|
||||
def findConfigFile(configfile, data):
|
||||
search = []
|
||||
bbpath = data.getVar("BBPATH")
|
||||
bbpath = data.getVar("BBPATH", True)
|
||||
if bbpath:
|
||||
for i in bbpath.split(":"):
|
||||
search.append(os.path.join(i, "conf", configfile))
|
||||
@@ -240,9 +234,9 @@ class CookerDataBuilder(object):
|
||||
|
||||
bb.utils.set_context(bb.utils.clean_context())
|
||||
bb.event.set_class_handlers(bb.event.clean_class_handlers())
|
||||
self.basedata = bb.data.init()
|
||||
self.data = bb.data.init()
|
||||
if self.tracking:
|
||||
self.basedata.enableTracking()
|
||||
self.data.enableTracking()
|
||||
|
||||
# Keep a datastore of the initial environment variables and their
|
||||
# values from when BitBake was launched to enable child processes
|
||||
@@ -253,49 +247,16 @@ class CookerDataBuilder(object):
|
||||
self.savedenv.setVar(k, cookercfg.env[k])
|
||||
|
||||
filtered_keys = bb.utils.approved_variables()
|
||||
bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys)
|
||||
self.basedata.setVar("BB_ORIGENV", self.savedenv)
|
||||
bb.data.inheritFromOS(self.data, self.savedenv, filtered_keys)
|
||||
self.data.setVar("BB_ORIGENV", self.savedenv)
|
||||
|
||||
if worker:
|
||||
self.basedata.setVar("BB_WORKERCONTEXT", "1")
|
||||
|
||||
self.data = self.basedata
|
||||
self.mcdata = {}
|
||||
self.data.setVar("BB_WORKERCONTEXT", "1")
|
||||
|
||||
def parseBaseConfiguration(self):
|
||||
try:
|
||||
bb.parse.init_parser(self.basedata)
|
||||
self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
|
||||
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") 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):
|
||||
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
except SyntaxError:
|
||||
raise bb.BBHandledException
|
||||
except bb.data_smart.ExpansionError as e:
|
||||
logger.error(str(e))
|
||||
@@ -307,9 +268,9 @@ class CookerDataBuilder(object):
|
||||
def _findLayerConf(self, data):
|
||||
return findConfigFile("bblayers.conf", data)
|
||||
|
||||
def parseConfigurationFiles(self, prefiles, postfiles, mc = "default"):
|
||||
data = bb.data.createCopy(self.basedata)
|
||||
data.setVar("BB_CURRENT_MC", mc)
|
||||
def parseConfigurationFiles(self, prefiles, postfiles):
|
||||
data = self.data
|
||||
bb.parse.init_parser(data)
|
||||
|
||||
# Parse files for loading *before* bitbake.conf and any includes
|
||||
for f in prefiles:
|
||||
@@ -323,30 +284,23 @@ class CookerDataBuilder(object):
|
||||
data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
|
||||
data = parse_config_file(layerconf, data)
|
||||
|
||||
layers = (data.getVar('BBLAYERS') or "").split()
|
||||
layers = (data.getVar('BBLAYERS', True) or "").split()
|
||||
|
||||
data = bb.data.createCopy(data)
|
||||
approved = bb.utils.approved_variables()
|
||||
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)
|
||||
if 'HOME' in approved and '~' in layer:
|
||||
layer = os.path.expanduser(layer)
|
||||
if layer.endswith('/'):
|
||||
layer = layer.rstrip('/')
|
||||
data.setVar('LAYERDIR', layer)
|
||||
data.setVar('LAYERDIR_RE', re.escape(layer))
|
||||
data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
|
||||
data.expandVarref('LAYERDIR')
|
||||
data.expandVarref('LAYERDIR_RE')
|
||||
|
||||
data.delVar('LAYERDIR_RE')
|
||||
data.delVar('LAYERDIR')
|
||||
|
||||
if not data.getVar("BBPATH"):
|
||||
if not data.getVar("BBPATH", True):
|
||||
msg = "The BBPATH variable is not set"
|
||||
if not layerconf:
|
||||
msg += (" and bitbake did not find a conf/bblayers.conf file in"
|
||||
@@ -361,7 +315,7 @@ class CookerDataBuilder(object):
|
||||
data = parse_config_file(p, data)
|
||||
|
||||
# Handle any INHERITs and inherit the base class
|
||||
bbclasses = ["base"] + (data.getVar('INHERIT') or "").split()
|
||||
bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
|
||||
for bbclass in bbclasses:
|
||||
data = _inherit(bbclass, data)
|
||||
|
||||
@@ -369,13 +323,23 @@ class CookerDataBuilder(object):
|
||||
# We register any handlers we've found so far here...
|
||||
for var in data.getVar('__BBHANDLERS', False) or []:
|
||||
handlerfn = data.getVarFlag(var, "filename", False)
|
||||
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") or "").split(), handlerfn, handlerln)
|
||||
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))
|
||||
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)
|
||||
|
||||
|
||||
si = open('/dev/null', 'r')
|
||||
so = open(logfile, 'w')
|
||||
si = file('/dev/null', 'r')
|
||||
so = file(logfile, 'w')
|
||||
se = so
|
||||
|
||||
|
||||
|
||||
@@ -78,6 +78,59 @@ def initVar(var, d):
|
||||
"""Non-destructive var init for data structure"""
|
||||
d.initVar(var)
|
||||
|
||||
|
||||
def setVar(var, value, d):
|
||||
"""Set a variable to a given value"""
|
||||
d.setVar(var, value)
|
||||
|
||||
|
||||
def getVar(var, d, exp = False):
|
||||
"""Gets the value of a variable"""
|
||||
return d.getVar(var, exp)
|
||||
|
||||
|
||||
def renameVar(key, newkey, d):
|
||||
"""Renames a variable from key to newkey"""
|
||||
d.renameVar(key, newkey)
|
||||
|
||||
def delVar(var, d):
|
||||
"""Removes a variable from the data set"""
|
||||
d.delVar(var)
|
||||
|
||||
def appendVar(var, value, d):
|
||||
"""Append additional value to a variable"""
|
||||
d.appendVar(var, value)
|
||||
|
||||
def setVarFlag(var, flag, flagvalue, d):
|
||||
"""Set a flag for a given variable to a given value"""
|
||||
d.setVarFlag(var, flag, flagvalue)
|
||||
|
||||
def getVarFlag(var, flag, d):
|
||||
"""Gets given flag from given var"""
|
||||
return d.getVarFlag(var, flag, False)
|
||||
|
||||
def delVarFlag(var, flag, d):
|
||||
"""Removes a given flag from the variable's flags"""
|
||||
d.delVarFlag(var, flag)
|
||||
|
||||
def setVarFlags(var, flags, d):
|
||||
"""Set the flags for a given variable
|
||||
|
||||
Note:
|
||||
setVarFlags will not clear previous
|
||||
flags. Think of this method as
|
||||
addVarFlags
|
||||
"""
|
||||
d.setVarFlags(var, flags)
|
||||
|
||||
def getVarFlags(var, d):
|
||||
"""Gets a variable's flags"""
|
||||
return d.getVarFlags(var)
|
||||
|
||||
def delVarFlags(var, d):
|
||||
"""Removes a variable's flags"""
|
||||
d.delVarFlags(var)
|
||||
|
||||
def keys(d):
|
||||
"""Return a list of keys in d"""
|
||||
return d.keys()
|
||||
@@ -121,7 +174,7 @@ def inheritFromOS(d, savedenv, permitted):
|
||||
for s in savedenv.keys():
|
||||
if s in permitted:
|
||||
try:
|
||||
d.setVar(s, savedenv.getVar(s), op = 'from env')
|
||||
d.setVar(s, savedenv.getVar(s, True), op = 'from env')
|
||||
if s in exportlist:
|
||||
d.setVarFlag(s, "export", True, op = 'auto env export')
|
||||
except TypeError:
|
||||
@@ -129,19 +182,19 @@ def inheritFromOS(d, savedenv, permitted):
|
||||
|
||||
def emit_var(var, o=sys.__stdout__, d = init(), all=False):
|
||||
"""Emit a variable to be sourced by a shell."""
|
||||
func = d.getVarFlag(var, "func", False)
|
||||
if d.getVarFlag(var, 'python', False) and func:
|
||||
if d.getVarFlag(var, "python", False):
|
||||
return False
|
||||
|
||||
export = d.getVarFlag(var, "export", False)
|
||||
unexport = d.getVarFlag(var, "unexport", False)
|
||||
func = d.getVarFlag(var, "func", False)
|
||||
if not all and not export and not unexport and not func:
|
||||
return False
|
||||
|
||||
try:
|
||||
if all:
|
||||
oval = d.getVar(var, False)
|
||||
val = d.getVar(var)
|
||||
val = d.getVar(var, True)
|
||||
except (KeyboardInterrupt, bb.build.FuncFailed):
|
||||
raise
|
||||
except Exception as exc:
|
||||
@@ -196,7 +249,7 @@ def emit_env(o=sys.__stdout__, d = init(), all=False):
|
||||
keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
|
||||
grouped = groupby(keys, isfunc)
|
||||
for isfunc, keys in grouped:
|
||||
for key in sorted(keys):
|
||||
for key in keys:
|
||||
emit_var(key, o, d, all and not isfunc) and o.write('\n')
|
||||
|
||||
def exported_keys(d):
|
||||
@@ -205,13 +258,11 @@ def exported_keys(d):
|
||||
not d.getVarFlag(key, 'unexport', False))
|
||||
|
||||
def exported_vars(d):
|
||||
k = list(exported_keys(d))
|
||||
for key in k:
|
||||
for key in exported_keys(d):
|
||||
try:
|
||||
value = d.getVar(key)
|
||||
except Exception as err:
|
||||
bb.warn("%s: Unable to export ${%s}: %s" % (d.getVar("FILE"), key, err))
|
||||
continue
|
||||
value = d.getVar(key, True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if value is not None:
|
||||
yield key, str(value)
|
||||
@@ -220,13 +271,13 @@ def emit_func(func, o=sys.__stdout__, d = init()):
|
||||
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
|
||||
|
||||
keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func", False))
|
||||
for key in sorted(keys):
|
||||
for key in keys:
|
||||
emit_var(key, o, d, False)
|
||||
|
||||
o.write('\n')
|
||||
emit_var(func, o, d, False) and o.write('\n')
|
||||
newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func))
|
||||
newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
|
||||
newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func, True))
|
||||
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
|
||||
seen = set()
|
||||
while newdeps:
|
||||
deps = newdeps
|
||||
@@ -235,8 +286,8 @@ def emit_func(func, o=sys.__stdout__, d = init()):
|
||||
for dep in deps:
|
||||
if d.getVarFlag(dep, "func", False) and not d.getVarFlag(dep, "python", False):
|
||||
emit_var(dep, o, d, False) and o.write('\n')
|
||||
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep))
|
||||
newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
|
||||
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
|
||||
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
||||
newdeps -= seen
|
||||
|
||||
_functionfmt = """
|
||||
@@ -259,7 +310,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
|
||||
pp = bb.codeparser.PythonParser(func, logger)
|
||||
pp.parse_python(d.getVar(func, False))
|
||||
newdeps = pp.execs
|
||||
newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
|
||||
newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
|
||||
seen = set()
|
||||
while newdeps:
|
||||
deps = newdeps
|
||||
@@ -271,7 +322,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
|
||||
pp = bb.codeparser.PythonParser(dep, logger)
|
||||
pp.parse_python(d.getVar(dep, False))
|
||||
newdeps |= pp.execs
|
||||
newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
|
||||
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
||||
newdeps -= seen
|
||||
|
||||
def update_data(d):
|
||||
@@ -288,21 +339,19 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
deps |= parser.references
|
||||
deps = deps | (keys & parser.execs)
|
||||
return deps, value
|
||||
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "exports", "postfuncs", "prefuncs", "lineno", "filename"]) or {}
|
||||
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "vardepvalueexclude", "postfuncs", "prefuncs", "lineno", "filename"]) or {}
|
||||
vardeps = varflags.get("vardeps")
|
||||
value = d.getVar(key, False)
|
||||
|
||||
def handle_contains(value, contains, d):
|
||||
newvalue = ""
|
||||
for k in sorted(contains):
|
||||
l = (d.getVar(k) or "").split()
|
||||
for item in sorted(contains[k]):
|
||||
for word in item.split():
|
||||
if not word in l:
|
||||
newvalue += "\n%s{%s} = Unset" % (k, item)
|
||||
break
|
||||
l = (d.getVar(k, True) or "").split()
|
||||
for word in sorted(contains[k]):
|
||||
if word in l:
|
||||
newvalue += "\n%s{%s} = Set" % (k, word)
|
||||
else:
|
||||
newvalue += "\n%s{%s} = Set" % (k, item)
|
||||
newvalue += "\n%s{%s} = Unset" % (k, word)
|
||||
if not newvalue:
|
||||
return value
|
||||
if not value:
|
||||
@@ -315,7 +364,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
if varflags.get("python"):
|
||||
parser = bb.codeparser.PythonParser(key, logger)
|
||||
if value and "\t" in value:
|
||||
logger.warning("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE")))
|
||||
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"))
|
||||
deps = deps | parser.references
|
||||
deps = deps | (keys & parser.execs)
|
||||
@@ -334,8 +383,6 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
deps = deps | set(varflags["prefuncs"].split())
|
||||
if "postfuncs" in varflags:
|
||||
deps = deps | set(varflags["postfuncs"].split())
|
||||
if "exports" in varflags:
|
||||
deps = deps | set(varflags["exports"].split())
|
||||
else:
|
||||
parser = d.expandWithRefs(value, key)
|
||||
deps |= parser.references
|
||||
@@ -359,8 +406,6 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
|
||||
deps |= set((vardeps or "").split())
|
||||
deps -= set(varflags.get("vardepsexclude", "").split())
|
||||
except bb.parse.SkipRecipe:
|
||||
raise
|
||||
except Exception as e:
|
||||
bb.warn("Exception during build_dependencies for %s" % key)
|
||||
raise
|
||||
@@ -372,7 +417,7 @@ def generate_dependencies(d):
|
||||
|
||||
keys = set(key for key in d if not key.startswith("__"))
|
||||
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export", False) and not d.getVarFlag(key, "unexport", False))
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS')
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||
|
||||
deps = {}
|
||||
values = {}
|
||||
|
||||
@@ -108,7 +108,7 @@ class VariableParse:
|
||||
varparse = self.d.expand_cache[key]
|
||||
var = varparse.value
|
||||
else:
|
||||
var = self.d.getVarFlag(key, "_content")
|
||||
var = self.d.getVarFlag(key, "_content", True)
|
||||
self.references.add(key)
|
||||
if var is not None:
|
||||
return var
|
||||
@@ -116,21 +116,13 @@ class VariableParse:
|
||||
return match.group()
|
||||
|
||||
def python_sub(self, match):
|
||||
if isinstance(match, str):
|
||||
code = match
|
||||
else:
|
||||
code = match.group()[3:-1]
|
||||
|
||||
if "_remote_data" in self.d:
|
||||
connector = self.d["_remote_data"]
|
||||
return connector.expandPythonRef(self.varname, code, self.d)
|
||||
|
||||
code = match.group()[3:-1]
|
||||
codeobj = compile(code.strip(), self.varname or "<expansion>", "eval")
|
||||
|
||||
parser = bb.codeparser.PythonParser(self.varname, logger)
|
||||
parser.parse_python(code)
|
||||
if self.varname:
|
||||
vardeps = self.d.getVarFlag(self.varname, "vardeps")
|
||||
vardeps = self.d.getVarFlag(self.varname, "vardeps", True)
|
||||
if vardeps is None:
|
||||
parser.log.flush()
|
||||
else:
|
||||
@@ -143,7 +135,7 @@ class VariableParse:
|
||||
self.contains[k] = parser.contains[k].copy()
|
||||
else:
|
||||
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)
|
||||
|
||||
|
||||
@@ -154,7 +146,7 @@ class DataContext(dict):
|
||||
self['d'] = metadata
|
||||
|
||||
def __missing__(self, key):
|
||||
value = self.metadata.getVar(key)
|
||||
value = self.metadata.getVar(key, True)
|
||||
if value is None or self.metadata.getVarFlag(key, 'func', False):
|
||||
raise KeyError(key)
|
||||
else:
|
||||
@@ -230,19 +222,6 @@ class VariableHistory(object):
|
||||
new.variables = self.variables.copy()
|
||||
return new
|
||||
|
||||
def __getstate__(self):
|
||||
vardict = {}
|
||||
for k, v in self.variables.iteritems():
|
||||
vardict[k] = v
|
||||
return {'dataroot': self.dataroot,
|
||||
'variables': vardict}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.dataroot = state['dataroot']
|
||||
self.variables = COWDictBase.copy()
|
||||
for k, v in state['variables'].items():
|
||||
self.variables[k] = v
|
||||
|
||||
def record(self, *kwonly, **loginfo):
|
||||
if not self.dataroot._tracking:
|
||||
return
|
||||
@@ -268,15 +247,10 @@ class VariableHistory(object):
|
||||
self.variables[var].append(loginfo.copy())
|
||||
|
||||
def variable(self, var):
|
||||
remote_connector = self.dataroot.getVar('_remote_data', False)
|
||||
if remote_connector:
|
||||
varhistory = remote_connector.getVarHistory(var)
|
||||
else:
|
||||
varhistory = []
|
||||
|
||||
if var in self.variables:
|
||||
varhistory.extend(self.variables[var])
|
||||
return varhistory
|
||||
return self.variables[var]
|
||||
else:
|
||||
return []
|
||||
|
||||
def emit(self, var, oval, val, o, d):
|
||||
history = self.variable(var)
|
||||
@@ -344,7 +318,7 @@ class VariableHistory(object):
|
||||
the files in which they were added.
|
||||
"""
|
||||
history = self.variable(var)
|
||||
finalitems = (d.getVar(var) or '').split()
|
||||
finalitems = (d.getVar(var, True) or '').split()
|
||||
filemap = {}
|
||||
isset = False
|
||||
for event in history:
|
||||
@@ -398,7 +372,7 @@ class DataSmart(MutableMapping):
|
||||
|
||||
def expandWithRefs(self, s, varname):
|
||||
|
||||
if not isinstance(s, str): # sanity check
|
||||
if not isinstance(s, basestring): # sanity check
|
||||
return VariableParse(varname, self, s)
|
||||
|
||||
if varname and varname in self.expand_cache:
|
||||
@@ -423,7 +397,8 @@ class DataSmart(MutableMapping):
|
||||
except bb.parse.SkipRecipe:
|
||||
raise
|
||||
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
|
||||
|
||||
@@ -452,11 +427,11 @@ class DataSmart(MutableMapping):
|
||||
# Can end up here recursively so setup dummy values
|
||||
self.overrides = []
|
||||
self.overridesset = set()
|
||||
self.overrides = (self.getVar("OVERRIDES") or "").split(":") or []
|
||||
self.overrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
|
||||
self.overridesset = set(self.overrides)
|
||||
self.inoverride = False
|
||||
self.expand_cache = {}
|
||||
newoverrides = (self.getVar("OVERRIDES") or "").split(":") or []
|
||||
newoverrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
|
||||
if newoverrides == self.overrides:
|
||||
break
|
||||
self.overrides = newoverrides
|
||||
@@ -473,22 +448,17 @@ class DataSmart(MutableMapping):
|
||||
dest = self.dict
|
||||
while dest:
|
||||
if var in dest:
|
||||
return dest[var], self.overridedata.get(var, None)
|
||||
|
||||
if "_remote_data" in dest:
|
||||
connector = dest["_remote_data"]["_content"]
|
||||
return connector.getVar(var)
|
||||
return dest[var]
|
||||
|
||||
if "_data" not in dest:
|
||||
break
|
||||
dest = dest["_data"]
|
||||
return None, self.overridedata.get(var, None)
|
||||
|
||||
def _makeShadowCopy(self, var):
|
||||
if var in self.dict:
|
||||
return
|
||||
|
||||
local_var, _ = self._findVar(var)
|
||||
local_var = self._findVar(var)
|
||||
|
||||
if local_var:
|
||||
self.dict[var] = copy.copy(local_var)
|
||||
@@ -502,12 +472,6 @@ class DataSmart(MutableMapping):
|
||||
if 'parsing' in loginfo:
|
||||
parsing=True
|
||||
|
||||
if '_remote_data' in self.dict:
|
||||
connector = self.dict["_remote_data"]["_content"]
|
||||
res = connector.setVar(var, value)
|
||||
if not res:
|
||||
return
|
||||
|
||||
if 'op' not in loginfo:
|
||||
loginfo['op'] = "set"
|
||||
self.expand_cache = {}
|
||||
@@ -546,8 +510,6 @@ class DataSmart(MutableMapping):
|
||||
del self.dict[var]["_append"]
|
||||
if "_prepend" in self.dict[var]:
|
||||
del self.dict[var]["_prepend"]
|
||||
if "_remove" in self.dict[var]:
|
||||
del self.dict[var]["_remove"]
|
||||
if var in self.overridedata:
|
||||
active = []
|
||||
self.need_overrides()
|
||||
@@ -580,7 +542,7 @@ class DataSmart(MutableMapping):
|
||||
nextnew = set()
|
||||
self.overridevars.update(new)
|
||||
for i in new:
|
||||
vardata = self.expandWithRefs(self.getVar(i), i)
|
||||
vardata = self.expandWithRefs(self.getVar(i, True), i)
|
||||
nextnew.update(vardata.references)
|
||||
nextnew.update(vardata.contains.keys())
|
||||
new = nextnew
|
||||
@@ -604,19 +566,13 @@ class DataSmart(MutableMapping):
|
||||
if len(shortvar) == 0:
|
||||
override = None
|
||||
|
||||
def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
|
||||
def getVar(self, var, expand, noweakdefault=False, parsing=False):
|
||||
return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
|
||||
|
||||
def renameVar(self, key, newkey, **loginfo):
|
||||
"""
|
||||
Rename the variable key to newkey
|
||||
"""
|
||||
if '_remote_data' in self.dict:
|
||||
connector = self.dict["_remote_data"]["_content"]
|
||||
res = connector.renameVar(key, newkey)
|
||||
if not res:
|
||||
return
|
||||
|
||||
val = self.getVar(key, 0, parsing=True)
|
||||
if val is not None:
|
||||
loginfo['variable'] = newkey
|
||||
@@ -660,12 +616,6 @@ class DataSmart(MutableMapping):
|
||||
self.setVar(var + "_prepend", value, ignore=True, parsing=True)
|
||||
|
||||
def delVar(self, var, **loginfo):
|
||||
if '_remote_data' in self.dict:
|
||||
connector = self.dict["_remote_data"]["_content"]
|
||||
res = connector.delVar(var)
|
||||
if not res:
|
||||
return
|
||||
|
||||
loginfo['detail'] = ""
|
||||
loginfo['op'] = 'del'
|
||||
self.varhistory.record(**loginfo)
|
||||
@@ -692,12 +642,6 @@ class DataSmart(MutableMapping):
|
||||
override = None
|
||||
|
||||
def setVarFlag(self, var, flag, value, **loginfo):
|
||||
if '_remote_data' in self.dict:
|
||||
connector = self.dict["_remote_data"]["_content"]
|
||||
res = connector.setVarFlag(var, flag, value)
|
||||
if not res:
|
||||
return
|
||||
|
||||
self.expand_cache = {}
|
||||
if 'op' not in loginfo:
|
||||
loginfo['op'] = "set"
|
||||
@@ -719,14 +663,14 @@ class DataSmart(MutableMapping):
|
||||
self.dict["__exportlist"]["_content"] = set()
|
||||
self.dict["__exportlist"]["_content"].add(var)
|
||||
|
||||
def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False):
|
||||
local_var, overridedata = self._findVar(var)
|
||||
def getVarFlag(self, var, flag, expand, noweakdefault=False, parsing=False):
|
||||
local_var = self._findVar(var)
|
||||
value = None
|
||||
if flag == "_content" and overridedata is not None and not parsing:
|
||||
if flag == "_content" and var in self.overridedata and not parsing:
|
||||
match = False
|
||||
active = {}
|
||||
self.need_overrides()
|
||||
for (r, o) in overridedata:
|
||||
for (r, o) in self.overridedata[var]:
|
||||
# What about double overrides both with "_" in the name?
|
||||
if o in self.overridesset:
|
||||
active[o] = r
|
||||
@@ -805,25 +749,18 @@ class DataSmart(MutableMapping):
|
||||
if match:
|
||||
removes.extend(self.expand(r).split())
|
||||
|
||||
if removes:
|
||||
filtered = filter(lambda v: v not in removes,
|
||||
value.split())
|
||||
value = " ".join(filtered)
|
||||
if expand and var in self.expand_cache:
|
||||
# We need to ensure the expand cache has the correct value
|
||||
# flag == "_content" here
|
||||
self.expand_cache[var].value = value
|
||||
filtered = filter(lambda v: v not in removes,
|
||||
value.split())
|
||||
value = " ".join(filtered)
|
||||
if expand and var in self.expand_cache:
|
||||
# We need to ensure the expand cache has the correct value
|
||||
# flag == "_content" here
|
||||
self.expand_cache[var].value = value
|
||||
return value
|
||||
|
||||
def delVarFlag(self, var, flag, **loginfo):
|
||||
if '_remote_data' in self.dict:
|
||||
connector = self.dict["_remote_data"]["_content"]
|
||||
res = connector.delVarFlag(var, flag)
|
||||
if not res:
|
||||
return
|
||||
|
||||
self.expand_cache = {}
|
||||
local_var, _ = self._findVar(var)
|
||||
local_var = self._findVar(var)
|
||||
if not local_var:
|
||||
return
|
||||
if not var in self.dict:
|
||||
@@ -866,7 +803,7 @@ class DataSmart(MutableMapping):
|
||||
self.dict[var][i] = flags[i]
|
||||
|
||||
def getVarFlags(self, var, expand = False, internalflags=False):
|
||||
local_var, _ = self._findVar(var)
|
||||
local_var = self._findVar(var)
|
||||
flags = {}
|
||||
|
||||
if local_var:
|
||||
@@ -908,7 +845,7 @@ class DataSmart(MutableMapping):
|
||||
data = DataSmart()
|
||||
data.dict["_data"] = self.dict
|
||||
data.varhistory = self.varhistory.copy()
|
||||
data.varhistory.dataroot = data
|
||||
data.varhistory.datasmart = data
|
||||
data.inchistory = self.inchistory.copy()
|
||||
|
||||
data._tracking = self._tracking
|
||||
@@ -939,7 +876,7 @@ class DataSmart(MutableMapping):
|
||||
|
||||
def localkeys(self):
|
||||
for key in self.dict:
|
||||
if key not in ['_data', '_remote_data']:
|
||||
if key != '_data':
|
||||
yield key
|
||||
|
||||
def __iter__(self):
|
||||
@@ -948,7 +885,7 @@ class DataSmart(MutableMapping):
|
||||
def keylist(d):
|
||||
klist = set()
|
||||
for key in d:
|
||||
if key in ["_data", "_remote_data"]:
|
||||
if key == "_data":
|
||||
continue
|
||||
if key in deleted:
|
||||
continue
|
||||
@@ -962,13 +899,6 @@ class DataSmart(MutableMapping):
|
||||
if "_data" in d:
|
||||
klist |= keylist(d["_data"])
|
||||
|
||||
if "_remote_data" in d:
|
||||
connector = d["_remote_data"]["_content"]
|
||||
for key in connector.getKeys():
|
||||
if key in deleted:
|
||||
continue
|
||||
klist.add(key)
|
||||
|
||||
return klist
|
||||
|
||||
self.need_overrides()
|
||||
@@ -987,7 +917,7 @@ class DataSmart(MutableMapping):
|
||||
yield k
|
||||
|
||||
def __len__(self):
|
||||
return len(frozenset(iter(self)))
|
||||
return len(frozenset(self))
|
||||
|
||||
def __getitem__(self, item):
|
||||
value = self.getVar(item, False)
|
||||
@@ -1006,8 +936,9 @@ class DataSmart(MutableMapping):
|
||||
data = {}
|
||||
d = self.createCopy()
|
||||
bb.data.expandKeys(d)
|
||||
bb.data.update_data(d)
|
||||
|
||||
config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split())
|
||||
config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST", True) or "").split())
|
||||
keys = set(key for key in iter(d) if not key.startswith("__"))
|
||||
for key in keys:
|
||||
if key in config_whitelist:
|
||||
@@ -1026,6 +957,7 @@ class DataSmart(MutableMapping):
|
||||
|
||||
for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
|
||||
bb_list = d.getVar(key, False) or []
|
||||
bb_list.sort()
|
||||
data.update({key:str(bb_list)})
|
||||
|
||||
if key == "__BBANONFUNCS":
|
||||
@@ -1034,4 +966,4 @@ class DataSmart(MutableMapping):
|
||||
data.update({i:value})
|
||||
|
||||
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,14 @@ BitBake build tools.
|
||||
|
||||
import os, sys
|
||||
import warnings
|
||||
import pickle
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
import logging
|
||||
import atexit
|
||||
import traceback
|
||||
import ast
|
||||
import threading
|
||||
|
||||
import bb.utils
|
||||
import bb.compat
|
||||
import bb.exceptions
|
||||
@@ -48,16 +49,6 @@ class Event(object):
|
||||
def __init__(self):
|
||||
self.pid = worker_pid
|
||||
|
||||
|
||||
class HeartbeatEvent(Event):
|
||||
"""Triggered at regular time intervals of 10 seconds. Other events can fire much more often
|
||||
(runQueueTaskStarted when there are many short tasks) or not at all for long periods
|
||||
of time (again runQueueTaskStarted, when there is just one long-running task), so this
|
||||
event is more suitable for doing some task-independent work occassionally."""
|
||||
def __init__(self, time):
|
||||
Event.__init__(self)
|
||||
self.time = time
|
||||
|
||||
Registered = 10
|
||||
AlreadyRegistered = 14
|
||||
|
||||
@@ -80,27 +71,12 @@ _event_handler_map = {}
|
||||
_catchall_handlers = {}
|
||||
_eventfilter = None
|
||||
_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):
|
||||
event.data = d
|
||||
addedd = False
|
||||
if 'd' not in builtins:
|
||||
builtins['d'] = d
|
||||
if 'd' not in __builtins__:
|
||||
__builtins__['d'] = d
|
||||
addedd = True
|
||||
try:
|
||||
ret = handler(event)
|
||||
@@ -118,7 +94,7 @@ def execute_handler(name, handler, event, d):
|
||||
finally:
|
||||
del event.data
|
||||
if addedd:
|
||||
del builtins['d']
|
||||
del __builtins__['d']
|
||||
|
||||
def fire_class_handlers(event, d):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
@@ -126,7 +102,7 @@ def fire_class_handlers(event, d):
|
||||
|
||||
eid = str(event.__class__)[8:-2]
|
||||
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 _eventfilter:
|
||||
if not _eventfilter(name, handler, event, d):
|
||||
@@ -168,17 +144,11 @@ def print_ui_queue():
|
||||
logger.handle(event)
|
||||
|
||||
def fire_ui_handlers(event, d):
|
||||
global _thread_lock
|
||||
global _thread_lock_enabled
|
||||
|
||||
if not _uiready:
|
||||
# No UI handlers registered yet, queue up the messages
|
||||
ui_queue.append(event)
|
||||
return
|
||||
|
||||
if _thread_lock_enabled:
|
||||
_thread_lock.acquire()
|
||||
|
||||
errors = []
|
||||
for h in _ui_handlers:
|
||||
#print "Sending event %s" % event
|
||||
@@ -197,9 +167,6 @@ def fire_ui_handlers(event, d):
|
||||
for h in errors:
|
||||
del _ui_handlers[h]
|
||||
|
||||
if _thread_lock_enabled:
|
||||
_thread_lock.release()
|
||||
|
||||
def fire(event, d):
|
||||
"""Fire off an Event"""
|
||||
|
||||
@@ -227,7 +194,7 @@ def register(name, handler, mask=None, filename=None, lineno=None):
|
||||
|
||||
if handler is not None:
|
||||
# handle string containing python code
|
||||
if isinstance(handler, str):
|
||||
if isinstance(handler, basestring):
|
||||
tmp = "def %s(e):\n%s" % (name, handler)
|
||||
try:
|
||||
code = bb.methodpool.compile_cache(tmp)
|
||||
@@ -265,13 +232,6 @@ def remove(name, handler):
|
||||
"""Remove an Event handler"""
|
||||
_handlers.pop(name)
|
||||
|
||||
def get_handlers():
|
||||
return _handlers
|
||||
|
||||
def set_handlers(handlers):
|
||||
global _handlers
|
||||
_handlers = handlers
|
||||
|
||||
def set_eventfilter(func):
|
||||
global _eventfilter
|
||||
_eventfilter = func
|
||||
@@ -361,17 +321,6 @@ class RecipeEvent(Event):
|
||||
class RecipePreFinalise(RecipeEvent):
|
||||
""" Recipe Parsing Complete but not yet finialised"""
|
||||
|
||||
class RecipeTaskPreProcess(RecipeEvent):
|
||||
"""
|
||||
Recipe Tasks about to be finalised
|
||||
The list of tasks should be final at this point and handlers
|
||||
are only able to change interdependencies
|
||||
"""
|
||||
def __init__(self, fn, tasklist):
|
||||
self.fn = fn
|
||||
self.tasklist = tasklist
|
||||
Event.__init__(self)
|
||||
|
||||
class RecipeParsed(RecipeEvent):
|
||||
""" Recipe Parsing Complete """
|
||||
|
||||
@@ -393,7 +342,7 @@ class StampUpdate(Event):
|
||||
targets = property(getTargets)
|
||||
|
||||
class BuildBase(Event):
|
||||
"""Base class for bitbake build events"""
|
||||
"""Base class for bbmake run events"""
|
||||
|
||||
def __init__(self, n, p, failures = 0):
|
||||
self._name = n
|
||||
@@ -431,20 +380,16 @@ 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):
|
||||
"""Event when builds start"""
|
||||
"""bbmake build run started"""
|
||||
def __init__(self, n, p, failures = 0):
|
||||
OperationStarted.__init__(self, "Building Started")
|
||||
BuildBase.__init__(self, n, p, failures)
|
||||
|
||||
class BuildCompleted(BuildBase, OperationCompleted):
|
||||
"""Event when builds have completed"""
|
||||
"""bbmake build run completed"""
|
||||
def __init__(self, total, n, p, failures=0, interrupted=0):
|
||||
if not failures:
|
||||
OperationCompleted.__init__(self, total, "Building Succeeded")
|
||||
@@ -462,23 +407,6 @@ class DiskFull(Event):
|
||||
self._free = freespace
|
||||
self._mountpoint = mountpoint
|
||||
|
||||
class DiskUsageSample:
|
||||
def __init__(self, available_bytes, free_bytes, total_bytes):
|
||||
# Number of bytes available to non-root processes.
|
||||
self.available_bytes = available_bytes
|
||||
# Number of bytes available to root processes.
|
||||
self.free_bytes = free_bytes
|
||||
# Total capacity of the volume.
|
||||
self.total_bytes = total_bytes
|
||||
|
||||
class MonitorDiskEvent(Event):
|
||||
"""If BB_DISKMON_DIRS is set, then this event gets triggered each time disk space is checked.
|
||||
Provides information about devices that are getting monitored."""
|
||||
def __init__(self, disk_usage):
|
||||
Event.__init__(self)
|
||||
# hash of device root path -> DiskUsageSample
|
||||
self.disk_usage = disk_usage
|
||||
|
||||
class NoProvider(Event):
|
||||
"""No Provider for an Event"""
|
||||
|
||||
@@ -684,9 +612,8 @@ class LogHandler(logging.Handler):
|
||||
if hasattr(tb, 'tb_next'):
|
||||
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_formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
|
||||
value = str(value)
|
||||
record.bb_exc_info = (etype, value, tb)
|
||||
record.exc_info = None
|
||||
fire(record, None)
|
||||
|
||||
@@ -717,33 +644,6 @@ class MetadataEvent(Event):
|
||||
self.type = eventtype
|
||||
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):
|
||||
"""
|
||||
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 traceback
|
||||
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):
|
||||
if isinstance(exc, SystemExit):
|
||||
if not isinstance(exc.code, str):
|
||||
if not isinstance(exc.code, basestring):
|
||||
return 'Exited with "%d"' % exc.code
|
||||
return str(exc)
|
||||
|
||||
@@ -25,26 +25,31 @@ BitBake build tools.
|
||||
#
|
||||
# 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 signal
|
||||
import logging
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
if 'git' not in urllib.parse.uses_netloc:
|
||||
urllib.parse.uses_netloc.append('git')
|
||||
import operator
|
||||
import collections
|
||||
import subprocess
|
||||
import pickle
|
||||
import errno
|
||||
import urllib
|
||||
import urlparse
|
||||
import bb.persist_data, bb.utils
|
||||
import bb.checksum
|
||||
from bb import data
|
||||
import bb.process
|
||||
import subprocess
|
||||
|
||||
__version__ = "2"
|
||||
_checksum_cache = bb.checksum.FileChecksumCache()
|
||||
|
||||
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 all fetch exceptions inherit from"""
|
||||
def __init__(self, message):
|
||||
@@ -226,14 +231,14 @@ class URI(object):
|
||||
# them are not quite RFC compliant.
|
||||
uri, param_str = (uri.split(";", 1) + [None])[:2]
|
||||
|
||||
urlp = urllib.parse.urlparse(uri)
|
||||
urlp = urlparse.urlparse(uri)
|
||||
self.scheme = urlp.scheme
|
||||
|
||||
reparse = 0
|
||||
|
||||
# Coerce urlparse to make URI scheme use netloc
|
||||
if not self.scheme in urllib.parse.uses_netloc:
|
||||
urllib.parse.uses_params.append(self.scheme)
|
||||
if not self.scheme in urlparse.uses_netloc:
|
||||
urlparse.uses_params.append(self.scheme)
|
||||
reparse = 1
|
||||
|
||||
# Make urlparse happy(/ier) by converting local resources
|
||||
@@ -244,7 +249,7 @@ class URI(object):
|
||||
reparse = 1
|
||||
|
||||
if reparse:
|
||||
urlp = urllib.parse.urlparse(uri)
|
||||
urlp = urlparse.urlparse(uri)
|
||||
|
||||
# Identify if the URI is relative or not
|
||||
if urlp.scheme in self._relative_schemes and \
|
||||
@@ -260,7 +265,7 @@ class URI(object):
|
||||
if urlp.password:
|
||||
self.userinfo += ':%s' % urlp.password
|
||||
|
||||
self.path = urllib.parse.unquote(urlp.path)
|
||||
self.path = urllib.unquote(urlp.path)
|
||||
|
||||
if param_str:
|
||||
self.params = self._param_str_split(param_str, ";")
|
||||
@@ -292,7 +297,7 @@ class URI(object):
|
||||
if self.query else '')
|
||||
|
||||
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)]:
|
||||
ret[k] = v
|
||||
return ret
|
||||
@@ -308,11 +313,11 @@ class URI(object):
|
||||
|
||||
@property
|
||||
def path_quoted(self):
|
||||
return urllib.parse.quote(self.path)
|
||||
return urllib.quote(self.path)
|
||||
|
||||
@path_quoted.setter
|
||||
def path_quoted(self, path):
|
||||
self.path = urllib.parse.unquote(path)
|
||||
self.path = urllib.unquote(path)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
@@ -355,7 +360,7 @@ def decodeurl(url):
|
||||
user, password, parameters).
|
||||
"""
|
||||
|
||||
m = re.compile('(?P<type>[^:]*)://((?P<user>[^/;]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
|
||||
m = re.compile('(?P<type>[^:]*)://((?P<user>[^/]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
|
||||
if not m:
|
||||
raise MalformedUrl(url)
|
||||
|
||||
@@ -385,7 +390,7 @@ def decodeurl(url):
|
||||
user = ''
|
||||
pswd = ''
|
||||
|
||||
p = collections.OrderedDict()
|
||||
p = {}
|
||||
if parm:
|
||||
for s in parm.split(';'):
|
||||
if s:
|
||||
@@ -394,7 +399,7 @@ def decodeurl(url):
|
||||
s1, s2 = s.split('=')
|
||||
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):
|
||||
"""Encodes a URL from tokens (scheme, network location, path,
|
||||
@@ -418,7 +423,7 @@ def encodeurl(decoded):
|
||||
# Standardise path to ensure comparisons work
|
||||
while '//' in path:
|
||||
path = path.replace("//", "/")
|
||||
url += "%s" % urllib.parse.quote(path)
|
||||
url += "%s" % urllib.quote(path)
|
||||
if p:
|
||||
for parm in p:
|
||||
url += ";%s=%s" % (parm, p[parm])
|
||||
@@ -491,7 +496,7 @@ def fetcher_init(d):
|
||||
Calls before this must not hit the cache.
|
||||
"""
|
||||
# When to drop SCM head revisions controlled by user policy
|
||||
srcrev_policy = d.getVar('BB_SRCREV_POLICY') or "clear"
|
||||
srcrev_policy = d.getVar('BB_SRCREV_POLICY', True) or "clear"
|
||||
if srcrev_policy == "cache":
|
||||
logger.debug(1, "Keeping SRCREV cache due to cache policy of: %s", srcrev_policy)
|
||||
elif srcrev_policy == "clear":
|
||||
@@ -537,11 +542,7 @@ def fetcher_compare_revisions():
|
||||
return False
|
||||
|
||||
def mirror_from_string(data):
|
||||
mirrors = (data or "").replace('\\n',' ').split()
|
||||
# Split into pairs
|
||||
if len(mirrors) % 2 != 0:
|
||||
bb.warn('Invalid mirror data %s, should have paired members.' % data)
|
||||
return list(zip(*[iter(mirrors)]*2))
|
||||
return [ i.split() for i in (data or "").replace('\\n','\n').split('\n') if i ]
|
||||
|
||||
def verify_checksum(ud, d, precomputed={}):
|
||||
"""
|
||||
@@ -576,7 +577,7 @@ def verify_checksum(ud, d, precomputed={}):
|
||||
|
||||
if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
|
||||
# If strict checking enabled and neither sum defined, raise error
|
||||
strict = d.getVar("BB_STRICT_CHECKSUM") or "0"
|
||||
strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
|
||||
if strict == "1":
|
||||
logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
|
||||
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
|
||||
@@ -585,12 +586,12 @@ def verify_checksum(ud, d, precomputed={}):
|
||||
raise NoChecksumError('Missing SRC_URI checksum', ud.url)
|
||||
|
||||
# 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'
|
||||
'SRC_URI[%s] = "%s"',
|
||||
ud.localpath, ud.md5_name, md5data)
|
||||
logger.warning('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
|
||||
'SRC_URI[%s] = "%s"',
|
||||
ud.localpath, ud.sha256_name, sha256data)
|
||||
logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
|
||||
'SRC_URI[%s] = "%s"',
|
||||
ud.localpath, ud.md5_name, md5data)
|
||||
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)
|
||||
|
||||
# We want to alert the user if a checksum is defined in the recipe but
|
||||
# it does not match.
|
||||
@@ -625,7 +626,7 @@ def verify_donestamp(ud, d, origud=None):
|
||||
Returns True, if the donestamp exists and is valid, False otherwise. When
|
||||
returning False, any existing done stamps are removed.
|
||||
"""
|
||||
if not ud.needdonestamp or (origud and not origud.needdonestamp):
|
||||
if not ud.needdonestamp:
|
||||
return True
|
||||
|
||||
if not os.path.exists(ud.donestamp):
|
||||
@@ -658,9 +659,9 @@ def verify_donestamp(ud, d, origud=None):
|
||||
# files to those containing the checksums.
|
||||
if not isinstance(e, EOFError):
|
||||
# Ignore errors, they aren't fatal
|
||||
logger.warning("Couldn't load checksums from donestamp %s: %s "
|
||||
"(msg: %s)" % (ud.donestamp, type(e).__name__,
|
||||
str(e)))
|
||||
logger.warn("Couldn't load checksums from donestamp %s: %s "
|
||||
"(msg: %s)" % (ud.donestamp, type(e).__name__,
|
||||
str(e)))
|
||||
|
||||
try:
|
||||
checksums = verify_checksum(ud, d, precomputed_checksums)
|
||||
@@ -668,14 +669,14 @@ def verify_donestamp(ud, d, origud=None):
|
||||
# as an upgrade path from the previous done stamp file format.
|
||||
if checksums != precomputed_checksums:
|
||||
with open(ud.donestamp, "wb") as cachefile:
|
||||
p = pickle.Pickler(cachefile, 2)
|
||||
p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
|
||||
p.dump(checksums)
|
||||
return True
|
||||
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)
|
||||
logger.warn("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)
|
||||
@@ -702,13 +703,13 @@ def update_stamp(ud, d):
|
||||
checksums = verify_checksum(ud, d)
|
||||
# Store the checksums for later re-verification against the recipe
|
||||
with open(ud.donestamp, "wb") as cachefile:
|
||||
p = pickle.Pickler(cachefile, 2)
|
||||
p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
|
||||
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)
|
||||
logger.warn("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)
|
||||
@@ -722,13 +723,13 @@ def subprocess_setup():
|
||||
|
||||
def get_autorev(d):
|
||||
# only not cache src rev in autorev case
|
||||
if d.getVar('BB_SRCREV_POLICY') != "cache":
|
||||
if d.getVar('BB_SRCREV_POLICY', True) != "cache":
|
||||
d.setVar('BB_DONT_CACHE', '1')
|
||||
return "AUTOINC"
|
||||
|
||||
def get_srcrev(d, method_name='sortable_revision'):
|
||||
"""
|
||||
Return the revision string, usually for use in the version string (PV) of the current package
|
||||
Return the revsion string, usually for use in the version string (PV) of the current package
|
||||
Most packages usually only have one SCM so we just pass on the call.
|
||||
In the multi SCM case, we build a value based on SRCREV_FORMAT which must
|
||||
have been set.
|
||||
@@ -741,7 +742,7 @@ def get_srcrev(d, method_name='sortable_revision'):
|
||||
"""
|
||||
|
||||
scms = []
|
||||
fetcher = Fetch(d.getVar('SRC_URI').split(), d)
|
||||
fetcher = Fetch(d.getVar('SRC_URI', True).split(), d)
|
||||
urldata = fetcher.ud
|
||||
for u in urldata:
|
||||
if urldata[u].method.supports_srcrev():
|
||||
@@ -761,11 +762,10 @@ def get_srcrev(d, method_name='sortable_revision'):
|
||||
#
|
||||
# Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
|
||||
#
|
||||
format = d.getVar('SRCREV_FORMAT')
|
||||
format = d.getVar('SRCREV_FORMAT', True)
|
||||
if not format:
|
||||
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
|
||||
|
||||
name_to_rev = {}
|
||||
seenautoinc = False
|
||||
for scm in scms:
|
||||
ud = urldata[scm]
|
||||
@@ -774,16 +774,7 @@ def get_srcrev(d, method_name='sortable_revision'):
|
||||
seenautoinc = seenautoinc or autoinc
|
||||
if len(rev) > 10:
|
||||
rev = rev[:10]
|
||||
name_to_rev[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)
|
||||
|
||||
format = format.replace(name, rev)
|
||||
if seenautoinc:
|
||||
format = "AUTOINC+" + format
|
||||
|
||||
@@ -793,7 +784,7 @@ def localpath(url, d):
|
||||
fetcher = bb.fetch2.Fetch([url], d)
|
||||
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
|
||||
Raise an error if interrupted or cmd fails
|
||||
@@ -817,37 +808,24 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
||||
'GIT_SMART_HTTP',
|
||||
'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
|
||||
'SOCKS5_USER', 'SOCKS5_PASSWD',
|
||||
'DBUS_SESSION_BUS_ADDRESS',
|
||||
'P4CONFIG']
|
||||
'DBUS_SESSION_BUS_ADDRESS']
|
||||
|
||||
if not cleanup:
|
||||
cleanup = []
|
||||
|
||||
# If PATH contains WORKDIR which contains PV which contains SRCPV we
|
||||
# can end up in circular recursion here so give the option of breaking it
|
||||
# in a data store copy.
|
||||
try:
|
||||
d.getVar("PV")
|
||||
except bb.data_smart.ExpansionError:
|
||||
d = bb.data.createCopy(d)
|
||||
d.setVar("PV", "fetcheravoidrecurse")
|
||||
|
||||
origenv = d.getVar("BB_ORIGENV", False)
|
||||
for var in exportvars:
|
||||
val = d.getVar(var) or (origenv and origenv.getVar(var))
|
||||
val = d.getVar(var, True) or (origenv and origenv.getVar(var, True))
|
||||
if val:
|
||||
cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
|
||||
|
||||
# Disable pseudo as it may affect ssh, potentially causing it to hang.
|
||||
cmd = 'export PSEUDO_DISABLED=1; ' + cmd
|
||||
|
||||
logger.debug(1, "Running %s", cmd)
|
||||
|
||||
success = False
|
||||
error_message = ""
|
||||
|
||||
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
|
||||
except bb.process.NotFoundError as e:
|
||||
error_message = "Fetch command %s" % (e.command)
|
||||
@@ -858,7 +836,7 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
||||
output = "output:\n%s" % e.stderr
|
||||
else:
|
||||
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:
|
||||
error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
|
||||
if not success:
|
||||
@@ -872,15 +850,12 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
|
||||
|
||||
return output
|
||||
|
||||
def check_network_access(d, info, url):
|
||||
def check_network_access(d, info = "", url = None):
|
||||
"""
|
||||
log remote network access, and error if BB_NO_NETWORK is set or the given
|
||||
URI is untrusted
|
||||
log remote network access, and error if BB_NO_NETWORK is set
|
||||
"""
|
||||
if d.getVar("BB_NO_NETWORK") == "1":
|
||||
if d.getVar("BB_NO_NETWORK", True) == "1":
|
||||
raise NetworkAccess(url, info)
|
||||
elif not trusted_network(d, url):
|
||||
raise UntrustedUrl(url, info)
|
||||
else:
|
||||
logger.debug(1, "Fetcher accessed the network with the command %s" % info)
|
||||
|
||||
@@ -964,6 +939,8 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
||||
return found
|
||||
return False
|
||||
|
||||
os.chdir(ld.getVar("DL_DIR", True))
|
||||
|
||||
if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
|
||||
ud.method.download(ud, ld)
|
||||
if hasattr(ud.method,"build_mirror_data"):
|
||||
@@ -977,7 +954,7 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
||||
|
||||
# We may be obtaining a mirror tarball which needs further processing by the real fetcher
|
||||
# If that tarball is a local file:// we need to provide a symlink to it
|
||||
dldir = ld.getVar("DL_DIR")
|
||||
dldir = ld.getVar("DL_DIR", True)
|
||||
if origud.mirrortarball and os.path.basename(ud.localpath) == os.path.basename(origud.mirrortarball) \
|
||||
and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
|
||||
# Create donestamp in old format to avoid triggering a re-download
|
||||
@@ -986,14 +963,7 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
||||
open(ud.donestamp, 'w').close()
|
||||
dest = os.path.join(dldir, os.path.basename(ud.localpath))
|
||||
if not os.path.exists(dest):
|
||||
# In case this is executing without any file locks held (as is
|
||||
# the case for file:// URLs), two tasks may end up here at the
|
||||
# same time, in which case we do not want the second task to
|
||||
# fail when the link has already been created by the first task.
|
||||
try:
|
||||
os.symlink(ud.localpath, dest)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.symlink(ud.localpath, dest)
|
||||
if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
|
||||
origud.method.download(origud, ld)
|
||||
if hasattr(origud.method,"build_mirror_data"):
|
||||
@@ -1005,27 +975,17 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
|
||||
# Broken symbolic link
|
||||
os.unlink(origud.localpath)
|
||||
|
||||
# As per above, in case two tasks end up here simultaneously.
|
||||
try:
|
||||
os.symlink(ud.localpath, origud.localpath)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.symlink(ud.localpath, origud.localpath)
|
||||
update_stamp(origud, ld)
|
||||
return ud.localpath
|
||||
|
||||
except bb.fetch2.NetworkAccess:
|
||||
raise
|
||||
|
||||
except IOError as e:
|
||||
if e.errno in [os.errno.ESTALE]:
|
||||
logger.warn("Stale Error Observed %s." % ud.url)
|
||||
return False
|
||||
raise
|
||||
|
||||
except bb.fetch2.BBFetchException as e:
|
||||
if isinstance(e, ChecksumError):
|
||||
logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
|
||||
logger.warning(str(e))
|
||||
logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
|
||||
logger.warn(str(e))
|
||||
if os.path.exists(ud.localpath):
|
||||
rename_bad_checksum(ud, e.checksum)
|
||||
elif isinstance(e, NoChecksumError):
|
||||
@@ -1068,14 +1028,14 @@ def trusted_network(d, url):
|
||||
BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
|
||||
Note: modifies SRC_URI & mirrors.
|
||||
"""
|
||||
if d.getVar('BB_NO_NETWORK') == "1":
|
||||
if d.getVar('BB_NO_NETWORK', True) == "1":
|
||||
return True
|
||||
|
||||
pkgname = d.expand(d.getVar('PN', False))
|
||||
trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
|
||||
|
||||
if not trusted_hosts:
|
||||
trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
|
||||
trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS', True)
|
||||
|
||||
# Not enabled.
|
||||
if not trusted_hosts:
|
||||
@@ -1107,7 +1067,7 @@ def srcrev_internal_helper(ud, d, name):
|
||||
"""
|
||||
|
||||
srcrev = None
|
||||
pn = d.getVar("PN")
|
||||
pn = d.getVar("PN", True)
|
||||
attempts = []
|
||||
if name != '' and pn:
|
||||
attempts.append("SRCREV_%s_pn-%s" % (name, pn))
|
||||
@@ -1118,7 +1078,7 @@ def srcrev_internal_helper(ud, d, name):
|
||||
attempts.append("SRCREV")
|
||||
|
||||
for a in attempts:
|
||||
srcrev = d.getVar(a)
|
||||
srcrev = d.getVar(a, True)
|
||||
if srcrev and srcrev != "INVALID":
|
||||
break
|
||||
|
||||
@@ -1151,7 +1111,7 @@ def get_checksum_file_list(d):
|
||||
"""
|
||||
fetch = Fetch([], d, cache = False, localonly = True)
|
||||
|
||||
dl_dir = d.getVar('DL_DIR')
|
||||
dl_dir = d.getVar('DL_DIR', True)
|
||||
filelist = []
|
||||
for u in fetch.urls:
|
||||
ud = fetch.ud[u]
|
||||
@@ -1165,9 +1125,9 @@ def get_checksum_file_list(d):
|
||||
if f.startswith(dl_dir):
|
||||
# The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
|
||||
if os.path.exists(f):
|
||||
bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN'), os.path.basename(f)))
|
||||
bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
|
||||
else:
|
||||
bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN'), os.path.basename(f)))
|
||||
bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
|
||||
filelist.append(f + ":" + str(os.path.exists(f)))
|
||||
|
||||
return " ".join(filelist)
|
||||
@@ -1196,7 +1156,7 @@ class FetchData(object):
|
||||
self.mirrortarball = None
|
||||
self.basename = None
|
||||
self.basepath = None
|
||||
(self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
|
||||
(self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(data.expand(url, d))
|
||||
self.date = self.getSRCDate(d)
|
||||
self.url = url
|
||||
if not self.user and "user" in self.parm:
|
||||
@@ -1213,16 +1173,16 @@ class FetchData(object):
|
||||
self.sha256_name = "sha256sum"
|
||||
if self.md5_name in self.parm:
|
||||
self.md5_expected = self.parm[self.md5_name]
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||
self.md5_expected = None
|
||||
else:
|
||||
self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
|
||||
self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name, True)
|
||||
if self.sha256_name in self.parm:
|
||||
self.sha256_expected = self.parm[self.sha256_name]
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
|
||||
elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
|
||||
self.sha256_expected = None
|
||||
else:
|
||||
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
|
||||
self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name, True)
|
||||
self.ignore_checksums = False
|
||||
|
||||
self.names = self.parm.get("name",'default').split(',')
|
||||
@@ -1240,7 +1200,7 @@ class FetchData(object):
|
||||
raise NonLocalMethod()
|
||||
|
||||
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'))
|
||||
logger.warn('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN', True))
|
||||
self.parm["protocol"] = self.parm.get("proto", None)
|
||||
|
||||
if hasattr(self.method, "urldata_init"):
|
||||
@@ -1253,7 +1213,7 @@ class FetchData(object):
|
||||
elif self.localfile:
|
||||
self.localpath = self.method.localpath(self, d)
|
||||
|
||||
dldir = d.getVar("DL_DIR")
|
||||
dldir = d.getVar("DL_DIR", True)
|
||||
|
||||
if not self.needdonestamp:
|
||||
return
|
||||
@@ -1271,7 +1231,7 @@ class FetchData(object):
|
||||
self.donestamp = basepath + '.done'
|
||||
self.lockfile = basepath + '.lock'
|
||||
|
||||
def setup_revisions(self, d):
|
||||
def setup_revisons(self, d):
|
||||
self.revisions = {}
|
||||
for name in self.names:
|
||||
self.revisions[name] = srcrev_internal_helper(self, d, name)
|
||||
@@ -1293,12 +1253,12 @@ class FetchData(object):
|
||||
if "srcdate" in self.parm:
|
||||
return self.parm['srcdate']
|
||||
|
||||
pn = d.getVar("PN")
|
||||
pn = d.getVar("PN", True)
|
||||
|
||||
if pn:
|
||||
return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
|
||||
return d.getVar("SRCDATE_%s" % pn, True) or d.getVar("SRCDATE", True) or d.getVar("DATE", True)
|
||||
|
||||
return d.getVar("SRCDATE") or d.getVar("DATE")
|
||||
return d.getVar("SRCDATE", True) or d.getVar("DATE", True)
|
||||
|
||||
class FetchMethod(object):
|
||||
"""Base class for 'fetch'ing data"""
|
||||
@@ -1318,7 +1278,7 @@ class FetchMethod(object):
|
||||
Can also setup variables in urldata for use in go (saving code duplication
|
||||
and duplicate code execution)
|
||||
"""
|
||||
return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
|
||||
return os.path.join(data.getVar("DL_DIR", d, True), urldata.localfile)
|
||||
|
||||
def supports_checksum(self, urldata):
|
||||
"""
|
||||
@@ -1418,10 +1378,6 @@ class FetchMethod(object):
|
||||
cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
|
||||
elif file.endswith('.lz'):
|
||||
cmd = 'lzip -dc %s > %s' % (file, efile)
|
||||
elif file.endswith('.tar.7z'):
|
||||
cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
|
||||
elif file.endswith('.7z'):
|
||||
cmd = '7za x -y %s 1>/dev/null' % file
|
||||
elif file.endswith('.zip') or file.endswith('.jar'):
|
||||
try:
|
||||
dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
|
||||
@@ -1441,28 +1397,15 @@ class FetchMethod(object):
|
||||
else:
|
||||
cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
|
||||
elif file.endswith('.deb') or file.endswith('.ipk'):
|
||||
output = subprocess.check_output('ar -t %s' % file, preexec_fn=subprocess_setup, shell=True)
|
||||
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)
|
||||
cmd = 'ar -p %s data.tar.gz | zcat | tar --no-same-owner -xpf -' % file
|
||||
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)
|
||||
unpackdir = '%s/%s' % (rootdir, urldata.parm.get('subdir'))
|
||||
bb.utils.mkdirhier(unpackdir)
|
||||
else:
|
||||
unpackdir = rootdir
|
||||
@@ -1486,11 +1429,17 @@ class FetchMethod(object):
|
||||
if not cmd:
|
||||
return
|
||||
|
||||
path = data.getVar('PATH')
|
||||
# Change to unpackdir before executing command
|
||||
save_cwd = os.getcwd();
|
||||
os.chdir(unpackdir)
|
||||
|
||||
path = data.getVar('PATH', True)
|
||||
if path:
|
||||
cmd = "PATH=\"%s\" %s" % (path, cmd)
|
||||
bb.note("Unpacking %s to %s/" % (file, unpackdir))
|
||||
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
|
||||
bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
|
||||
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True)
|
||||
|
||||
os.chdir(save_cwd)
|
||||
|
||||
if ret != 0:
|
||||
raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
|
||||
@@ -1543,7 +1492,7 @@ class FetchMethod(object):
|
||||
|
||||
def generate_revision_key(self, ud, d, name):
|
||||
key = self._revision_key(ud, d, name)
|
||||
return "%s-%s" % (key, d.getVar("PN") or "")
|
||||
return "%s-%s" % (key, d.getVar("PN", True) or "")
|
||||
|
||||
class Fetch(object):
|
||||
def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
|
||||
@@ -1551,16 +1500,15 @@ class Fetch(object):
|
||||
raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
|
||||
|
||||
if len(urls) == 0:
|
||||
urls = d.getVar("SRC_URI").split()
|
||||
urls = d.getVar("SRC_URI", True).split()
|
||||
self.urls = urls
|
||||
self.d = d
|
||||
self.ud = {}
|
||||
self.connection_cache = connection_cache
|
||||
|
||||
fn = d.getVar('FILE')
|
||||
mc = d.getVar('__BBMULTICONFIG') or ""
|
||||
if cache and fn and mc + fn in urldata_cache:
|
||||
self.ud = urldata_cache[mc + fn]
|
||||
fn = d.getVar('FILE', True)
|
||||
if cache and fn and fn in urldata_cache:
|
||||
self.ud = urldata_cache[fn]
|
||||
|
||||
for url in urls:
|
||||
if url not in self.ud:
|
||||
@@ -1572,7 +1520,7 @@ class Fetch(object):
|
||||
pass
|
||||
|
||||
if fn and cache:
|
||||
urldata_cache[mc + fn] = self.ud
|
||||
urldata_cache[fn] = self.ud
|
||||
|
||||
def localpath(self, url):
|
||||
if url not in self.urls:
|
||||
@@ -1601,8 +1549,8 @@ class Fetch(object):
|
||||
if not urls:
|
||||
urls = self.urls
|
||||
|
||||
network = self.d.getVar("BB_NO_NETWORK")
|
||||
premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY") == "1")
|
||||
network = self.d.getVar("BB_NO_NETWORK", True)
|
||||
premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY", True) == "1")
|
||||
|
||||
for u in urls:
|
||||
ud = self.ud[u]
|
||||
@@ -1620,21 +1568,14 @@ class Fetch(object):
|
||||
localpath = ud.localpath
|
||||
elif m.try_premirror(ud, self.d):
|
||||
logger.debug(1, "Trying PREMIRRORS")
|
||||
mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
|
||||
mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
|
||||
localpath = try_mirrors(self, self.d, ud, mirrors, False)
|
||||
if localpath:
|
||||
try:
|
||||
# early checksum verification so that if the checksum of the premirror
|
||||
# contents mismatch the fetcher can still try upstream and mirrors
|
||||
update_stamp(ud, self.d)
|
||||
except ChecksumError as e:
|
||||
logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
|
||||
logger.debug(1, str(e))
|
||||
localpath = ""
|
||||
|
||||
if premirroronly:
|
||||
self.d.setVar("BB_NO_NETWORK", "1")
|
||||
|
||||
os.chdir(self.d.getVar("DL_DIR", True))
|
||||
|
||||
firsterr = None
|
||||
verified_stamp = verify_donestamp(ud, self.d)
|
||||
if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
|
||||
@@ -1655,21 +1596,21 @@ class Fetch(object):
|
||||
|
||||
except BBFetchException as e:
|
||||
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))
|
||||
if os.path.exists(ud.localpath):
|
||||
rename_bad_checksum(ud, e.checksum)
|
||||
elif isinstance(e, NoChecksumError):
|
||||
raise
|
||||
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))
|
||||
firsterr = e
|
||||
# Remove any incomplete fetch
|
||||
if not verified_stamp:
|
||||
m.clean(ud, self.d)
|
||||
logger.debug(1, "Trying MIRRORS")
|
||||
mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
|
||||
mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
|
||||
localpath = try_mirrors(self, self.d, ud, mirrors)
|
||||
|
||||
if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
|
||||
@@ -1679,11 +1620,6 @@ class Fetch(object):
|
||||
|
||||
update_stamp(ud, self.d)
|
||||
|
||||
except IOError as e:
|
||||
if e.errno in [os.errno.ESTALE]:
|
||||
logger.error("Stale Error Observed %s." % u)
|
||||
raise ChecksumError("Stale Error Detected")
|
||||
|
||||
except BBFetchException as e:
|
||||
if isinstance(e, ChecksumError):
|
||||
logger.error("Checksum failure fetching %s" % u)
|
||||
@@ -1707,7 +1643,7 @@ class Fetch(object):
|
||||
m = ud.method
|
||||
logger.debug(1, "Testing URL %s", u)
|
||||
# First try checking uri, u, from PREMIRRORS
|
||||
mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
|
||||
mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
|
||||
ret = try_mirrors(self, self.d, ud, mirrors, True)
|
||||
if not ret:
|
||||
# Next try checking from the original uri, u
|
||||
@@ -1715,7 +1651,7 @@ class Fetch(object):
|
||||
ret = m.checkstatus(self, ud, self.d)
|
||||
except:
|
||||
# Finally, try checking uri, u, from MIRRORS
|
||||
mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
|
||||
mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
|
||||
ret = try_mirrors(self, self.d, ud, mirrors, True)
|
||||
|
||||
if not ret:
|
||||
@@ -1800,7 +1736,7 @@ class FetchConnectionCache(object):
|
||||
del self.cache[cn]
|
||||
|
||||
def close_connections(self):
|
||||
for cn in list(self.cache.keys()):
|
||||
for cn in self.cache.keys():
|
||||
self.cache[cn].close()
|
||||
del self.cache[cn]
|
||||
|
||||
@@ -1813,7 +1749,6 @@ from . import svn
|
||||
from . import wget
|
||||
from . import ssh
|
||||
from . import sftp
|
||||
from . import s3
|
||||
from . import perforce
|
||||
from . import bzr
|
||||
from . import hg
|
||||
@@ -1831,7 +1766,6 @@ methods.append(gitannex.GitANNEX())
|
||||
methods.append(cvs.Cvs())
|
||||
methods.append(ssh.SSH())
|
||||
methods.append(sftp.SFTP())
|
||||
methods.append(s3.S3())
|
||||
methods.append(perforce.Perforce())
|
||||
methods.append(bzr.Bzr())
|
||||
methods.append(hg.Hg())
|
||||
|
||||
@@ -27,6 +27,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import runfetchcmd
|
||||
@@ -42,14 +43,14 @@ class Bzr(FetchMethod):
|
||||
"""
|
||||
# Create paths to bzr checkouts
|
||||
relpath = self._strip_leading_slashes(ud.path)
|
||||
ud.pkgdir = os.path.join(d.expand('${BZRDIR}'), ud.host, relpath)
|
||||
ud.pkgdir = os.path.join(data.expand('${BZRDIR}', d), ud.host, relpath)
|
||||
|
||||
ud.setup_revisions(d)
|
||||
ud.setup_revisons(d)
|
||||
|
||||
if not ud.revision:
|
||||
ud.revision = self.latest_revision(ud, d)
|
||||
|
||||
ud.localfile = d.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision))
|
||||
ud.localfile = data.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision), d)
|
||||
|
||||
def _buildbzrcommand(self, ud, d, command):
|
||||
"""
|
||||
@@ -57,7 +58,7 @@ class Bzr(FetchMethod):
|
||||
command is "fetch", "update", "revno"
|
||||
"""
|
||||
|
||||
basecmd = d.expand('${FETCHCMD_bzr}')
|
||||
basecmd = data.expand('${FETCHCMD_bzr}', d)
|
||||
|
||||
proto = ud.parm.get('protocol', 'http')
|
||||
|
||||
@@ -87,25 +88,28 @@ class Bzr(FetchMethod):
|
||||
bzrcmd = self._buildbzrcommand(ud, d, "update")
|
||||
logger.debug(1, "BZR Update %s", 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:
|
||||
bb.utils.remove(os.path.join(ud.pkgdir, os.path.basename(ud.pkgdir)), True)
|
||||
bzrcmd = self._buildbzrcommand(ud, d, "fetch")
|
||||
bb.fetch2.check_network_access(d, bzrcmd, ud.url)
|
||||
logger.debug(1, "BZR Checkout %s", ud.url)
|
||||
bb.utils.mkdirhier(ud.pkgdir)
|
||||
os.chdir(ud.pkgdir)
|
||||
logger.debug(1, "Running %s", bzrcmd)
|
||||
runfetchcmd(bzrcmd, d, workdir=ud.pkgdir)
|
||||
runfetchcmd(bzrcmd, d)
|
||||
|
||||
os.chdir(ud.pkgdir)
|
||||
|
||||
scmdata = ud.parm.get("scmdata", "")
|
||||
if scmdata == "keep":
|
||||
tar_flags = ""
|
||||
else:
|
||||
tar_flags = "--exclude='.bzr' --exclude='.bzrtags'"
|
||||
tar_flags = "--exclude '.bzr' --exclude '.bzrtags'"
|
||||
|
||||
# tar them up to a defined filename
|
||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(ud.pkgdir)),
|
||||
d, cleanup=[ud.localpath], workdir=ud.pkgdir)
|
||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, os.path.basename(ud.pkgdir)), d, cleanup = [ud.localpath])
|
||||
|
||||
def supports_srcrev(self):
|
||||
return True
|
||||
|
||||
@@ -65,10 +65,12 @@ import os
|
||||
import sys
|
||||
import shutil
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import runfetchcmd
|
||||
from bb.fetch2 import logger
|
||||
from distutils import spawn
|
||||
|
||||
class ClearCase(FetchMethod):
|
||||
"""Class to fetch urls via 'clearcase'"""
|
||||
@@ -106,13 +108,13 @@ class ClearCase(FetchMethod):
|
||||
else:
|
||||
ud.module = ""
|
||||
|
||||
ud.basecmd = d.getVar("FETCHCMD_ccrc") or "/usr/bin/env cleartool || rcleartool"
|
||||
ud.basecmd = d.getVar("FETCHCMD_ccrc", True) or spawn.find_executable("cleartool") or spawn.find_executable("rcleartool")
|
||||
|
||||
if d.getVar("SRCREV") == "INVALID":
|
||||
if data.getVar("SRCREV", d, True) == "INVALID":
|
||||
raise FetchError("Set a valid SRCREV for the clearcase fetcher in your recipe, e.g. SRCREV = \"/main/LATEST\" or any other label of your choice.")
|
||||
|
||||
ud.label = d.getVar("SRCREV", False)
|
||||
ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC")
|
||||
ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC", True)
|
||||
|
||||
ud.server = "%s://%s%s" % (ud.proto, ud.host, ud.path)
|
||||
|
||||
@@ -122,7 +124,7 @@ class ClearCase(FetchMethod):
|
||||
|
||||
ud.viewname = "%s-view%s" % (ud.identifier, d.getVar("DATETIME", d, True))
|
||||
ud.csname = "%s-config-spec" % (ud.identifier)
|
||||
ud.ccasedir = os.path.join(d.getVar("DL_DIR"), ud.type)
|
||||
ud.ccasedir = os.path.join(data.getVar("DL_DIR", d, True), ud.type)
|
||||
ud.viewdir = os.path.join(ud.ccasedir, ud.viewname)
|
||||
ud.configspecfile = os.path.join(ud.ccasedir, ud.csname)
|
||||
ud.localfile = "%s.tar.gz" % (ud.identifier)
|
||||
@@ -142,7 +144,7 @@ class ClearCase(FetchMethod):
|
||||
self.debug("configspecfile = %s" % ud.configspecfile)
|
||||
self.debug("localfile = %s" % ud.localfile)
|
||||
|
||||
ud.localfile = os.path.join(d.getVar("DL_DIR"), ud.localfile)
|
||||
ud.localfile = os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
|
||||
|
||||
def _build_ccase_command(self, ud, command):
|
||||
"""
|
||||
@@ -200,10 +202,11 @@ class ClearCase(FetchMethod):
|
||||
|
||||
def _remove_view(self, ud, d):
|
||||
if os.path.exists(ud.viewdir):
|
||||
os.chdir(ud.ccasedir)
|
||||
cmd = self._build_ccase_command(ud, 'rmview');
|
||||
logger.info("cleaning up [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||
output = runfetchcmd(cmd, d, workdir=ud.ccasedir)
|
||||
output = runfetchcmd(cmd, d)
|
||||
logger.info("rmview output: %s", output)
|
||||
|
||||
def need_update(self, ud, d):
|
||||
@@ -238,10 +241,11 @@ class ClearCase(FetchMethod):
|
||||
raise e
|
||||
|
||||
# Set configspec: Setting the configspec effectively fetches the files as defined in the configspec
|
||||
os.chdir(ud.viewdir)
|
||||
cmd = self._build_ccase_command(ud, 'setcs');
|
||||
logger.info("fetching data [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
|
||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||
output = runfetchcmd(cmd, d, workdir=ud.viewdir)
|
||||
output = runfetchcmd(cmd, d)
|
||||
logger.info("%s", output)
|
||||
|
||||
# Copy the configspec to the viewdir so we have it in our source tarball later
|
||||
|
||||
@@ -63,7 +63,7 @@ class Cvs(FetchMethod):
|
||||
if 'fullpath' in ud.parm:
|
||||
fullpath = '_fullpath'
|
||||
|
||||
ud.localfile = d.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath))
|
||||
ud.localfile = bb.data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d)
|
||||
|
||||
def need_update(self, ud, d):
|
||||
if (ud.date == "now"):
|
||||
@@ -87,10 +87,10 @@ class Cvs(FetchMethod):
|
||||
cvsroot = ud.path
|
||||
else:
|
||||
cvsroot = ":" + method
|
||||
cvsproxyhost = d.getVar('CVS_PROXY_HOST')
|
||||
cvsproxyhost = d.getVar('CVS_PROXY_HOST', True)
|
||||
if cvsproxyhost:
|
||||
cvsroot += ";proxy=" + cvsproxyhost
|
||||
cvsproxyport = d.getVar('CVS_PROXY_PORT')
|
||||
cvsproxyport = d.getVar('CVS_PROXY_PORT', True)
|
||||
if cvsproxyport:
|
||||
cvsroot += ";proxyport=" + cvsproxyport
|
||||
cvsroot += ":" + ud.user
|
||||
@@ -110,7 +110,7 @@ class Cvs(FetchMethod):
|
||||
if ud.tag:
|
||||
options.append("-r %s" % ud.tag)
|
||||
|
||||
cvsbasecmd = d.getVar("FETCHCMD_cvs")
|
||||
cvsbasecmd = d.getVar("FETCHCMD_cvs", True)
|
||||
cvscmd = cvsbasecmd + " '-d" + cvsroot + "' co " + " ".join(options) + " " + ud.module
|
||||
cvsupdatecmd = cvsbasecmd + " '-d" + cvsroot + "' update -d -P " + " ".join(options)
|
||||
|
||||
@@ -120,26 +120,25 @@ class Cvs(FetchMethod):
|
||||
|
||||
# create module directory
|
||||
logger.debug(2, "Fetch: checking for module directory")
|
||||
pkg = d.getVar('PN')
|
||||
pkgdir = os.path.join(d.getVar('CVSDIR'), pkg)
|
||||
pkg = d.getVar('PN', True)
|
||||
pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
|
||||
moddir = os.path.join(pkgdir, localdir)
|
||||
workdir = None
|
||||
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
|
||||
logger.info("Update " + ud.url)
|
||||
bb.fetch2.check_network_access(d, cvsupdatecmd, ud.url)
|
||||
# update sources there
|
||||
workdir = moddir
|
||||
os.chdir(moddir)
|
||||
cmd = cvsupdatecmd
|
||||
else:
|
||||
logger.info("Fetch " + ud.url)
|
||||
# check out sources there
|
||||
bb.utils.mkdirhier(pkgdir)
|
||||
workdir = pkgdir
|
||||
os.chdir(pkgdir)
|
||||
logger.debug(1, "Running %s", cvscmd)
|
||||
bb.fetch2.check_network_access(d, cvscmd, ud.url)
|
||||
cmd = cvscmd
|
||||
|
||||
runfetchcmd(cmd, d, cleanup=[moddir], workdir=workdir)
|
||||
runfetchcmd(cmd, d, cleanup = [moddir])
|
||||
|
||||
if not os.access(moddir, os.R_OK):
|
||||
raise FetchError("Directory %s was not readable despite sucessful fetch?!" % moddir, ud.url)
|
||||
@@ -148,24 +147,24 @@ class Cvs(FetchMethod):
|
||||
if scmdata == "keep":
|
||||
tar_flags = ""
|
||||
else:
|
||||
tar_flags = "--exclude='CVS'"
|
||||
tar_flags = "--exclude 'CVS'"
|
||||
|
||||
# tar them up to a defined filename
|
||||
workdir = None
|
||||
if 'fullpath' in ud.parm:
|
||||
workdir = pkgdir
|
||||
os.chdir(pkgdir)
|
||||
cmd = "tar %s -czf %s %s" % (tar_flags, ud.localpath, localdir)
|
||||
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))
|
||||
|
||||
runfetchcmd(cmd, d, cleanup=[ud.localpath], workdir=workdir)
|
||||
runfetchcmd(cmd, d, cleanup = [ud.localpath])
|
||||
|
||||
def clean(self, ud, d):
|
||||
""" Clean CVS Files and tarballs """
|
||||
|
||||
pkg = d.getVar('PN')
|
||||
pkgdir = os.path.join(d.getVar("CVSDIR"), pkg)
|
||||
pkg = d.getVar('PN', True)
|
||||
pkgdir = os.path.join(d.getVar("CVSDIR", True), pkg)
|
||||
|
||||
bb.utils.remove(pkgdir, True)
|
||||
bb.utils.remove(ud.localpath)
|
||||
|
||||
@@ -49,10 +49,6 @@ Supported SRC_URI options are:
|
||||
referring to commit which is valid in tag instead of branch.
|
||||
The default is "0", set nobranch=1 if needed.
|
||||
|
||||
- usehead
|
||||
For local git:// urls to use the current branch HEAD as the revision for use with
|
||||
AUTOREV. Implies nobranch.
|
||||
|
||||
"""
|
||||
|
||||
#Copyright (C) 2005 Richard Purdie
|
||||
@@ -75,52 +71,11 @@ import os
|
||||
import re
|
||||
import bb
|
||||
import errno
|
||||
import bb.progress
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import runfetchcmd
|
||||
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 to fetch a module or modules from git repositories"""
|
||||
def init(self, d):
|
||||
@@ -156,13 +111,6 @@ class Git(FetchMethod):
|
||||
|
||||
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
|
||||
ud.bareclone = ud.parm.get("bareclone","0") == "1"
|
||||
if ud.bareclone:
|
||||
@@ -173,19 +121,16 @@ class Git(FetchMethod):
|
||||
if len(branches) != len(ud.names):
|
||||
raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
|
||||
ud.branches = {}
|
||||
for pos, name in enumerate(ud.names):
|
||||
branch = branches[pos]
|
||||
for name in ud.names:
|
||||
branch = branches[ud.names.index(name)]
|
||||
ud.branches[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 = d.getVar("FETCHCMD_git") 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 = ((d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0") != "0") or ud.rebaseable
|
||||
|
||||
ud.setup_revisions(d)
|
||||
ud.setup_revisons(d)
|
||||
|
||||
for name in ud.names:
|
||||
# Ensure anything that doesn't look like a sha256 checksum/revision is translated into one
|
||||
@@ -205,9 +150,9 @@ class Git(FetchMethod):
|
||||
if ud.rebaseable:
|
||||
for name in ud.names:
|
||||
gitsrcname = gitsrcname + '_' + ud.revisions[name]
|
||||
ud.mirrortarball = 'git2_%s.tar.gz' % gitsrcname
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
|
||||
gitdir = d.getVar("GITDIR") or (d.getVar("DL_DIR") + "/git2/")
|
||||
ud.mirrortarball = 'git2_%s.tar.gz' % (gitsrcname)
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
|
||||
gitdir = d.getVar("GITDIR", True) or (d.getVar("DL_DIR", True) + "/git2/")
|
||||
ud.clonedir = os.path.join(gitdir, gitsrcname)
|
||||
|
||||
ud.localfile = ud.clonedir
|
||||
@@ -218,8 +163,9 @@ class Git(FetchMethod):
|
||||
def need_update(self, ud, d):
|
||||
if not os.path.exists(ud.clonedir):
|
||||
return True
|
||||
os.chdir(ud.clonedir)
|
||||
for name in ud.names:
|
||||
if not self._contains_ref(ud, d, name, ud.clonedir):
|
||||
if not self._contains_ref(ud, d, name):
|
||||
return True
|
||||
if ud.write_tarballs and not os.path.exists(ud.fullmirror):
|
||||
return True
|
||||
@@ -228,7 +174,7 @@ class Git(FetchMethod):
|
||||
def try_premirror(self, ud, d):
|
||||
# If we don't do this, updating an existing checkout with only premirrors
|
||||
# is not possible
|
||||
if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
|
||||
if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
|
||||
return True
|
||||
if os.path.exists(ud.clonedir):
|
||||
return False
|
||||
@@ -240,7 +186,8 @@ class Git(FetchMethod):
|
||||
# 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):
|
||||
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)
|
||||
|
||||
@@ -249,38 +196,38 @@ class Git(FetchMethod):
|
||||
# We do this since git will use a "-l" option automatically for local urls where possible
|
||||
if repourl.startswith("file://"):
|
||||
repourl = repourl[7:]
|
||||
clone_cmd = "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':
|
||||
bb.fetch2.check_network_access(d, clone_cmd, ud.url)
|
||||
progresshandler = GitProgressHandler(d)
|
||||
runfetchcmd(clone_cmd, d, log=progresshandler)
|
||||
bb.fetch2.check_network_access(d, clone_cmd)
|
||||
runfetchcmd(clone_cmd, d)
|
||||
|
||||
os.chdir(ud.clonedir)
|
||||
# Update the checkout if needed
|
||||
needupdate = False
|
||||
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
|
||||
if needupdate:
|
||||
try:
|
||||
runfetchcmd("%s remote rm origin" % ud.basecmd, d, workdir=ud.clonedir)
|
||||
runfetchcmd("%s remote rm origin" % ud.basecmd, d)
|
||||
except bb.fetch2.FetchError:
|
||||
logger.debug(1, "No Origin")
|
||||
|
||||
runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d, workdir=ud.clonedir)
|
||||
fetch_cmd = "LANG=C %s fetch -f --progress %s refs/*:refs/*" % (ud.basecmd, repourl)
|
||||
runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d)
|
||||
fetch_cmd = "%s fetch -f --prune %s refs/*:refs/*" % (ud.basecmd, repourl)
|
||||
if ud.proto.lower() != 'file':
|
||||
bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
|
||||
progresshandler = GitProgressHandler(d)
|
||||
runfetchcmd(fetch_cmd, d, log=progresshandler, workdir=ud.clonedir)
|
||||
runfetchcmd("%s prune-packed" % ud.basecmd, d, workdir=ud.clonedir)
|
||||
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d, workdir=ud.clonedir)
|
||||
runfetchcmd(fetch_cmd, d)
|
||||
runfetchcmd("%s prune-packed" % ud.basecmd, d)
|
||||
runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
|
||||
try:
|
||||
os.unlink(ud.fullmirror)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise
|
||||
os.chdir(ud.clonedir)
|
||||
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]))
|
||||
|
||||
def build_mirror_data(self, ud, d):
|
||||
@@ -290,16 +237,17 @@ class Git(FetchMethod):
|
||||
if os.path.islink(ud.fullmirror):
|
||||
os.unlink(ud.fullmirror)
|
||||
|
||||
os.chdir(ud.clonedir)
|
||||
logger.info("Creating tarball of git repository")
|
||||
runfetchcmd("tar -czf %s ." % ud.fullmirror, d, workdir=ud.clonedir)
|
||||
runfetchcmd("touch %s.done" % ud.fullmirror, d)
|
||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, os.path.join(".") ), d)
|
||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d)
|
||||
|
||||
def unpack(self, ud, destdir, d):
|
||||
""" unpack the downloaded src to destdir"""
|
||||
|
||||
subdir = ud.parm.get("subpath", "")
|
||||
if subdir != "":
|
||||
readpathspec = ":%s" % subdir
|
||||
readpathspec = ":%s" % (subdir)
|
||||
def_destsuffix = "%s/" % os.path.basename(subdir.rstrip('/'))
|
||||
else:
|
||||
readpathspec = ""
|
||||
@@ -315,21 +263,21 @@ class Git(FetchMethod):
|
||||
cloneflags += " --mirror"
|
||||
|
||||
runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, cloneflags, ud.clonedir, destdir), d)
|
||||
os.chdir(destdir)
|
||||
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 subdir != "":
|
||||
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, workdir=destdir)
|
||||
runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d)
|
||||
runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d)
|
||||
elif not ud.nobranch:
|
||||
branchname = ud.branches[ud.names[0]]
|
||||
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, \
|
||||
branchname), d, workdir=destdir)
|
||||
branchname), d)
|
||||
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
|
||||
|
||||
@@ -343,7 +291,7 @@ class Git(FetchMethod):
|
||||
def supports_srcrev(self):
|
||||
return True
|
||||
|
||||
def _contains_ref(self, ud, d, name, wd):
|
||||
def _contains_ref(self, ud, d, name):
|
||||
cmd = ""
|
||||
if ud.nobranch:
|
||||
cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
|
||||
@@ -352,7 +300,7 @@ class Git(FetchMethod):
|
||||
cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
|
||||
ud.basecmd, ud.revisions[name], ud.branches[name])
|
||||
try:
|
||||
output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
|
||||
output = runfetchcmd(cmd, d, quiet=True)
|
||||
except bb.fetch2.FetchError:
|
||||
return False
|
||||
if len(output.split()) > 1:
|
||||
@@ -379,26 +327,14 @@ class Git(FetchMethod):
|
||||
"""
|
||||
Run git ls-remote with the specified search string
|
||||
"""
|
||||
# Prevent recursion e.g. in OE if SRCPV is in PV, PV is in WORKDIR,
|
||||
# and WORKDIR is in PATH (as a result of RSS), our call to
|
||||
# runfetchcmd() exports PATH so this function will get called again (!)
|
||||
# In this scenario the return call of the function isn't actually
|
||||
# important - WORKDIR isn't needed in PATH to call git ls-remote
|
||||
# anyway.
|
||||
if d.getVar('_BB_GIT_IN_LSREMOTE', False):
|
||||
return ''
|
||||
d.setVar('_BB_GIT_IN_LSREMOTE', '1')
|
||||
try:
|
||||
repourl = self._get_repo_url(ud)
|
||||
cmd = "%s ls-remote %s %s" % \
|
||||
(ud.basecmd, repourl, search)
|
||||
if ud.proto.lower() != 'file':
|
||||
bb.fetch2.check_network_access(d, cmd, repourl)
|
||||
output = runfetchcmd(cmd, d, True)
|
||||
if not output:
|
||||
raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
|
||||
finally:
|
||||
d.delVar('_BB_GIT_IN_LSREMOTE')
|
||||
repourl = self._get_repo_url(ud)
|
||||
cmd = "%s ls-remote %s %s" % \
|
||||
(ud.basecmd, repourl, search)
|
||||
if ud.proto.lower() != 'file':
|
||||
bb.fetch2.check_network_access(d, cmd)
|
||||
output = runfetchcmd(cmd, d, True)
|
||||
if not output:
|
||||
raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
|
||||
return output
|
||||
|
||||
def _latest_revision(self, ud, d, name):
|
||||
@@ -407,17 +343,16 @@ class Git(FetchMethod):
|
||||
"""
|
||||
output = self._lsremote(ud, d, "")
|
||||
# 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]
|
||||
tag = ud.unresolvedrev[name]
|
||||
else:
|
||||
head = "refs/heads/%s" % ud.unresolvedrev[name]
|
||||
tag = "refs/tags/%s" % ud.unresolvedrev[name]
|
||||
for s in [head, tag + "^{}", tag]:
|
||||
for l in output.strip().split('\n'):
|
||||
sha1, ref = l.split()
|
||||
if s == ref:
|
||||
return sha1
|
||||
for l in output.split('\n'):
|
||||
if s in l:
|
||||
return l.split()[0]
|
||||
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))
|
||||
|
||||
@@ -429,7 +364,7 @@ class Git(FetchMethod):
|
||||
"""
|
||||
pupver = ('', '')
|
||||
|
||||
tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX') or "(?P<pver>([0-9][\.|_]?)+)")
|
||||
tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX', True) or "(?P<pver>([0-9][\.|_]?)+)")
|
||||
try:
|
||||
output = self._lsremote(ud, d, "refs/tags/*")
|
||||
except bb.fetch2.FetchError or bb.fetch2.NetworkAccess:
|
||||
@@ -481,7 +416,7 @@ class Git(FetchMethod):
|
||||
if not os.path.exists(rev_file) or not os.path.getsize(rev_file):
|
||||
from pipes import quote
|
||||
commits = bb.fetch2.runfetchcmd(
|
||||
"git rev-list %s -- | wc -l" % quote(rev),
|
||||
"git rev-list %s -- | wc -l" % (quote(rev)),
|
||||
d, quiet=True).strip().lstrip('0')
|
||||
if commits:
|
||||
open(rev_file, "w").write("%d\n" % int(commits))
|
||||
@@ -496,5 +431,5 @@ class Git(FetchMethod):
|
||||
try:
|
||||
self._lsremote(ud, d, "")
|
||||
return True
|
||||
except bb.fetch2.FetchError:
|
||||
except FetchError:
|
||||
return False
|
||||
|
||||
@@ -22,6 +22,7 @@ BitBake 'Fetch' git annex implementation
|
||||
|
||||
import os
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2.git import Git
|
||||
from bb.fetch2 import runfetchcmd
|
||||
from bb.fetch2 import logger
|
||||
@@ -33,42 +34,43 @@ class GitANNEX(Git):
|
||||
"""
|
||||
return ud.type in ['gitannex']
|
||||
|
||||
def uses_annex(self, ud, d, wd):
|
||||
def uses_annex(self, ud, d):
|
||||
for name in ud.names:
|
||||
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
|
||||
except bb.fetch.FetchError:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def update_annex(self, ud, d, wd):
|
||||
def update_annex(self, ud, d):
|
||||
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:
|
||||
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
|
||||
|
||||
def 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:
|
||||
self.update_annex(ud, d, ud.clonedir)
|
||||
self.update_annex(ud, d)
|
||||
|
||||
def unpack(self, ud, destdir, d):
|
||||
Git.unpack(self, ud, destdir, d)
|
||||
|
||||
os.chdir(ud.destdir)
|
||||
try:
|
||||
runfetchcmd("%s annex init" % (ud.basecmd), d, workdir=ud.destdir)
|
||||
runfetchcmd("%s annex sync" % (ud.basecmd), d)
|
||||
except bb.fetch.FetchError:
|
||||
pass
|
||||
|
||||
annex = self.uses_annex(ud, d, ud.destdir)
|
||||
annex = self.uses_annex(ud, d)
|
||||
if annex:
|
||||
runfetchcmd("%s annex get" % (ud.basecmd), d, workdir=ud.destdir)
|
||||
runfetchcmd("chmod u+w -R %s/.git/annex" % (ud.destdir), d, quiet=True, workdir=ud.destdir)
|
||||
|
||||
runfetchcmd("%s annex get" % (ud.basecmd), d)
|
||||
runfetchcmd("chmod u+w -R %s/.git/annex" % (ud.destdir), d, quiet=True)
|
||||
|
||||
@@ -31,6 +31,7 @@ NOTE: Switching a SRC_URI from "git://" to "gitsm://" requires a clean of your r
|
||||
|
||||
import os
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2.git import Git
|
||||
from bb.fetch2 import runfetchcmd
|
||||
from bb.fetch2 import logger
|
||||
@@ -42,10 +43,10 @@ class GitSM(Git):
|
||||
"""
|
||||
return ud.type in ['gitsm']
|
||||
|
||||
def uses_submodules(self, ud, d, wd):
|
||||
def uses_submodules(self, ud, d):
|
||||
for name in ud.names:
|
||||
try:
|
||||
runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=wd)
|
||||
runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True)
|
||||
return True
|
||||
except bb.fetch.FetchError:
|
||||
pass
|
||||
@@ -106,25 +107,28 @@ class GitSM(Git):
|
||||
os.mkdir(tmpclonedir)
|
||||
os.rename(ud.clonedir, gitdir)
|
||||
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
|
||||
runfetchcmd(ud.basecmd + " reset --hard", d, workdir=tmpclonedir)
|
||||
runfetchcmd(ud.basecmd + " checkout -f " + ud.revisions[ud.names[0]], d, workdir=tmpclonedir)
|
||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d, workdir=tmpclonedir)
|
||||
os.chdir(tmpclonedir)
|
||||
runfetchcmd(ud.basecmd + " reset --hard", d)
|
||||
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d)
|
||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d)
|
||||
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,)
|
||||
bb.utils.remove(tmpclonedir, True)
|
||||
|
||||
def 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:
|
||||
self.update_submodules(ud, d)
|
||||
|
||||
def 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:
|
||||
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d, workdir=ud.destdir)
|
||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d, workdir=ud.destdir)
|
||||
runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d)
|
||||
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d)
|
||||
|
||||
@@ -29,6 +29,7 @@ import sys
|
||||
import logging
|
||||
import bb
|
||||
import errno
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import MissingParameterError
|
||||
@@ -66,7 +67,7 @@ class Hg(FetchMethod):
|
||||
else:
|
||||
ud.proto = "hg"
|
||||
|
||||
ud.setup_revisions(d)
|
||||
ud.setup_revisons(d)
|
||||
|
||||
if 'rev' in ud.parm:
|
||||
ud.revision = ud.parm['rev']
|
||||
@@ -77,15 +78,15 @@ class Hg(FetchMethod):
|
||||
hgsrcname = '%s_%s_%s' % (ud.module.replace('/', '.'), \
|
||||
ud.host, ud.path.replace('/', '.'))
|
||||
ud.mirrortarball = 'hg_%s.tar.gz' % hgsrcname
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
|
||||
|
||||
hgdir = d.getVar("HGDIR") or (d.getVar("DL_DIR") + "/hg/")
|
||||
hgdir = d.getVar("HGDIR", True) or (d.getVar("DL_DIR", True) + "/hg/")
|
||||
ud.pkgdir = os.path.join(hgdir, hgsrcname)
|
||||
ud.moddir = os.path.join(ud.pkgdir, ud.module)
|
||||
ud.localfile = ud.moddir
|
||||
ud.basecmd = d.getVar("FETCHCMD_hg") or "/usr/bin/env hg"
|
||||
ud.basecmd = data.getVar("FETCHCMD_hg", d, True) or "/usr/bin/env hg"
|
||||
|
||||
ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS")
|
||||
ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS", True)
|
||||
|
||||
def need_update(self, ud, d):
|
||||
revTag = ud.parm.get('rev', 'tip')
|
||||
@@ -98,7 +99,7 @@ class Hg(FetchMethod):
|
||||
def try_premirror(self, ud, d):
|
||||
# If we don't do this, updating an existing checkout with only premirrors
|
||||
# is not possible
|
||||
if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
|
||||
if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
|
||||
return True
|
||||
if os.path.exists(ud.moddir):
|
||||
return False
|
||||
@@ -168,22 +169,25 @@ class Hg(FetchMethod):
|
||||
# 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):
|
||||
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):
|
||||
# Found the source, check whether need pull
|
||||
updatecmd = self._buildhgcommand(ud, d, "update")
|
||||
os.chdir(ud.moddir)
|
||||
logger.debug(1, "Running %s", updatecmd)
|
||||
try:
|
||||
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
||||
runfetchcmd(updatecmd, d)
|
||||
except bb.fetch2.FetchError:
|
||||
# Runnning pull in the repo
|
||||
pullcmd = self._buildhgcommand(ud, d, "pull")
|
||||
logger.info("Pulling " + ud.url)
|
||||
# update sources there
|
||||
os.chdir(ud.moddir)
|
||||
logger.debug(1, "Running %s", pullcmd)
|
||||
bb.fetch2.check_network_access(d, pullcmd, ud.url)
|
||||
runfetchcmd(pullcmd, d, workdir=ud.moddir)
|
||||
runfetchcmd(pullcmd, d)
|
||||
try:
|
||||
os.unlink(ud.fullmirror)
|
||||
except OSError as exc:
|
||||
@@ -196,15 +200,17 @@ class Hg(FetchMethod):
|
||||
logger.info("Fetch " + ud.url)
|
||||
# check out sources there
|
||||
bb.utils.mkdirhier(ud.pkgdir)
|
||||
os.chdir(ud.pkgdir)
|
||||
logger.debug(1, "Running %s", fetchcmd)
|
||||
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
|
||||
# won't checkout the specified revision if its on a branch
|
||||
updatecmd = self._buildhgcommand(ud, d, "update")
|
||||
os.chdir(ud.moddir)
|
||||
logger.debug(1, "Running %s", updatecmd)
|
||||
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
||||
runfetchcmd(updatecmd, d)
|
||||
|
||||
def clean(self, ud, d):
|
||||
""" Clean the hg dir """
|
||||
@@ -220,7 +226,7 @@ class Hg(FetchMethod):
|
||||
"""
|
||||
Compute tip revision for the url
|
||||
"""
|
||||
bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"), ud.url)
|
||||
bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"))
|
||||
output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
|
||||
return output.strip()
|
||||
|
||||
@@ -240,9 +246,10 @@ class Hg(FetchMethod):
|
||||
if os.path.islink(ud.fullmirror):
|
||||
os.unlink(ud.fullmirror)
|
||||
|
||||
os.chdir(ud.pkgdir)
|
||||
logger.info("Creating tarball of hg repository")
|
||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d, workdir=ud.pkgdir)
|
||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.pkgdir)
|
||||
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d)
|
||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d)
|
||||
|
||||
def localpath(self, ud, d):
|
||||
return ud.pkgdir
|
||||
@@ -262,8 +269,10 @@ class Hg(FetchMethod):
|
||||
logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
|
||||
runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
|
||||
logger.debug(2, "Unpack: updating source in '" + codir + "'")
|
||||
runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d, workdir=codir)
|
||||
runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d, workdir=codir)
|
||||
os.chdir(codir)
|
||||
runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d)
|
||||
runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d)
|
||||
else:
|
||||
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,9 +26,10 @@ BitBake build tools.
|
||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||
|
||||
import os
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib
|
||||
import bb
|
||||
import bb.utils
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod, FetchError
|
||||
from bb.fetch2 import logger
|
||||
|
||||
@@ -41,7 +42,7 @@ class Local(FetchMethod):
|
||||
|
||||
def urldata_init(self, ud, d):
|
||||
# We don't set localfile as for this fetcher the file is already local!
|
||||
ud.decodedurl = urllib.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.basepath = ud.decodedurl
|
||||
ud.needdonestamp = False
|
||||
@@ -62,11 +63,17 @@ class Local(FetchMethod):
|
||||
newpath = path
|
||||
if path[0] == "/":
|
||||
return [path]
|
||||
filespath = d.getVar('FILESPATH')
|
||||
filespath = data.getVar('FILESPATH', d, True)
|
||||
if filespath:
|
||||
logger.debug(2, "Searching for %s in paths:\n %s" % (path, "\n ".join(filespath.split(":"))))
|
||||
newpath, hist = bb.utils.which(filespath, path, history=True)
|
||||
searched.extend(hist)
|
||||
if not newpath:
|
||||
filesdir = data.getVar('FILESDIR', d, True)
|
||||
if filesdir:
|
||||
logger.debug(2, "Searching for %s in path: %s" % (path, filesdir))
|
||||
newpath = os.path.join(filesdir, path)
|
||||
searched.append(newpath)
|
||||
if (not newpath or not os.path.exists(newpath)) and path.find("*") != -1:
|
||||
# For expressions using '*', best we can do is take the first directory in FILESPATH that exists
|
||||
newpath, hist = bb.utils.which(filespath, ".", history=True)
|
||||
@@ -74,7 +81,7 @@ class Local(FetchMethod):
|
||||
logger.debug(2, "Searching for %s in path: %s" % (path, newpath))
|
||||
return searched
|
||||
if not os.path.exists(newpath):
|
||||
dldirfile = os.path.join(d.getVar("DL_DIR"), path)
|
||||
dldirfile = os.path.join(d.getVar("DL_DIR", True), path)
|
||||
logger.debug(2, "Defaulting to %s for %s" % (dldirfile, path))
|
||||
bb.utils.mkdirhier(os.path.dirname(dldirfile))
|
||||
searched.append(dldirfile)
|
||||
@@ -93,10 +100,13 @@ class Local(FetchMethod):
|
||||
# no need to fetch local files, we'll deal with them in place.
|
||||
if self.supports_checksum(urldata) and not os.path.exists(urldata.localpath):
|
||||
locations = []
|
||||
filespath = d.getVar('FILESPATH')
|
||||
filespath = data.getVar('FILESPATH', d, True)
|
||||
if filespath:
|
||||
locations = filespath.split(":")
|
||||
locations.append(d.getVar("DL_DIR"))
|
||||
filesdir = data.getVar('FILESDIR', d, True)
|
||||
if filesdir:
|
||||
locations.append(filesdir)
|
||||
locations.append(d.getVar("DL_DIR", True))
|
||||
|
||||
msg = "Unable to find file " + urldata.url + " anywhere. The paths that were searched were:\n " + "\n ".join(locations)
|
||||
raise FetchError(msg)
|
||||
|
||||
@@ -13,18 +13,19 @@ Usage in the recipe:
|
||||
- name
|
||||
- version
|
||||
|
||||
npm://registry.npmjs.org/${PN}/-/${PN}-${PV}.tgz would become npm://registry.npmjs.org;name=${PN};version=${PV}
|
||||
npm://registry.npmjs.org/${PN}/-/${PN}-${PV}.tgz would become npm://registry.npmjs.org;name=${PN};ver=${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 urllib
|
||||
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
|
||||
@@ -32,6 +33,7 @@ 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
|
||||
@@ -78,7 +80,6 @@ class Npm(FetchMethod):
|
||||
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.bbnpmmanifest = ud.bbnpmmanifest.replace('/', '-')
|
||||
ud.registry = "http://%s" % (ud.url.replace('npm://', '', 1).split(';'))[0]
|
||||
prefixdir = "npm/%s" % ud.pkgname
|
||||
ud.pkgdatadir = d.expand("${DL_DIR}/%s" % prefixdir)
|
||||
@@ -86,13 +87,12 @@ class Npm(FetchMethod):
|
||||
bb.utils.mkdirhier(ud.pkgdatadir)
|
||||
ud.localpath = d.expand("${DL_DIR}/npm/%s" % ud.bbnpmmanifest)
|
||||
|
||||
self.basecmd = d.getVar("FETCHCMD_wget") or "/usr/bin/env wget -O -t 2 -T 30 -nv --passive-ftp --no-check-certificate "
|
||||
ud.prefixdir = prefixdir
|
||||
self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -O -t 2 -T 30 -nv --passive-ftp --no-check-certificate "
|
||||
self.basecmd += " --directory-prefix=%s " % prefixdir
|
||||
|
||||
ud.write_tarballs = ((d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0") != "0")
|
||||
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.mirrortarball = ud.mirrortarball.replace('/', '-')
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
|
||||
ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
|
||||
|
||||
def need_update(self, ud, d):
|
||||
if os.path.exists(ud.localpath):
|
||||
@@ -101,9 +101,8 @@ class Npm(FetchMethod):
|
||||
|
||||
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, ud.url)
|
||||
dldir = d.getVar("DL_DIR")
|
||||
runfetchcmd(command, d, quiet, workdir=dldir)
|
||||
bb.fetch2.check_network_access(d, command)
|
||||
runfetchcmd(command, d, quiet)
|
||||
|
||||
def _unpackdep(self, ud, pkg, data, destdir, dldir, d):
|
||||
file = data[pkg]['tgz']
|
||||
@@ -114,13 +113,16 @@ class Npm(FetchMethod):
|
||||
bb.fatal("NPM package %s downloaded not a tarball!" % file)
|
||||
|
||||
# Change to subdir before executing command
|
||||
save_cwd = os.getcwd()
|
||||
if not os.path.exists(destdir):
|
||||
os.makedirs(destdir)
|
||||
path = d.getVar('PATH')
|
||||
os.chdir(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)
|
||||
bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
|
||||
ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True)
|
||||
os.chdir(save_cwd)
|
||||
|
||||
if ret != 0:
|
||||
raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), ud.url)
|
||||
@@ -132,17 +134,13 @@ class Npm(FetchMethod):
|
||||
|
||||
|
||||
def unpack(self, ud, destdir, d):
|
||||
dldir = d.getVar("DL_DIR")
|
||||
with open("%s/npm/%s" % (dldir, ud.bbnpmmanifest)) as datafile:
|
||||
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)
|
||||
self._unpackdep(ud, ud.pkgname, workobj, "%s/npmpkg" % destdir, dldir, d)
|
||||
|
||||
def _parse_view(self, output):
|
||||
'''
|
||||
@@ -164,9 +162,7 @@ class Npm(FetchMethod):
|
||||
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 = []
|
||||
def _getdependencies(self, pkg, data, version, d, ud, optional=False):
|
||||
pkgfullname = pkg
|
||||
if version != '*' and not '/' in version:
|
||||
pkgfullname += "@'%s'" % version
|
||||
@@ -181,25 +177,17 @@ class Npm(FetchMethod):
|
||||
if pkg_os:
|
||||
if not isinstance(pkg_os, list):
|
||||
pkg_os = [pkg_os]
|
||||
blacklist = False
|
||||
for item in pkg_os:
|
||||
if item.startswith('!'):
|
||||
blacklist = True
|
||||
break
|
||||
if (not blacklist and 'linux' not in pkg_os) or '!linux' in 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)
|
||||
self._runwget(ud, d, "%s %s" % (self.basecmd, outputurl), False)
|
||||
|
||||
dependencies = pdata.get('dependencies', {})
|
||||
optionalDependencies = pdata.get('optionalDependencies', {})
|
||||
dependencies.update(optionalDependencies)
|
||||
depsfound = {}
|
||||
optdepsfound = {}
|
||||
data[pkg]['deps'] = {}
|
||||
@@ -208,20 +196,13 @@ class Npm(FetchMethod):
|
||||
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)
|
||||
for dep, version in optdepsfound.iteritems():
|
||||
self._getdependencies(dep, data[pkg]['deps'], version, d, ud, optional=True)
|
||||
for dep, version in depsfound.iteritems():
|
||||
self._getdependencies(dep, data[pkg]['deps'], version, d, ud)
|
||||
|
||||
def _getshrinkeddependencies(self, pkg, data, version, d, ud, lockdown, manifest, toplevel=True):
|
||||
def _getshrinkeddependencies(self, pkg, data, version, d, ud, lockdown, manifest):
|
||||
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}
|
||||
@@ -230,7 +211,7 @@ class Npm(FetchMethod):
|
||||
outputurl = runfetchcmd(fetchcmd, d, True)
|
||||
else:
|
||||
outputurl = data['resolved']
|
||||
self._runwget(ud, d, "%s --directory-prefix=%s %s" % (self.basecmd, ud.prefixdir, outputurl), False)
|
||||
self._runwget(ud, d, "%s %s" % (self.basecmd, outputurl), False)
|
||||
manifest[pkg] = {}
|
||||
manifest[pkg]['tgz'] = os.path.basename(outputurl).rstrip()
|
||||
manifest[pkg]['deps'] = {}
|
||||
@@ -247,7 +228,7 @@ class Npm(FetchMethod):
|
||||
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)
|
||||
self._getshrinkeddependencies(obj, data['dependencies'][obj], data['dependencies'][obj]['version'], d, ud, lockdown, manifest[pkg]['deps'])
|
||||
|
||||
def download(self, ud, d):
|
||||
"""Fetch url"""
|
||||
@@ -256,31 +237,28 @@ class Npm(FetchMethod):
|
||||
lockdown = {}
|
||||
|
||||
if not os.listdir(ud.pkgdatadir) and os.path.exists(ud.fullmirror):
|
||||
dest = d.getVar("DL_DIR")
|
||||
dest = d.getVar("DL_DIR", True)
|
||||
bb.utils.mkdirhier(dest)
|
||||
runfetchcmd("tar -xJf %s" % (ud.fullmirror), d, workdir=dest)
|
||||
save_cwd = os.getcwd()
|
||||
os.chdir(dest)
|
||||
runfetchcmd("tar -xJf %s" % (ud.fullmirror), d)
|
||||
os.chdir(save_cwd)
|
||||
return
|
||||
|
||||
shwrf = d.getVar('NPM_SHRINKWRAP')
|
||||
shwrf = d.getVar('NPM_SHRINKWRAP', True)
|
||||
logger.debug(2, "NPM shrinkwrap file is %s" % shwrf)
|
||||
if shwrf:
|
||||
try:
|
||||
with open(shwrf) as datafile:
|
||||
shrinkobj = json.load(datafile)
|
||||
except Exception as e:
|
||||
raise FetchError('Error loading NPM_SHRINKWRAP file "%s" for %s: %s' % (shwrf, ud.pkgname, str(e)))
|
||||
elif not ud.ignore_checksums:
|
||||
logger.warning('Missing shrinkwrap file in NPM_SHRINKWRAP for %s, this will lead to unreliable builds!' % ud.pkgname)
|
||||
lckdf = d.getVar('NPM_LOCKDOWN')
|
||||
try:
|
||||
with open(shwrf) as datafile:
|
||||
shrinkobj = json.load(datafile)
|
||||
except:
|
||||
logger.warn('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)
|
||||
if lckdf:
|
||||
try:
|
||||
with open(lckdf) as datafile:
|
||||
lockdown = json.load(datafile)
|
||||
except Exception as e:
|
||||
raise FetchError('Error loading NPM_LOCKDOWN file "%s" for %s: %s' % (lckdf, ud.pkgname, str(e)))
|
||||
elif not ud.ignore_checksums:
|
||||
logger.warning('Missing lockdown file in NPM_LOCKDOWN for %s, this will lead to unreproducible builds!' % ud.pkgname)
|
||||
try:
|
||||
with open(lckdf) as datafile:
|
||||
lockdown = json.load(datafile)
|
||||
except:
|
||||
logger.warn('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)
|
||||
@@ -297,8 +275,10 @@ class Npm(FetchMethod):
|
||||
if os.path.islink(ud.fullmirror):
|
||||
os.unlink(ud.fullmirror)
|
||||
|
||||
dldir = d.getVar("DL_DIR")
|
||||
save_cwd = os.getcwd()
|
||||
os.chdir(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)
|
||||
runfetchcmd("tar -cJf %s npm/%s npm/%s" % (ud.fullmirror, ud.bbnpmmanifest, ud.pkgname), d)
|
||||
runfetchcmd("touch %s.done" % (ud.fullmirror), d)
|
||||
os.chdir(save_cwd)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import MissingParameterError
|
||||
@@ -33,7 +34,7 @@ class Osc(FetchMethod):
|
||||
|
||||
# Create paths to osc checkouts
|
||||
relpath = self._strip_leading_slashes(ud.path)
|
||||
ud.pkgdir = os.path.join(d.getVar('OSCDIR'), ud.host)
|
||||
ud.pkgdir = os.path.join(d.getVar('OSCDIR', True), ud.host)
|
||||
ud.moddir = os.path.join(ud.pkgdir, relpath, ud.module)
|
||||
|
||||
if 'rev' in ud.parm:
|
||||
@@ -46,7 +47,7 @@ class Osc(FetchMethod):
|
||||
else:
|
||||
ud.revision = ""
|
||||
|
||||
ud.localfile = d.expand('%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.path.replace('/', '.'), ud.revision))
|
||||
ud.localfile = data.expand('%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.path.replace('/', '.'), ud.revision), d)
|
||||
|
||||
def _buildosccommand(self, ud, d, command):
|
||||
"""
|
||||
@@ -54,7 +55,7 @@ class Osc(FetchMethod):
|
||||
command is "fetch", "update", "info"
|
||||
"""
|
||||
|
||||
basecmd = d.expand('${FETCHCMD_osc}')
|
||||
basecmd = data.expand('${FETCHCMD_osc}', d)
|
||||
|
||||
proto = ud.parm.get('protocol', 'ocs')
|
||||
|
||||
@@ -83,25 +84,27 @@ class Osc(FetchMethod):
|
||||
|
||||
logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
|
||||
|
||||
if os.access(os.path.join(d.getVar('OSCDIR'), ud.path, ud.module), os.R_OK):
|
||||
if os.access(os.path.join(d.getVar('OSCDIR', True), ud.path, ud.module), os.R_OK):
|
||||
oscupdatecmd = self._buildosccommand(ud, d, "update")
|
||||
logger.info("Update "+ ud.url)
|
||||
# update sources there
|
||||
os.chdir(ud.moddir)
|
||||
logger.debug(1, "Running %s", oscupdatecmd)
|
||||
bb.fetch2.check_network_access(d, oscupdatecmd, ud.url)
|
||||
runfetchcmd(oscupdatecmd, d, workdir=ud.moddir)
|
||||
runfetchcmd(oscupdatecmd, d)
|
||||
else:
|
||||
oscfetchcmd = self._buildosccommand(ud, d, "fetch")
|
||||
logger.info("Fetch " + ud.url)
|
||||
# check out sources there
|
||||
bb.utils.mkdirhier(ud.pkgdir)
|
||||
os.chdir(ud.pkgdir)
|
||||
logger.debug(1, "Running %s", oscfetchcmd)
|
||||
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
|
||||
runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d,
|
||||
cleanup=[ud.localpath], workdir=os.path.join(ud.pkgdir + ud.path))
|
||||
runfetchcmd("tar -czf %s %s" % (ud.localpath, ud.module), d, cleanup = [ud.localpath])
|
||||
|
||||
def supports_srcrev(self):
|
||||
return False
|
||||
@@ -111,7 +114,7 @@ class Osc(FetchMethod):
|
||||
Generate a .oscrc to be used for this run.
|
||||
"""
|
||||
|
||||
config_path = os.path.join(d.getVar('OSCDIR'), "oscrc")
|
||||
config_path = os.path.join(d.getVar('OSCDIR', True), "oscrc")
|
||||
if (os.path.exists(config_path)):
|
||||
os.remove(config_path)
|
||||
|
||||
@@ -120,8 +123,8 @@ class Osc(FetchMethod):
|
||||
f.write("apisrv = %s\n" % ud.host)
|
||||
f.write("scheme = http\n")
|
||||
f.write("su-wrapper = su -c\n")
|
||||
f.write("build-root = %s\n" % d.getVar('WORKDIR'))
|
||||
f.write("urllist = %s\n" % d.getVar("OSCURLLIST"))
|
||||
f.write("build-root = %s\n" % d.getVar('WORKDIR', True))
|
||||
f.write("urllist = %s\n" % d.getVar("OSCURLLIST", True))
|
||||
f.write("extra-pkgs = gzip\n")
|
||||
f.write("\n")
|
||||
f.write("[%s]\n" % ud.host)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- 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) 2016 Kodak Alaris, 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
|
||||
@@ -23,187 +25,163 @@ BitBake 'Fetch' implementation for perforce
|
||||
#
|
||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||
|
||||
from future_builtins import zip
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import logger
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
class Perforce(FetchMethod):
|
||||
""" Class to fetch from perforce repositories """
|
||||
def supports(self, ud, d):
|
||||
""" Check to see if a given url can be fetched with perforce. """
|
||||
return ud.type in ['p4']
|
||||
|
||||
def urldata_init(self, ud, d):
|
||||
"""
|
||||
Initialize perforce specific variables within url data. If P4CONFIG is
|
||||
provided by the env, use it. If P4PORT is specified by the recipe, use
|
||||
its values, which may override the settings in P4CONFIG.
|
||||
"""
|
||||
ud.basecmd = d.getVar('FETCHCMD_p4')
|
||||
if not ud.basecmd:
|
||||
ud.basecmd = "/usr/bin/env p4"
|
||||
|
||||
ud.dldir = d.getVar('P4DIR')
|
||||
if not ud.dldir:
|
||||
ud.dldir = '%s/%s' % (d.getVar('DL_DIR'), 'p4')
|
||||
|
||||
path = ud.url.split('://')[1]
|
||||
path = path.split(';')[0]
|
||||
delim = path.find('@');
|
||||
def doparse(url, d):
|
||||
parm = {}
|
||||
path = url.split("://")[1]
|
||||
delim = path.find("@");
|
||||
if delim != -1:
|
||||
(ud.user, ud.pswd) = path.split('@')[0].split(':')
|
||||
ud.path = path.split('@')[1]
|
||||
(user, pswd, host, port) = path.split('@')[0].split(":")
|
||||
path = path.split('@')[1]
|
||||
else:
|
||||
ud.path = path
|
||||
(host, port) = d.getVar('P4PORT', False).split(':')
|
||||
user = ""
|
||||
pswd = ""
|
||||
|
||||
ud.usingp4config = False
|
||||
p4port = d.getVar('P4PORT')
|
||||
if path.find(";") != -1:
|
||||
keys=[]
|
||||
values=[]
|
||||
plist = path.split(';')
|
||||
for item in plist:
|
||||
if item.count('='):
|
||||
(key, value) = item.split('=')
|
||||
keys.append(key)
|
||||
values.append(value)
|
||||
|
||||
if p4port:
|
||||
logger.debug(1, 'Using recipe provided P4PORT: %s' % p4port)
|
||||
ud.host = p4port
|
||||
else:
|
||||
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.url)
|
||||
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
|
||||
parm = dict(zip(keys, values))
|
||||
path = "//" + path.split(';')[0]
|
||||
host += ":%s" % (port)
|
||||
parm["cset"] = Perforce.getcset(d, path, host, user, pswd, parm)
|
||||
|
||||
cleanedpath = ud.path.replace('/...', '').replace('/', '.')
|
||||
cleanedhost = ud.host.replace(':', '.')
|
||||
ud.pkgdir = os.path.join(ud.dldir, cleanedhost, cleanedpath)
|
||||
return host, path, user, pswd, parm
|
||||
doparse = staticmethod(doparse)
|
||||
|
||||
ud.setup_revisions(d)
|
||||
|
||||
ud.localfile = d.expand('%s_%s_%s.tar.gz' % (cleanedhost, cleanedpath, ud.revision))
|
||||
|
||||
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.
|
||||
"""
|
||||
def getcset(d, depot, host, user, pswd, parm):
|
||||
p4opt = ""
|
||||
if "cset" in parm:
|
||||
return parm["cset"];
|
||||
if user:
|
||||
p4opt += " -u %s" % (user)
|
||||
if pswd:
|
||||
p4opt += " -P %s" % (pswd)
|
||||
if host:
|
||||
p4opt += " -p %s" % (host)
|
||||
|
||||
if ud.user:
|
||||
p4opt += ' -u "%s"' % (ud.user)
|
||||
p4date = d.getVar("P4DATE", True)
|
||||
if "revision" in parm:
|
||||
depot += "#%s" % (parm["revision"])
|
||||
elif "label" in parm:
|
||||
depot += "@%s" % (parm["label"])
|
||||
elif p4date:
|
||||
depot += "@%s" % (p4date)
|
||||
|
||||
if ud.pswd:
|
||||
p4opt += ' -P "%s"' % (ud.pswd)
|
||||
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
|
||||
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:
|
||||
p4opt += ' -p %s' % (ud.host)
|
||||
return cset.split(' ')[1]
|
||||
getcset = staticmethod(getcset)
|
||||
|
||||
if hasattr(ud, 'revision') and ud.revision:
|
||||
pathnrev = '%s@%s' % (ud.path, ud.revision)
|
||||
def urldata_init(self, ud, d):
|
||||
(host, path, user, pswd, parm) = Perforce.doparse(ud.url, d)
|
||||
|
||||
base_path = path.replace('/...', '')
|
||||
base_path = self._strip_leading_slashes(base_path)
|
||||
|
||||
if "label" in parm:
|
||||
version = parm["label"]
|
||||
else:
|
||||
pathnrev = '%s' % (ud.path)
|
||||
version = Perforce.getcset(d, path, host, user, pswd, parm)
|
||||
|
||||
if depot_filename:
|
||||
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, ud.url)
|
||||
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
|
||||
ud.localfile = data.expand('%s+%s+%s.tar.gz' % (host, base_path.replace('/', '.'), version), d)
|
||||
|
||||
def download(self, ud, d):
|
||||
""" Get the list of files, fetch each one """
|
||||
filelist = self._p4listfiles(ud, d)
|
||||
if not filelist:
|
||||
raise FetchError('No files found in depot %s@%s' % (ud.host, ud.path))
|
||||
"""
|
||||
Fetch urls
|
||||
"""
|
||||
|
||||
bb.utils.remove(ud.pkgdir, True)
|
||||
bb.utils.mkdirhier(ud.pkgdir)
|
||||
(host, depot, user, pswd, parm) = Perforce.doparse(ud.url, d)
|
||||
|
||||
for afile in filelist:
|
||||
p4fetchcmd = self._buildp4command(ud, d, 'print', afile)
|
||||
bb.fetch2.check_network_access(d, p4fetchcmd, ud.url)
|
||||
runfetchcmd(p4fetchcmd, d, workdir=ud.pkgdir)
|
||||
if depot.find('/...') != -1:
|
||||
path = depot[:depot.find('/...')]
|
||||
else:
|
||||
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):
|
||||
""" Cleanup p4 specific files and dirs"""
|
||||
bb.utils.remove(ud.localpath)
|
||||
bb.utils.remove(ud.pkgdir, True)
|
||||
# Get the p4 command
|
||||
p4opt = ""
|
||||
if user:
|
||||
p4opt += " -u %s" % (user)
|
||||
|
||||
def supports_srcrev(self):
|
||||
return True
|
||||
if pswd:
|
||||
p4opt += " -P %s" % (pswd)
|
||||
|
||||
def _revision_key(self, ud, d, name):
|
||||
""" Return a unique key for the url """
|
||||
return 'p4:%s' % ud.pkgdir
|
||||
if host:
|
||||
p4opt += " -p %s" % (host)
|
||||
|
||||
def _latest_revision(self, ud, d, name):
|
||||
""" Return the latest upstream scm revision number """
|
||||
p4cmd = self._buildp4command(ud, d, "changes")
|
||||
bb.fetch2.check_network_access(d, p4cmd, ud.url)
|
||||
tip = runfetchcmd(p4cmd, d, True)
|
||||
p4cmd = d.getVar('FETCHCMD_p4', True) or "p4"
|
||||
|
||||
if not tip:
|
||||
raise FetchError('Could not determine the latest perforce changelist')
|
||||
# create temp directory
|
||||
logger.debug(2, "Fetch: creating temporary directory")
|
||||
bb.utils.mkdirhier(d.expand('${WORKDIR}'))
|
||||
mktemp = d.getVar("FETCHCMD_p4mktemp", True) or d.expand("mktemp -d -q '${WORKDIR}/oep4.XXXXXX'")
|
||||
tmpfile, errors = bb.process.run(mktemp)
|
||||
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]
|
||||
logger.debug(1, 'p4 tip found to be changelist %s' % tipcset)
|
||||
return tipcset
|
||||
if "label" in parm:
|
||||
depot = "%s@%s" % (depot, parm["label"])
|
||||
else:
|
||||
cset = Perforce.getcset(d, depot, host, user, pswd, parm)
|
||||
depot = "%s@%s" % (depot, cset)
|
||||
|
||||
def sortable_revision(self, ud, d, name):
|
||||
""" Return a sortable revision number """
|
||||
return False, self._build_revision(ud, d)
|
||||
os.chdir(tmpfile)
|
||||
logger.info("Fetch " + ud.url)
|
||||
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):
|
||||
return ud.revision
|
||||
if not p4file:
|
||||
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)
|
||||
|
||||
@@ -25,6 +25,7 @@ BitBake "Fetch" repo (git) implementation
|
||||
|
||||
import os
|
||||
import bb
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
@@ -50,17 +51,17 @@ class Repo(FetchMethod):
|
||||
if not ud.manifest.endswith('.xml'):
|
||||
ud.manifest += '.xml'
|
||||
|
||||
ud.localfile = d.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch))
|
||||
ud.localfile = data.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch), d)
|
||||
|
||||
def download(self, ud, d):
|
||||
"""Fetch url"""
|
||||
|
||||
if os.access(os.path.join(d.getVar("DL_DIR"), ud.localfile), os.R_OK):
|
||||
if os.access(os.path.join(data.getVar("DL_DIR", d, True), ud.localfile), os.R_OK):
|
||||
logger.debug(1, "%s already exists (or was stashed). Skipping repo init / sync.", ud.localpath)
|
||||
return
|
||||
|
||||
gitsrcname = "%s%s" % (ud.host, ud.path.replace("/", "."))
|
||||
repodir = d.getVar("REPODIR") or os.path.join(d.getVar("DL_DIR"), "repo")
|
||||
repodir = data.getVar("REPODIR", d, True) or os.path.join(data.getVar("DL_DIR", d, True), "repo")
|
||||
codir = os.path.join(repodir, gitsrcname, ud.manifest)
|
||||
|
||||
if ud.user:
|
||||
@@ -68,23 +69,24 @@ class Repo(FetchMethod):
|
||||
else:
|
||||
username = ""
|
||||
|
||||
repodir = os.path.join(codir, "repo")
|
||||
bb.utils.mkdirhier(repodir)
|
||||
if not os.path.exists(os.path.join(repodir, ".repo")):
|
||||
bb.utils.mkdirhier(os.path.join(codir, "repo"))
|
||||
os.chdir(os.path.join(codir, "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)
|
||||
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)
|
||||
runfetchcmd("repo sync", d, workdir=repodir)
|
||||
runfetchcmd("repo sync", d)
|
||||
os.chdir(codir)
|
||||
|
||||
scmdata = ud.parm.get("scmdata", "")
|
||||
if scmdata == "keep":
|
||||
tar_flags = ""
|
||||
else:
|
||||
tar_flags = "--exclude='.repo' --exclude='.git'"
|
||||
tar_flags = "--exclude '.repo' --exclude '.git'"
|
||||
|
||||
# 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):
|
||||
return False
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
"""
|
||||
BitBake 'Fetch' implementation for Amazon AWS S3.
|
||||
|
||||
Class for fetching files from Amazon S3 using the AWS Command Line Interface.
|
||||
The aws tool must be correctly installed and configured prior to use.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (C) 2017, Andre McCurdy <armccurdy@gmail.com>
|
||||
#
|
||||
# Based in part on bb.fetch2.wget:
|
||||
# Copyright (C) 2003, 2004 Chris Larson
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||||
|
||||
import os
|
||||
import bb
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
class S3(FetchMethod):
|
||||
"""Class to fetch urls via 'aws s3'"""
|
||||
|
||||
def supports(self, ud, d):
|
||||
"""
|
||||
Check to see if a given url can be fetched with s3.
|
||||
"""
|
||||
return ud.type in ['s3']
|
||||
|
||||
def recommends_checksum(self, urldata):
|
||||
return True
|
||||
|
||||
def urldata_init(self, ud, d):
|
||||
if 'downloadfilename' in ud.parm:
|
||||
ud.basename = ud.parm['downloadfilename']
|
||||
else:
|
||||
ud.basename = os.path.basename(ud.path)
|
||||
|
||||
ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
|
||||
|
||||
ud.basecmd = d.getVar("FETCHCMD_s3") or "/usr/bin/env aws s3"
|
||||
|
||||
def download(self, ud, d):
|
||||
"""
|
||||
Fetch urls
|
||||
Assumes localpath was called first
|
||||
"""
|
||||
|
||||
cmd = '%s cp s3://%s%s %s' % (ud.basecmd, ud.host, ud.path, ud.localpath)
|
||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||
runfetchcmd(cmd, d)
|
||||
|
||||
# Additional sanity checks copied from the wget class (although there
|
||||
# are no known issues which mean these are required, treat the aws cli
|
||||
# tool with a little healthy suspicion).
|
||||
|
||||
if not os.path.exists(ud.localpath):
|
||||
raise FetchError("The aws cp command returned success for s3://%s%s but %s doesn't exist?!" % (ud.host, ud.path, ud.localpath))
|
||||
|
||||
if os.path.getsize(ud.localpath) == 0:
|
||||
os.remove(ud.localpath)
|
||||
raise FetchError("The aws cp command for s3://%s%s resulted in a zero size file?! Deleting and failing since this isn't right." % (ud.host, ud.path))
|
||||
|
||||
return True
|
||||
|
||||
def checkstatus(self, fetch, ud, d):
|
||||
"""
|
||||
Check the status of a URL
|
||||
"""
|
||||
|
||||
cmd = '%s ls s3://%s%s' % (ud.basecmd, ud.host, ud.path)
|
||||
bb.fetch2.check_network_access(d, cmd, ud.url)
|
||||
output = runfetchcmd(cmd, d)
|
||||
|
||||
# "aws s3 ls s3://mybucket/foo" will exit with success even if the file
|
||||
# is not found, so check output of the command to confirm success.
|
||||
|
||||
if not output:
|
||||
raise FetchError("The aws ls command for s3://%s%s gave empty output" % (ud.host, ud.path))
|
||||
|
||||
return True
|
||||
@@ -61,11 +61,14 @@ SRC_URI = "sftp://user@host.example.com/dir/path.file.txt"
|
||||
|
||||
import os
|
||||
import bb
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib
|
||||
import commands
|
||||
from bb import data
|
||||
from bb.fetch2 import URI
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import runfetchcmd
|
||||
|
||||
|
||||
class SFTP(FetchMethod):
|
||||
"""Class to fetch urls via 'sftp'"""
|
||||
|
||||
@@ -90,7 +93,7 @@ class SFTP(FetchMethod):
|
||||
else:
|
||||
ud.basename = os.path.basename(ud.path)
|
||||
|
||||
ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
|
||||
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
|
||||
|
||||
def download(self, ud, d):
|
||||
"""Fetch urls"""
|
||||
@@ -102,7 +105,7 @@ class SFTP(FetchMethod):
|
||||
port = '-P %d' % urlo.port
|
||||
urlo.port = None
|
||||
|
||||
dldir = d.getVar('DL_DIR')
|
||||
dldir = data.getVar('DL_DIR', d, True)
|
||||
lpath = os.path.join(dldir, ud.localfile)
|
||||
|
||||
user = ''
|
||||
@@ -118,7 +121,8 @@ class SFTP(FetchMethod):
|
||||
|
||||
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)
|
||||
runfetchcmd(cmd, d)
|
||||
|
||||
@@ -43,6 +43,7 @@ IETF secsh internet draft:
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import re, os
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import logger
|
||||
@@ -86,11 +87,11 @@ class SSH(FetchMethod):
|
||||
m = __pattern__.match(urldata.url)
|
||||
path = m.group('path')
|
||||
host = m.group('host')
|
||||
urldata.localpath = os.path.join(d.getVar('DL_DIR'),
|
||||
urldata.localpath = os.path.join(d.getVar('DL_DIR', True),
|
||||
os.path.basename(os.path.normpath(path)))
|
||||
|
||||
def download(self, urldata, d):
|
||||
dldir = d.getVar('DL_DIR')
|
||||
dldir = d.getVar('DL_DIR', True)
|
||||
|
||||
m = __pattern__.match(urldata.url)
|
||||
path = m.group('path')
|
||||
@@ -113,10 +114,12 @@ class SSH(FetchMethod):
|
||||
fr = host
|
||||
fr += ':%s' % path
|
||||
|
||||
|
||||
import commands
|
||||
cmd = 'scp -B -r %s %s %s/' % (
|
||||
portarg,
|
||||
fr,
|
||||
dldir
|
||||
commands.mkarg(fr),
|
||||
commands.mkarg(dldir)
|
||||
)
|
||||
|
||||
bb.fetch2.check_network_access(d, cmd, urldata.url)
|
||||
|
||||
@@ -28,6 +28,7 @@ import sys
|
||||
import logging
|
||||
import bb
|
||||
import re
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import MissingParameterError
|
||||
@@ -49,7 +50,7 @@ class Svn(FetchMethod):
|
||||
if not "module" in ud.parm:
|
||||
raise MissingParameterError('module', ud.url)
|
||||
|
||||
ud.basecmd = d.getVar('FETCHCMD_svn')
|
||||
ud.basecmd = d.getVar('FETCHCMD_svn', True)
|
||||
|
||||
ud.module = ud.parm["module"]
|
||||
|
||||
@@ -60,15 +61,15 @@ class Svn(FetchMethod):
|
||||
|
||||
# Create paths to svn checkouts
|
||||
relpath = self._strip_leading_slashes(ud.path)
|
||||
ud.pkgdir = os.path.join(d.expand('${SVNDIR}'), ud.host, relpath)
|
||||
ud.pkgdir = os.path.join(data.expand('${SVNDIR}', d), ud.host, relpath)
|
||||
ud.moddir = os.path.join(ud.pkgdir, ud.module)
|
||||
|
||||
ud.setup_revisions(d)
|
||||
ud.setup_revisons(d)
|
||||
|
||||
if 'rev' in ud.parm:
|
||||
ud.revision = ud.parm['rev']
|
||||
|
||||
ud.localfile = d.expand('%s_%s_%s_%s_.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision))
|
||||
ud.localfile = data.expand('%s_%s_%s_%s_.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision), d)
|
||||
|
||||
def _buildsvncommand(self, ud, d, command):
|
||||
"""
|
||||
@@ -78,9 +79,9 @@ class Svn(FetchMethod):
|
||||
|
||||
proto = ud.parm.get('protocol', 'svn')
|
||||
|
||||
svn_ssh = None
|
||||
if proto == "svn+ssh" and "ssh" in ud.parm:
|
||||
svn_ssh = ud.parm["ssh"]
|
||||
svn_rsh = None
|
||||
if proto == "svn+ssh" and "rsh" in ud.parm:
|
||||
svn_rsh = ud.parm["rsh"]
|
||||
|
||||
svnroot = ud.host + ud.path
|
||||
|
||||
@@ -112,8 +113,8 @@ class Svn(FetchMethod):
|
||||
else:
|
||||
raise FetchError("Invalid svn command %s" % command, ud.url)
|
||||
|
||||
if svn_ssh:
|
||||
svncmd = "SVN_SSH=\"%s\" %s" % (svn_ssh, svncmd)
|
||||
if svn_rsh:
|
||||
svncmd = "svn_RSH=\"%s\" %s" % (svn_rsh, svncmd)
|
||||
|
||||
return svncmd
|
||||
|
||||
@@ -125,32 +126,35 @@ class Svn(FetchMethod):
|
||||
if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK):
|
||||
svnupdatecmd = self._buildsvncommand(ud, d, "update")
|
||||
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
|
||||
try:
|
||||
runfetchcmd(ud.basecmd + " upgrade", d, workdir=ud.moddir)
|
||||
runfetchcmd(ud.basecmd + " upgrade", d)
|
||||
except FetchError:
|
||||
pass
|
||||
logger.debug(1, "Running %s", svnupdatecmd)
|
||||
bb.fetch2.check_network_access(d, svnupdatecmd, ud.url)
|
||||
runfetchcmd(svnupdatecmd, d, workdir=ud.moddir)
|
||||
runfetchcmd(svnupdatecmd, d)
|
||||
else:
|
||||
svnfetchcmd = self._buildsvncommand(ud, d, "fetch")
|
||||
logger.info("Fetch " + ud.url)
|
||||
# check out sources there
|
||||
bb.utils.mkdirhier(ud.pkgdir)
|
||||
os.chdir(ud.pkgdir)
|
||||
logger.debug(1, "Running %s", svnfetchcmd)
|
||||
bb.fetch2.check_network_access(d, svnfetchcmd, ud.url)
|
||||
runfetchcmd(svnfetchcmd, d, workdir=ud.pkgdir)
|
||||
runfetchcmd(svnfetchcmd, d)
|
||||
|
||||
scmdata = ud.parm.get("scmdata", "")
|
||||
if scmdata == "keep":
|
||||
tar_flags = ""
|
||||
else:
|
||||
tar_flags = "--exclude='.svn'"
|
||||
tar_flags = "--exclude '.svn'"
|
||||
|
||||
os.chdir(ud.pkgdir)
|
||||
# tar them up to a defined filename
|
||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d,
|
||||
cleanup=[ud.localpath], workdir=ud.pkgdir)
|
||||
runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d, cleanup = [ud.localpath])
|
||||
|
||||
def clean(self, ud, d):
|
||||
""" Clean SVN specific files and dirs """
|
||||
@@ -172,7 +176,7 @@ class Svn(FetchMethod):
|
||||
"""
|
||||
Return the latest upstream revision number
|
||||
"""
|
||||
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"), ud.url)
|
||||
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"))
|
||||
|
||||
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True)
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ import subprocess
|
||||
import os
|
||||
import logging
|
||||
import bb
|
||||
import bb.progress
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
from bb.fetch2 import logger
|
||||
@@ -41,27 +41,6 @@ from bb.utils import export_proxies
|
||||
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 to fetch urls via 'wget'"""
|
||||
def supports(self, ud, d):
|
||||
@@ -83,19 +62,17 @@ class Wget(FetchMethod):
|
||||
else:
|
||||
ud.basename = os.path.basename(ud.path)
|
||||
|
||||
ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
|
||||
ud.localfile = data.expand(urllib.unquote(ud.basename), d)
|
||||
if not ud.localfile:
|
||||
ud.localfile = d.expand(urllib.parse.unquote(ud.host + ud.path).replace("/", "."))
|
||||
ud.localfile = data.expand(urllib.unquote(ud.host + ud.path).replace("/", "."), d)
|
||||
|
||||
self.basecmd = d.getVar("FETCHCMD_wget") 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):
|
||||
|
||||
progresshandler = WgetProgressHandler(d)
|
||||
|
||||
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
|
||||
bb.fetch2.check_network_access(d, command, ud.url)
|
||||
runfetchcmd(command + ' --progress=dot -v', d, quiet, log=progresshandler)
|
||||
bb.fetch2.check_network_access(d, command)
|
||||
runfetchcmd(command, d, quiet)
|
||||
|
||||
def download(self, ud, d):
|
||||
"""Fetch urls"""
|
||||
@@ -103,13 +80,10 @@ class Wget(FetchMethod):
|
||||
fetchcmd = self.basecmd
|
||||
|
||||
if 'downloadfilename' in ud.parm:
|
||||
dldir = d.getVar("DL_DIR")
|
||||
dldir = d.getVar("DL_DIR", True)
|
||||
bb.utils.mkdirhier(os.path.dirname(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]
|
||||
if os.path.exists(ud.localpath):
|
||||
# file exists, but we didnt complete it.. trying again..
|
||||
@@ -131,11 +105,11 @@ class Wget(FetchMethod):
|
||||
return True
|
||||
|
||||
def checkstatus(self, fetch, ud, d, try_again=True):
|
||||
import urllib.request, urllib.error, urllib.parse, socket, http.client
|
||||
from urllib.response import addinfourl
|
||||
import urllib2, socket, httplib
|
||||
from urllib import addinfourl
|
||||
from bb.fetch2 import FetchConnectionCache
|
||||
|
||||
class HTTPConnectionCache(http.client.HTTPConnection):
|
||||
class HTTPConnectionCache(httplib.HTTPConnection):
|
||||
if fetch.connection_cache:
|
||||
def connect(self):
|
||||
"""Connect to the host and port specified in __init__."""
|
||||
@@ -151,7 +125,7 @@ class Wget(FetchMethod):
|
||||
if self._tunnel_host:
|
||||
self._tunnel()
|
||||
|
||||
class CacheHTTPHandler(urllib.request.HTTPHandler):
|
||||
class CacheHTTPHandler(urllib2.HTTPHandler):
|
||||
def http_open(self, req):
|
||||
return self.do_open(HTTPConnectionCache, req)
|
||||
|
||||
@@ -165,7 +139,7 @@ class Wget(FetchMethod):
|
||||
- geturl(): return the original request URL
|
||||
- code: HTTP status code
|
||||
"""
|
||||
host = req.host
|
||||
host = req.get_host()
|
||||
if not host:
|
||||
raise urlllib2.URLError('no host given')
|
||||
|
||||
@@ -173,7 +147,7 @@ class Wget(FetchMethod):
|
||||
h.set_debuglevel(self._debuglevel)
|
||||
|
||||
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))
|
||||
|
||||
# We want to make an HTTP/1.1 request, but the addinfourl
|
||||
@@ -190,7 +164,7 @@ class Wget(FetchMethod):
|
||||
headers["Connection"] = "Keep-Alive" # Works for HTTP/1.0
|
||||
|
||||
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:
|
||||
tunnel_headers = {}
|
||||
@@ -203,12 +177,12 @@ class Wget(FetchMethod):
|
||||
h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
|
||||
|
||||
try:
|
||||
h.request(req.get_method(), req.selector, req.data, headers)
|
||||
except socket.error as err: # XXX what error?
|
||||
h.request(req.get_method(), req.get_selector(), req.data, headers)
|
||||
except socket.error, err: # XXX what error?
|
||||
# Don't close connection when cache is enabled.
|
||||
if fetch.connection_cache is None:
|
||||
h.close()
|
||||
raise urllib.error.URLError(err)
|
||||
raise urllib2.URLError(err)
|
||||
else:
|
||||
try:
|
||||
r = h.getresponse(buffering=True)
|
||||
@@ -248,7 +222,7 @@ class Wget(FetchMethod):
|
||||
|
||||
return resp
|
||||
|
||||
class HTTPMethodFallback(urllib.request.BaseHandler):
|
||||
class HTTPMethodFallback(urllib2.BaseHandler):
|
||||
"""
|
||||
Fallback to GET if HEAD is not allowed (405 HTTP error)
|
||||
"""
|
||||
@@ -256,11 +230,11 @@ class Wget(FetchMethod):
|
||||
fp.read()
|
||||
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"))
|
||||
return self.parent.open(urllib.request.Request(req.get_full_url(),
|
||||
return self.parent.open(urllib2.Request(req.get_full_url(),
|
||||
headers=newheaders,
|
||||
origin_req_host=req.origin_req_host,
|
||||
origin_req_host=req.get_origin_req_host(),
|
||||
unverifiable=True))
|
||||
|
||||
"""
|
||||
@@ -275,54 +249,35 @@ class Wget(FetchMethod):
|
||||
"""
|
||||
http_error_406 = http_error_405
|
||||
|
||||
class FixedHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
|
||||
class FixedHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
"""
|
||||
urllib2.HTTPRedirectHandler resets the method to GET on redirect,
|
||||
when we want to follow redirects using the original method.
|
||||
"""
|
||||
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()
|
||||
return newreq
|
||||
exported_proxies = export_proxies(d)
|
||||
|
||||
handlers = [FixedHTTPRedirectHandler, HTTPMethodFallback]
|
||||
if export_proxies:
|
||||
handlers.append(urllib.request.ProxyHandler())
|
||||
handlers.append(urllib2.ProxyHandler())
|
||||
handlers.append(CacheHTTPHandler())
|
||||
# XXX: Since Python 2.7.9 ssl cert validation is enabled by default
|
||||
# see PEP-0476, this causes verification errors on some https servers
|
||||
# so disable by default.
|
||||
import ssl
|
||||
if hasattr(ssl, '_create_unverified_context'):
|
||||
handlers.append(urllib.request.HTTPSHandler(context=ssl._create_unverified_context()))
|
||||
opener = urllib.request.build_opener(*handlers)
|
||||
handlers.append(urllib2.HTTPSHandler(context=ssl._create_unverified_context()))
|
||||
opener = urllib2.build_opener(*handlers)
|
||||
|
||||
try:
|
||||
uri = ud.url.split(";")[0]
|
||||
r = urllib.request.Request(uri)
|
||||
r = urllib2.Request(uri)
|
||||
r.get_method = lambda: "HEAD"
|
||||
|
||||
def add_basic_auth(login_str, request):
|
||||
'''Adds Basic auth to http request, pass in login:password as string'''
|
||||
import base64
|
||||
encodeuser = base64.b64encode(login_str.encode('utf-8')).decode("utf-8")
|
||||
authheader = "Basic %s" % encodeuser
|
||||
r.add_header("Authorization", authheader)
|
||||
|
||||
if ud.user:
|
||||
add_basic_auth(ud.user, r)
|
||||
|
||||
try:
|
||||
import netrc, urllib.parse
|
||||
n = netrc.netrc()
|
||||
login, unused, password = n.authenticators(urllib.parse.urlparse(uri).hostname)
|
||||
add_basic_auth("%s:%s" % (login, password), r)
|
||||
except (TypeError, ImportError, IOError, netrc.NetrcParseError):
|
||||
pass
|
||||
|
||||
opener.open(r)
|
||||
except urllib.error.URLError as e:
|
||||
except urllib2.URLError as e:
|
||||
if try_again:
|
||||
logger.debug(2, "checkstatus: trying again")
|
||||
return self.checkstatus(fetch, ud, d, False)
|
||||
@@ -545,7 +500,7 @@ class Wget(FetchMethod):
|
||||
|
||||
# src.rpm extension was added only for rpm package. Can be removed if the rpm
|
||||
# packaged will always be considered as having to be manually upgraded
|
||||
psuffix_regex = "(tar\.gz|tgz|tar\.bz2|zip|xz|tar\.lz|rpm|bz2|orig\.tar\.gz|tar\.xz|src\.tar\.gz|src\.tgz|svnr\d+\.tar\.bz2|stable\.tar\.gz|src\.rpm)"
|
||||
psuffix_regex = "(tar\.gz|tgz|tar\.bz2|zip|xz|rpm|bz2|orig\.tar\.gz|tar\.xz|src\.tar\.gz|src\.tgz|svnr\d+\.tar\.bz2|stable\.tar\.gz|src\.rpm)"
|
||||
|
||||
# match name, version and archive type of a package
|
||||
package_regex_comp = re.compile("(?P<name>%s?\.?v?)(?P<pver>%s)(?P<arch>%s)?[\.-](?P<type>%s$)"
|
||||
@@ -553,7 +508,7 @@ class Wget(FetchMethod):
|
||||
self.suffix_regex_comp = re.compile(psuffix_regex)
|
||||
|
||||
# compile regex, can be specific by package or generic regex
|
||||
pn_regex = d.getVar('UPSTREAM_CHECK_REGEX')
|
||||
pn_regex = d.getVar('UPSTREAM_CHECK_REGEX', True)
|
||||
if pn_regex:
|
||||
package_custom_regex_comp = re.compile(pn_regex)
|
||||
else:
|
||||
@@ -574,7 +529,7 @@ class Wget(FetchMethod):
|
||||
sanity check to ensure same name and type.
|
||||
"""
|
||||
package = ud.path.split("/")[-1]
|
||||
current_version = ['', d.getVar('PV'), '']
|
||||
current_version = ['', d.getVar('PV', True), '']
|
||||
|
||||
"""possible to have no version in pkg name, such as spectrum-fw"""
|
||||
if not re.search("\d+", package):
|
||||
@@ -589,7 +544,7 @@ class Wget(FetchMethod):
|
||||
bb.debug(3, "latest_versionstring, regex: %s" % (package_regex.pattern))
|
||||
|
||||
uri = ""
|
||||
regex_uri = d.getVar("UPSTREAM_CHECK_URI")
|
||||
regex_uri = d.getVar("UPSTREAM_CHECK_URI", True)
|
||||
if not regex_uri:
|
||||
path = ud.path.split(package)[0]
|
||||
|
||||
@@ -598,7 +553,7 @@ class Wget(FetchMethod):
|
||||
dirver_regex = re.compile("(?P<dirver>[^/]*(\d+\.)*\d+([-_]r\d+)*)/")
|
||||
m = dirver_regex.search(path)
|
||||
if m:
|
||||
pn = d.getVar('PN')
|
||||
pn = d.getVar('PN', True)
|
||||
dirver = m.group('dirver')
|
||||
|
||||
dirver_pn_regex = re.compile("%s\d?" % (re.escape(pn)))
|
||||
|
||||
@@ -27,7 +27,6 @@ import sys
|
||||
import logging
|
||||
import optparse
|
||||
import warnings
|
||||
import fcntl
|
||||
|
||||
import bb
|
||||
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
|
||||
# suggest a fixed set this allows you to have flexibility in which
|
||||
# ones are available.
|
||||
module = __import__(pkg.__name__, fromlist=[modulename])
|
||||
module = __import__(pkg.__name__, fromlist = [modulename])
|
||||
return getattr(module, modulename)
|
||||
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__, modules))
|
||||
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))))
|
||||
|
||||
|
||||
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
|
||||
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)
|
||||
else:
|
||||
s = warnings.formatwarning(message, category, filename, lineno)
|
||||
warnlog.warning(s)
|
||||
warnlog.warn(s)
|
||||
|
||||
warnings.showwarning = _showwarning
|
||||
warnings.filterwarnings("ignore")
|
||||
@@ -131,203 +129,136 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||
|
||||
def parseCommandLine(self, argv=sys.argv):
|
||||
parser = optparse.OptionParser(
|
||||
formatter=BitbakeHelpFormatter(),
|
||||
version="BitBake Build Tool Core version %s" % bb.__version__,
|
||||
usage="""%prog [options] [recipename/target recipe:do_task ...]
|
||||
formatter = BitbakeHelpFormatter(),
|
||||
version = "BitBake Build Tool Core version %s" % bb.__version__,
|
||||
usage = """%prog [options] [recipename/target recipe:do_task ...]
|
||||
|
||||
Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
|
||||
It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
|
||||
will provide the layer, BBFILES and other configuration information.""")
|
||||
|
||||
parser.add_option("-b", "--buildfile", action="store", dest="buildfile", default=None,
|
||||
help="Execute tasks from a specific .bb recipe directly. WARNING: Does "
|
||||
"not handle any dependencies from other recipes.")
|
||||
parser.add_option("-b", "--buildfile", help = "Execute tasks from a specific .bb recipe directly. WARNING: Does not handle any dependencies from other recipes.",
|
||||
action = "store", dest = "buildfile", default = None)
|
||||
|
||||
parser.add_option("-k", "--continue", action="store_false", dest="abort", default=True,
|
||||
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.")
|
||||
parser.add_option("-k", "--continue", help = "Continue as much as possible after an error. While the target that failed and anything depending on it cannot be built, as much as possible will be built before stopping.",
|
||||
action = "store_false", dest = "abort", default = True)
|
||||
|
||||
parser.add_option("-a", "--tryaltconfigs", action="store_true",
|
||||
dest="tryaltconfigs", default=False,
|
||||
help="Continue with builds by trying to use alternative providers "
|
||||
"where possible.")
|
||||
parser.add_option("-a", "--tryaltconfigs", help = "Continue with builds by trying to use alternative providers where possible.",
|
||||
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||
|
||||
parser.add_option("-f", "--force", action="store_true", dest="force", default=False,
|
||||
help="Force the specified targets/task to run (invalidating any "
|
||||
"existing stamp file).")
|
||||
parser.add_option("-f", "--force", help = "Force the specified targets/task to run (invalidating any existing stamp file).",
|
||||
action = "store_true", dest = "force", default = False)
|
||||
|
||||
parser.add_option("-c", "--cmd", action="store", dest="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.")
|
||||
parser.add_option("-c", "--cmd", help = "Specify the task to execute. The exact options available depend on the metadata. Some examples might be 'compile' or 'populate_sysroot' or 'listtasks' may give a list of the tasks available.",
|
||||
action = "store", dest = "cmd")
|
||||
|
||||
parser.add_option("-C", "--clear-stamp", action="store", dest="invalidate_stamp",
|
||||
help="Invalidate the stamp for the specified task such as 'compile' "
|
||||
"and then run the default task for the specified target(s).")
|
||||
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified task such as 'compile' and then run the default task for the specified target(s).",
|
||||
action = "store", dest = "invalidate_stamp")
|
||||
|
||||
parser.add_option("-r", "--read", action="append", dest="prefile", default=[],
|
||||
help="Read the specified file before bitbake.conf.")
|
||||
parser.add_option("-r", "--read", help = "Read the specified file before bitbake.conf.",
|
||||
action = "append", dest = "prefile", default = [])
|
||||
|
||||
parser.add_option("-R", "--postread", action="append", dest="postfile", default=[],
|
||||
help="Read the specified file after bitbake.conf.")
|
||||
parser.add_option("-R", "--postread", 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,
|
||||
help="Enable tracing of shell tasks (with 'set -x'). "
|
||||
"Also print bb.note(...) messages to stdout (in "
|
||||
"addition to writing them to ${T}/log.do_<task>).")
|
||||
parser.add_option("-v", "--verbose", help = "Output more log message data to the terminal.",
|
||||
action = "store_true", dest = "verbose", default = False)
|
||||
|
||||
parser.add_option("-D", "--debug", action="count", dest="debug", default=0,
|
||||
help="Increase the debug level. You can specify this "
|
||||
"more than once. -D sets the debug level to 1, "
|
||||
"where only bb.debug(1, ...) messages are printed "
|
||||
"to stdout; -DD sets the debug level to 2, where "
|
||||
"both bb.debug(1, ...) and bb.debug(2, ...) "
|
||||
"messages are printed; etc. Without -D, no debug "
|
||||
"messages are printed. Note that -D only affects "
|
||||
"output to stdout. All debug messages are written "
|
||||
"to ${T}/log.do_taskname, regardless of the debug "
|
||||
"level.")
|
||||
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||
action = "count", dest="debug", default = 0)
|
||||
|
||||
parser.add_option("-q", "--quiet", action="count", dest="quiet", default=0,
|
||||
help="Output less log message data to the terminal. You can specify this more than once.")
|
||||
parser.add_option("-n", "--dry-run", help = "Don't execute, just go through the motions.",
|
||||
action = "store_true", dest = "dry_run", default = False)
|
||||
|
||||
parser.add_option("-n", "--dry-run", action="store_true", dest="dry_run", default=False,
|
||||
help="Don't execute, just go through the motions.")
|
||||
parser.add_option("-S", "--dump-signatures", help = "Dump out the signature construction information, with no task execution. The SIGNATURE_HANDLER parameter is passed to the handler. Two common values are none and printdiff but the handler may define more/less. none means only dump the signature, printdiff means compare the dumped signature with the cached one.",
|
||||
action = "append", dest = "dump_signatures", default = [], metavar="SIGNATURE_HANDLER")
|
||||
|
||||
parser.add_option("-S", "--dump-signatures", action="append", dest="dump_signatures",
|
||||
default=[], metavar="SIGNATURE_HANDLER",
|
||||
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", help = "Quit after parsing the BB recipes.",
|
||||
action = "store_true", dest = "parse_only", default = False)
|
||||
|
||||
parser.add_option("-p", "--parse-only", action="store_true",
|
||||
dest="parse_only", default=False,
|
||||
help="Quit after parsing the BB recipes.")
|
||||
parser.add_option("-s", "--show-versions", help = "Show current and preferred versions of all recipes.",
|
||||
action = "store_true", dest = "show_versions", default = False)
|
||||
|
||||
parser.add_option("-s", "--show-versions", action="store_true",
|
||||
dest="show_versions", default=False,
|
||||
help="Show current and preferred versions of all recipes.")
|
||||
parser.add_option("-e", "--environment", help = "Show the global or per-recipe environment complete with information about where variables were set/changed.",
|
||||
action = "store_true", dest = "show_environment", default = False)
|
||||
|
||||
parser.add_option("-e", "--environment", action="store_true",
|
||||
dest="show_environment", default=False,
|
||||
help="Show the global or per-recipe environment complete with information"
|
||||
" about where variables were set/changed.")
|
||||
parser.add_option("-g", "--graphviz", help = "Save dependency tree information for the specified targets in the dot syntax.",
|
||||
action = "store_true", dest = "dot_graph", default = False)
|
||||
|
||||
parser.add_option("-g", "--graphviz", action="store_true", dest="dot_graph", default=False,
|
||||
help="Save dependency tree information for the specified "
|
||||
"targets in the dot syntax.")
|
||||
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
|
||||
action = "append", dest = "extra_assume_provided", default = [])
|
||||
|
||||
parser.add_option("-I", "--ignore-deps", action="append",
|
||||
dest="extra_assume_provided", 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", help = """Show debug logging for the specified logging domains""",
|
||||
action = "append", dest = "debug_domains", default = [])
|
||||
|
||||
parser.add_option("-l", "--log-domains", action="append", dest="debug_domains", default=[],
|
||||
help="Show debug logging for the specified logging domains")
|
||||
parser.add_option("-P", "--profile", help = "Profile the command and save reports.",
|
||||
action = "store_true", dest = "profile", default = False)
|
||||
|
||||
parser.add_option("-P", "--profile", action="store_true", dest="profile", default=False,
|
||||
help="Profile the command and save reports.")
|
||||
env_ui = os.environ.get('BITBAKE_UI', None)
|
||||
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
|
||||
parser.add_option("-u", "--ui", action="store", dest="ui",
|
||||
default=os.environ.get('BITBAKE_UI', 'knotty'),
|
||||
help="The user interface to use (@CHOICES@ - default %default).")
|
||||
parser.add_option("-t", "--servertype", help = "Choose which server type to use (@CHOICES@ - default %default).",
|
||||
action = "store", dest = "servertype", default = "process")
|
||||
|
||||
# @CHOICES@ is substituted out by BitbakeHelpFormatter above
|
||||
parser.add_option("-t", "--servertype", action="store", dest="servertype",
|
||||
default=["process", "xmlrpc"]["BBSERVER" in os.environ],
|
||||
help="Choose which server type to use (@CHOICES@ - default %default).")
|
||||
parser.add_option("", "--token", help = "Specify the connection token to be used when connecting to a remote server.",
|
||||
action = "store", dest = "xmlrpctoken")
|
||||
|
||||
parser.add_option("", "--token", action="store", dest="xmlrpctoken",
|
||||
default=os.environ.get("BBTOKEN"),
|
||||
help="Specify the connection token to be used when connecting "
|
||||
"to a remote server.")
|
||||
parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not.",
|
||||
action = "store_true", dest = "revisions_changed", default = False)
|
||||
|
||||
parser.add_option("", "--revisions-changed", action="store_true",
|
||||
dest="revisions_changed", default=False,
|
||||
help="Set the exit code depending on whether upstream floating "
|
||||
"revisions have changed or not.")
|
||||
parser.add_option("", "--server-only", help = "Run bitbake without a UI, only starting a server (cooker) process.",
|
||||
action = "store_true", dest = "server_only", default = False)
|
||||
|
||||
parser.add_option("", "--server-only", action="store_true",
|
||||
dest="server_only", default=False,
|
||||
help="Run bitbake without a UI, only starting a server "
|
||||
"(cooker) process.")
|
||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to.",
|
||||
action = "store", dest = "bind", default = False)
|
||||
|
||||
parser.add_option("", "--foreground", action="store_true",
|
||||
help="Run bitbake server in foreground.")
|
||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks. sstate will be ignored and everything needed, built.",
|
||||
action = "store_true", dest = "nosetscene", default = False)
|
||||
|
||||
parser.add_option("-B", "--bind", action="store", dest="bind", default=False,
|
||||
help="The name/address for the bitbake server to bind to.")
|
||||
parser.add_option("", "--setscene-only", help = "Only run setscene tasks, don't run any real tasks.",
|
||||
action = "store_true", dest = "setsceneonly", default = False)
|
||||
|
||||
parser.add_option("-T", "--idle-timeout", type=int,
|
||||
default=int(os.environ.get("BBTIMEOUT", "0")),
|
||||
help="Set timeout to unload bitbake server due to inactivity")
|
||||
parser.add_option("", "--remote-server", help = "Connect to the specified server.",
|
||||
action = "store", dest = "remote_server", default = False)
|
||||
|
||||
parser.add_option("", "--no-setscene", action="store_true",
|
||||
dest="nosetscene", default=False,
|
||||
help="Do not run any setscene tasks. sstate will be ignored and "
|
||||
"everything needed, built.")
|
||||
parser.add_option("-m", "--kill-server", help = "Terminate the remote server.",
|
||||
action = "store_true", dest = "kill_server", default = False)
|
||||
|
||||
parser.add_option("", "--setscene-only", action="store_true",
|
||||
dest="setsceneonly", default=False,
|
||||
help="Only run setscene tasks, don't run any real tasks.")
|
||||
parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
|
||||
action = "store_true", dest = "observe_only", default = False)
|
||||
|
||||
parser.add_option("", "--remote-server", action="store", dest="remote_server",
|
||||
default=os.environ.get("BBSERVER"),
|
||||
help="Connect to the specified server.")
|
||||
parser.add_option("", "--status-only", help = "Check the status of the remote bitbake server.",
|
||||
action = "store_true", dest = "status_only", default = False)
|
||||
|
||||
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.")
|
||||
|
||||
parser.add_option("", "--runall", action="store", dest="runall",
|
||||
help="Run the specified task for all build targets and their dependencies.")
|
||||
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.",
|
||||
action = "store", dest = "writeeventlog")
|
||||
|
||||
options, targets = parser.parse_args(argv)
|
||||
|
||||
if options.quiet and options.verbose:
|
||||
parser.error("options --quiet and --verbose are mutually exclusive")
|
||||
# some environmental variables set also configuration options
|
||||
if "BBSERVER" in os.environ:
|
||||
options.servertype = "xmlrpc"
|
||||
options.remote_server = os.environ["BBSERVER"]
|
||||
|
||||
if options.quiet and options.debug:
|
||||
parser.error("options --quiet and --debug are mutually exclusive")
|
||||
if "BBTOKEN" in os.environ:
|
||||
options.xmlrpctoken = os.environ["BBTOKEN"]
|
||||
|
||||
# use configuration files from environment variables
|
||||
if "BBPRECONF" in os.environ:
|
||||
options.prefile.append(os.environ["BBPRECONF"])
|
||||
|
||||
if "BBPOSTCONF" in os.environ:
|
||||
options.postfile.append(os.environ["BBPOSTCONF"])
|
||||
if "BBEVENTLOG" in os.environ:
|
||||
options.writeeventlog = os.environ["BBEVENTLOG"]
|
||||
|
||||
# fill in proper log name if not supplied
|
||||
if options.writeeventlog is not None and len(options.writeeventlog) == 0:
|
||||
from datetime import datetime
|
||||
eventlog = "bitbake_eventlog_%s.json" % datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
options.writeeventlog = eventlog
|
||||
import datetime
|
||||
options.writeeventlog = "bitbake_eventlog_%s.json" % datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
|
||||
# if BBSERVER says to autodetect, let's do that
|
||||
if options.remote_server:
|
||||
port = -1
|
||||
if options.remote_server != 'autostart':
|
||||
host, port = options.remote_server.split(":", 2)
|
||||
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
|
||||
# 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
|
||||
@@ -344,20 +275,18 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||
lf.close()
|
||||
options.remote_server = remotedef
|
||||
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:]
|
||||
|
||||
|
||||
def start_server(servermodule, configParams, configuration, features):
|
||||
server = servermodule.BitBakeServer()
|
||||
single_use = not configParams.server_only and os.getenv('BBSERVER') != 'autostart'
|
||||
single_use = not configParams.server_only
|
||||
if configParams.bind:
|
||||
(host, port) = configParams.bind.split(':')
|
||||
server.initServer((host, int(port)), single_use=single_use,
|
||||
idle_timeout=configParams.idle_timeout)
|
||||
configuration.interface = [server.serverImpl.host, server.serverImpl.port]
|
||||
server.initServer((host, int(port)), single_use)
|
||||
configuration.interface = [ server.serverImpl.host, server.serverImpl.port ]
|
||||
else:
|
||||
server.initServer(single_use=single_use)
|
||||
configuration.interface = []
|
||||
@@ -370,18 +299,20 @@ def start_server(servermodule, configParams, configuration, features):
|
||||
server.addcooker(cooker)
|
||||
server.saveConnectionDetails()
|
||||
except Exception as e:
|
||||
exc_info = sys.exc_info()
|
||||
while hasattr(server, "event_queue"):
|
||||
import queue
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
try:
|
||||
event = server.event_queue.get(block=False)
|
||||
except (queue.Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
raise
|
||||
if not configParams.foreground:
|
||||
server.detach()
|
||||
cooker.shutdown()
|
||||
raise exc_info[1], None, exc_info[2]
|
||||
server.detach()
|
||||
cooker.lock.close()
|
||||
return server
|
||||
|
||||
@@ -397,15 +328,16 @@ def bitbake_main(configParams, configuration):
|
||||
# updates to log files for use with tail
|
||||
try:
|
||||
if sys.stdout.name == '<stdout>':
|
||||
# Reopen with O_SYNC (unbuffered)
|
||||
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:
|
||||
pass
|
||||
|
||||
|
||||
configuration.setConfigParameters(configParams)
|
||||
|
||||
ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
|
||||
servermodule = import_extension_module(bb.server, configParams.servertype, 'BitBakeServer')
|
||||
|
||||
if configParams.server_only:
|
||||
if configParams.servertype != "xmlrpc":
|
||||
raise BBMainException("FATAL: If '--server-only' is defined, we must set the "
|
||||
@@ -413,21 +345,10 @@ def bitbake_main(configParams, configuration):
|
||||
if not configParams.bind:
|
||||
raise BBMainException("FATAL: The '--server-only' option requires a name/address "
|
||||
"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:
|
||||
raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" %
|
||||
("the BBSERVER environment variable" if "BBSERVER" in os.environ \
|
||||
else "the '--remote-server' option"))
|
||||
|
||||
elif configParams.foreground:
|
||||
raise BBMainException("FATAL: The '--foreground' option can only be used "
|
||||
"with --server-only.\n")
|
||||
else "the '--remote-server' option" ))
|
||||
|
||||
if configParams.bind and configParams.servertype != "xmlrpc":
|
||||
raise BBMainException("FATAL: If '-B' or '--bind' is defined, we must "
|
||||
@@ -442,8 +363,7 @@ def bitbake_main(configParams, configuration):
|
||||
"connecting to a server.\n")
|
||||
|
||||
if configParams.kill_server and not configParams.remote_server:
|
||||
raise BBMainException("FATAL: '--kill-server' can only be used to "
|
||||
"terminate a remote server")
|
||||
raise BBMainException("FATAL: '--kill-server' can only be used to terminate a remote server")
|
||||
|
||||
if "BBDEBUG" in os.environ:
|
||||
level = int(os.environ["BBDEBUG"])
|
||||
@@ -451,33 +371,8 @@ def bitbake_main(configParams, configuration):
|
||||
configuration.debug = level
|
||||
|
||||
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
|
||||
configuration.debug_domains)
|
||||
configuration.debug_domains)
|
||||
|
||||
server, server_connection, ui_module = setup_bitbake(configParams, configuration)
|
||||
if server_connection is None and configParams.kill_server:
|
||||
return 0
|
||||
|
||||
if not configParams.server_only:
|
||||
if configParams.status_only:
|
||||
server_connection.terminate()
|
||||
return 0
|
||||
|
||||
try:
|
||||
return ui_module.main(server_connection.connection, server_connection.events,
|
||||
configParams)
|
||||
finally:
|
||||
bb.event.ui_queue = []
|
||||
server_connection.terminate()
|
||||
else:
|
||||
print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host,
|
||||
server.serverImpl.port))
|
||||
if configParams.foreground:
|
||||
server.serverImpl.serve_forever()
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
def setup_bitbake(configParams, configuration, extrafeatures=None):
|
||||
# Ensure logging messages get sent to the UI as events
|
||||
handler = bb.event.LogHandler()
|
||||
if not configParams.status_only:
|
||||
@@ -487,11 +382,8 @@ def setup_bitbake(configParams, configuration, extrafeatures=None):
|
||||
# Clear away any spurious environment variables while we stoke up the cooker
|
||||
cleanedvars = bb.utils.clean_environment()
|
||||
|
||||
if configParams.server_only:
|
||||
featureset = []
|
||||
ui_module = None
|
||||
else:
|
||||
ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
|
||||
featureset = []
|
||||
if not configParams.server_only:
|
||||
# Collect the feature set for the UI
|
||||
featureset = getattr(ui_module, "featureSet", [])
|
||||
|
||||
@@ -502,35 +394,17 @@ def setup_bitbake(configParams, configuration, extrafeatures=None):
|
||||
setattr(configuration, "%s_server" % param, value)
|
||||
param = "%s_server" % param
|
||||
|
||||
if extrafeatures:
|
||||
for feature in extrafeatures:
|
||||
if not feature in featureset:
|
||||
featureset.append(feature)
|
||||
|
||||
servermodule = import_extension_module(bb.server,
|
||||
configParams.servertype,
|
||||
'BitBakeServer')
|
||||
if configParams.remote_server:
|
||||
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
|
||||
from bb.server.xmlrpc import BitBakeXMLRPCClient
|
||||
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only,
|
||||
configParams.xmlrpctoken)
|
||||
server.saveConnectionDetails(configParams.remote_server)
|
||||
else:
|
||||
if not configParams.remote_server:
|
||||
# we start a server with a given configuration
|
||||
server = start_server(servermodule, configParams, configuration, featureset)
|
||||
bb.event.ui_queue = []
|
||||
|
||||
if configParams.server_only:
|
||||
server_connection = None
|
||||
else:
|
||||
# we start a stub server that is actually a XMLRPClient that connects to a real server
|
||||
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only, configParams.xmlrpctoken)
|
||||
server.saveConnectionDetails(configParams.remote_server)
|
||||
|
||||
|
||||
if not configParams.server_only:
|
||||
try:
|
||||
server_connection = server.establishConnection(featureset)
|
||||
except Exception as e:
|
||||
@@ -539,7 +413,7 @@ def setup_bitbake(configParams, configuration, extrafeatures=None):
|
||||
if configParams.kill_server:
|
||||
server_connection.connection.terminateServer()
|
||||
bb.event.ui_queue = []
|
||||
return None, None, None
|
||||
return 0
|
||||
|
||||
server_connection.setupEventQueue()
|
||||
|
||||
@@ -549,4 +423,18 @@ def setup_bitbake(configParams, configuration, extrafeatures=None):
|
||||
|
||||
logger.removeHandler(handler)
|
||||
|
||||
return server, server_connection, ui_module
|
||||
|
||||
if configParams.status_only:
|
||||
server_connection.terminate()
|
||||
return 0
|
||||
|
||||
try:
|
||||
return ui_module.main(server_connection.connection, server_connection.events, configParams)
|
||||
finally:
|
||||
bb.event.ui_queue = []
|
||||
server_connection.terminate()
|
||||
else:
|
||||
print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
@@ -129,7 +129,7 @@ def getDiskData(BBDirs, configuration):
|
||||
bb.utils.mkdirhier(path)
|
||||
dev = getMountedDev(path)
|
||||
# Use path/action as the key
|
||||
devDict[(path, action)] = [dev, minSpace, minInode]
|
||||
devDict[os.path.join(path, action)] = [dev, minSpace, minInode]
|
||||
|
||||
return devDict
|
||||
|
||||
@@ -141,7 +141,7 @@ def getInterval(configuration):
|
||||
spaceDefault = 50 * 1024 * 1024
|
||||
inodeDefault = 5 * 1024
|
||||
|
||||
interval = configuration.getVar("BB_DISKMON_WARNINTERVAL")
|
||||
interval = configuration.getVar("BB_DISKMON_WARNINTERVAL", True)
|
||||
if not interval:
|
||||
return spaceDefault, inodeDefault
|
||||
else:
|
||||
@@ -179,7 +179,7 @@ class diskMonitor:
|
||||
self.enableMonitor = False
|
||||
self.configuration = configuration
|
||||
|
||||
BBDirs = configuration.getVar("BB_DISKMON_DIRS") or None
|
||||
BBDirs = configuration.getVar("BB_DISKMON_DIRS", True) or None
|
||||
if BBDirs:
|
||||
self.devDict = getDiskData(BBDirs, configuration)
|
||||
if self.devDict:
|
||||
@@ -205,25 +205,22 @@ class diskMonitor:
|
||||
""" Take action for the monitor """
|
||||
|
||||
if self.enableMonitor:
|
||||
diskUsage = {}
|
||||
for k, attributes in self.devDict.items():
|
||||
path, action = k
|
||||
dev, minSpace, minInode = attributes
|
||||
for k in self.devDict:
|
||||
path = os.path.dirname(k)
|
||||
action = os.path.basename(k)
|
||||
dev = self.devDict[k][0]
|
||||
minSpace = self.devDict[k][1]
|
||||
minInode = self.devDict[k][2]
|
||||
|
||||
st = os.statvfs(path)
|
||||
|
||||
# The available free space, integer number
|
||||
# The free space, float point number
|
||||
freeSpace = st.f_bavail * st.f_frsize
|
||||
|
||||
# Send all relevant information in the event.
|
||||
freeSpaceRoot = st.f_bfree * st.f_frsize
|
||||
totalSpace = st.f_blocks * st.f_frsize
|
||||
diskUsage[dev] = bb.event.DiskUsageSample(freeSpace, freeSpaceRoot, totalSpace)
|
||||
|
||||
if minSpace and freeSpace < minSpace:
|
||||
# Always show warning, the self.checked would always be False if the action is WARN
|
||||
if self.preFreeS[k] == 0 or self.preFreeS[k] - freeSpace > self.spaceInterval and not self.checked[k]:
|
||||
logger.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))
|
||||
self.preFreeS[k] = freeSpace
|
||||
|
||||
@@ -238,7 +235,7 @@ class diskMonitor:
|
||||
rq.finish_runqueue(True)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, path), self.configuration)
|
||||
|
||||
# The free inodes, integer number
|
||||
# The free inodes, float point number
|
||||
freeInode = st.f_favail
|
||||
|
||||
if minInode and freeInode < minInode:
|
||||
@@ -249,7 +246,7 @@ class diskMonitor:
|
||||
continue
|
||||
# Always show warning, the self.checked would always be False if the action is WARN
|
||||
if self.preFreeI[k] == 0 or self.preFreeI[k] - freeInode > self.inodeInterval and not self.checked[k]:
|
||||
logger.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))
|
||||
self.preFreeI[k] = freeInode
|
||||
|
||||
@@ -263,6 +260,4 @@ class diskMonitor:
|
||||
self.checked[k] = True
|
||||
rq.finish_runqueue(True)
|
||||
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeInode, path), self.configuration)
|
||||
|
||||
bb.event.fire(bb.event.MonitorDiskEvent(diskUsage), self.configuration)
|
||||
return
|
||||
|
||||
@@ -57,7 +57,7 @@ class BBLogFormatter(logging.Formatter):
|
||||
}
|
||||
|
||||
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 = {
|
||||
DEBUG3 : CYAN,
|
||||
@@ -90,9 +90,8 @@ class BBLogFormatter(logging.Formatter):
|
||||
if self.color_enabled:
|
||||
record = self.colorize(record)
|
||||
msg = logging.Formatter.format(self, record)
|
||||
if hasattr(record, 'bb_exc_formatted'):
|
||||
msg += '\n' + ''.join(record.bb_exc_formatted)
|
||||
elif hasattr(record, 'bb_exc_info'):
|
||||
|
||||
if hasattr(record, 'bb_exc_info'):
|
||||
etype, value, tb = record.bb_exc_info
|
||||
formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
|
||||
msg += '\n' + ''.join(formatted)
|
||||
@@ -182,12 +181,9 @@ def constructLogOptions():
|
||||
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
|
||||
return level, debug_domains
|
||||
|
||||
def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
|
||||
def addDefaultlogFilter(handler, cls = BBLogFilter):
|
||||
level, debug_domains = constructLogOptions()
|
||||
|
||||
if forcelevel is not None:
|
||||
level = forcelevel
|
||||
|
||||
cls(handler, level, debug_domains)
|
||||
|
||||
#
|
||||
@@ -201,18 +197,3 @@ def fatal(msgdomain, msg):
|
||||
logger = logging.getLogger("BitBake")
|
||||
logger.critical(msg)
|
||||
sys.exit(1)
|
||||
|
||||
def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
|
||||
"""Standalone logger creation function"""
|
||||
logger = logging.getLogger(name)
|
||||
console = logging.StreamHandler(output)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if color == 'always' or (color == 'auto' and output.isatty()):
|
||||
format.enable_color()
|
||||
console.setFormatter(format)
|
||||
if preserve_handlers:
|
||||
logger.addHandler(console)
|
||||
else:
|
||||
logger.handlers = [console]
|
||||
logger.setLevel(level)
|
||||
return logger
|
||||
|
||||
@@ -123,7 +123,7 @@ def init_parser(d):
|
||||
|
||||
def resolve_file(fn, d):
|
||||
if not os.path.isabs(fn):
|
||||
bbpath = d.getVar("BBPATH")
|
||||
bbpath = d.getVar("BBPATH", True)
|
||||
newfn, attempts = bb.utils.which(bbpath, fn, history=True)
|
||||
for af in attempts:
|
||||
mark_dependency(d, af)
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future_builtins import filter
|
||||
import re
|
||||
import string
|
||||
import logging
|
||||
@@ -30,6 +31,8 @@ import itertools
|
||||
from bb import methodpool
|
||||
from bb.parse import logger
|
||||
|
||||
_bbversions_re = re.compile(r"\[(?P<from>[0-9]+)-(?P<to>[0-9]+)\]")
|
||||
|
||||
class StatementGroup(list):
|
||||
def eval(self, data):
|
||||
for statement in self:
|
||||
@@ -67,33 +70,6 @@ class ExportNode(AstNode):
|
||||
def eval(self, data):
|
||||
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):
|
||||
"""
|
||||
Various data related updates. For the sake of sanity
|
||||
@@ -130,6 +106,7 @@ class DataNode(AstNode):
|
||||
val = groupd["value"]
|
||||
elif "colon" in groupd and groupd["colon"] != None:
|
||||
e = data.createCopy()
|
||||
bb.data.update_data(e)
|
||||
op = "immediate"
|
||||
val = e.expand(groupd["value"], key + "[:=]")
|
||||
elif "append" in groupd and groupd["append"] != None:
|
||||
@@ -162,7 +139,7 @@ class DataNode(AstNode):
|
||||
data.setVar(key, val, parsing=True, **loginfo)
|
||||
|
||||
class MethodNode(AstNode):
|
||||
tr_tbl = str.maketrans('/.+-@%&', '_______')
|
||||
tr_tbl = string.maketrans('/.+-@%&', '_______')
|
||||
|
||||
def __init__(self, filename, lineno, func_name, body, python, fakeroot):
|
||||
AstNode.__init__(self, filename, lineno)
|
||||
@@ -294,12 +271,6 @@ def handleInclude(statements, filename, lineno, m, force):
|
||||
def handleExport(statements, filename, lineno, m):
|
||||
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):
|
||||
statements.append(DataNode(filename, lineno, groupd))
|
||||
|
||||
@@ -336,26 +307,24 @@ def handleInherit(statements, filename, lineno, m):
|
||||
statements.append(InheritNode(filename, lineno, classes))
|
||||
|
||||
def finalize(fn, d, variant = None):
|
||||
saved_handlers = bb.event.get_handlers().copy()
|
||||
|
||||
all_handlers = {}
|
||||
for var in d.getVar('__BBHANDLERS', False) or []:
|
||||
# try to add the handler
|
||||
handlerfn = d.getVarFlag(var, "filename", False)
|
||||
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") or "").split(), handlerfn, handlerln)
|
||||
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.data.expandKeys(d)
|
||||
bb.data.update_data(d)
|
||||
code = []
|
||||
for funcname in d.getVar("__BBANONFUNCS", False) or []:
|
||||
code.append("%s(d)" % funcname)
|
||||
bb.utils.better_exec("\n".join(code), {"d": d})
|
||||
bb.data.update_data(d)
|
||||
|
||||
tasklist = d.getVar('__BBTASKS', False) or []
|
||||
bb.event.fire(bb.event.RecipeTaskPreProcess(fn, list(tasklist)), d)
|
||||
bb.build.add_tasks(tasklist, d)
|
||||
|
||||
bb.parse.siggen.finalise(fn, d, variant)
|
||||
@@ -363,7 +332,6 @@ def finalize(fn, d, variant = None):
|
||||
d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
|
||||
|
||||
bb.event.fire(bb.event.RecipeParsed(fn), d)
|
||||
bb.event.set_handlers(saved_handlers)
|
||||
|
||||
def _create_variants(datastores, names, function, onlyfinalise):
|
||||
def create_variant(name, orig_d, arg = None):
|
||||
@@ -373,16 +341,37 @@ def _create_variants(datastores, names, function, onlyfinalise):
|
||||
function(arg or name, new_d)
|
||||
datastores[name] = new_d
|
||||
|
||||
for variant in list(datastores.keys()):
|
||||
for variant, variant_d in datastores.items():
|
||||
for name in names:
|
||||
if not variant:
|
||||
# Based on main recipe
|
||||
create_variant(name, datastores[""])
|
||||
create_variant(name, variant_d)
|
||||
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_one(version, start, end):
|
||||
for i in xrange(start, end + 1):
|
||||
ver = _bbversions_re.sub(str(i), version, 1)
|
||||
yield ver
|
||||
|
||||
versions = iter(versions)
|
||||
while True:
|
||||
try:
|
||||
version = next(versions)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
range_ver = _bbversions_re.search(version)
|
||||
if not range_ver:
|
||||
yield version
|
||||
else:
|
||||
newversions = expand_one(version, int(range_ver.group("from")),
|
||||
int(range_ver.group("to")))
|
||||
versions = itertools.chain(newversions, versions)
|
||||
|
||||
def multi_finalize(fn, d):
|
||||
appends = (d.getVar("__BBAPPEND") or "").split()
|
||||
appends = (d.getVar("__BBAPPEND", True) or "").split()
|
||||
for append in appends:
|
||||
logger.debug(1, "Appending .bbappend file %s to %s", append, fn)
|
||||
bb.parse.BBHandler.handle(append, d, True)
|
||||
@@ -397,7 +386,51 @@ def multi_finalize(fn, d):
|
||||
d.setVar("__SKIPPED", e.args[0])
|
||||
datastores = {"": safe_d}
|
||||
|
||||
extended = d.getVar("BBCLASSEXTEND") or ""
|
||||
versions = (d.getVar("BBVERSIONS", True) or "").split()
|
||||
if versions:
|
||||
pv = orig_pv = d.getVar("PV", True)
|
||||
baseversions = {}
|
||||
|
||||
def verfunc(ver, d, pv_d = None):
|
||||
if pv_d is None:
|
||||
pv_d = d
|
||||
|
||||
overrides = d.getVar("OVERRIDES", True).split(":")
|
||||
pv_d.setVar("PV", ver)
|
||||
overrides.append(ver)
|
||||
bpv = baseversions.get(ver) or orig_pv
|
||||
pv_d.setVar("BPV", bpv)
|
||||
overrides.append(bpv)
|
||||
d.setVar("OVERRIDES", ":".join(overrides))
|
||||
|
||||
versions = list(_expand_versions(versions))
|
||||
for pos, version in enumerate(list(versions)):
|
||||
try:
|
||||
pv, bpv = version.split(":", 2)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
versions[pos] = pv
|
||||
baseversions[pv] = bpv
|
||||
|
||||
if pv in versions and not baseversions.get(pv):
|
||||
versions.remove(pv)
|
||||
else:
|
||||
pv = versions.pop()
|
||||
|
||||
# This is necessary because our existing main datastore
|
||||
# has already been finalized with the old PV, we need one
|
||||
# that's been finalized with the new PV.
|
||||
d = bb.data.createCopy(safe_d)
|
||||
verfunc(pv, d, safe_d)
|
||||
try:
|
||||
finalize(fn, d)
|
||||
except bb.parse.SkipRecipe as e:
|
||||
d.setVar("__SKIPPED", e.args[0])
|
||||
|
||||
_create_variants(datastores, versions, verfunc, onlyfinalise)
|
||||
|
||||
extended = d.getVar("BBCLASSEXTEND", True) or ""
|
||||
if extended:
|
||||
# the following is to support bbextends with arguments, for e.g. multilib
|
||||
# an example is as follows:
|
||||
@@ -415,7 +448,7 @@ def multi_finalize(fn, d):
|
||||
else:
|
||||
extendedmap[ext] = ext
|
||||
|
||||
pn = d.getVar("PN")
|
||||
pn = d.getVar("PN", True)
|
||||
def extendfunc(name, d):
|
||||
if name != extendedmap[name]:
|
||||
d.setVar("BBEXTENDCURR", extendedmap[name])
|
||||
@@ -427,13 +460,17 @@ def multi_finalize(fn, d):
|
||||
safe_d.setVar("BBCLASSEXTEND", extended)
|
||||
_create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
|
||||
|
||||
for variant in datastores.keys():
|
||||
for variant, variant_d in datastores.iteritems():
|
||||
if variant:
|
||||
try:
|
||||
if not onlyfinalise or variant in onlyfinalise:
|
||||
finalize(fn, datastores[variant], variant)
|
||||
finalize(fn, variant_d, variant)
|
||||
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
|
||||
return datastores
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
import re, bb, os
|
||||
import logging
|
||||
import bb.build, bb.utils
|
||||
@@ -66,7 +66,7 @@ def inherit(files, fn, lineno, d):
|
||||
file = os.path.join('classes', '%s.bbclass' % file)
|
||||
|
||||
if not os.path.isabs(file):
|
||||
bbpath = d.getVar("BBPATH")
|
||||
bbpath = d.getVar("BBPATH", True)
|
||||
abs_fn, attempts = bb.utils.which(bbpath, file, history=True)
|
||||
for af in attempts:
|
||||
if af != abs_fn:
|
||||
@@ -87,17 +87,17 @@ def get_statements(filename, absolute_filename, base_name):
|
||||
try:
|
||||
return cached_statements[absolute_filename]
|
||||
except KeyError:
|
||||
with open(absolute_filename, 'r') as f:
|
||||
statements = ast.StatementGroup()
|
||||
|
||||
lineno = 0
|
||||
while True:
|
||||
lineno = lineno + 1
|
||||
s = f.readline()
|
||||
if not s: break
|
||||
s = s.rstrip()
|
||||
feeder(lineno, s, filename, base_name, statements)
|
||||
file = open(absolute_filename, 'r')
|
||||
statements = ast.StatementGroup()
|
||||
|
||||
lineno = 0
|
||||
while True:
|
||||
lineno = lineno + 1
|
||||
s = file.readline()
|
||||
if not s: break
|
||||
s = s.rstrip()
|
||||
feeder(lineno, s, filename, base_name, statements)
|
||||
file.close()
|
||||
if __inpython__:
|
||||
# add a blank line to close out any python definition
|
||||
feeder(lineno, "", filename, base_name, statements, eof=True)
|
||||
|
||||
@@ -33,7 +33,7 @@ from bb.parse import ParseError, resolve_file, ast, logger, handle
|
||||
__config_regexp__ = re.compile( r"""
|
||||
^
|
||||
(?P<exp>export\s*)?
|
||||
(?P<var>[a-zA-Z0-9\-_+.${}/~]+?)
|
||||
(?P<var>[a-zA-Z0-9\-~_+.${}/]+?)
|
||||
(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?
|
||||
|
||||
\s* (
|
||||
@@ -56,9 +56,7 @@ __config_regexp__ = re.compile( r"""
|
||||
""", re.X)
|
||||
__include_regexp__ = re.compile( r"include\s+(.+)" )
|
||||
__require_regexp__ = re.compile( r"require\s+(.+)" )
|
||||
__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/~]+)$" )
|
||||
__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\-_+.]+)\]$" )
|
||||
__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
|
||||
|
||||
def init(data):
|
||||
topdir = data.getVar('TOPDIR', False)
|
||||
@@ -83,16 +81,16 @@ def include(parentfn, fn, lineno, data, error_out):
|
||||
|
||||
if not os.path.isabs(fn):
|
||||
dname = os.path.dirname(parentfn)
|
||||
bbpath = "%s:%s" % (dname, data.getVar("BBPATH"))
|
||||
bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
|
||||
abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
|
||||
if abs_fn and bb.parse.check_dependency(data, abs_fn):
|
||||
logger.warning("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE')))
|
||||
logger.warn("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
|
||||
for af in attempts:
|
||||
bb.parse.mark_dependency(data, af)
|
||||
if abs_fn:
|
||||
fn = abs_fn
|
||||
elif bb.parse.check_dependency(data, fn):
|
||||
logger.warning("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE')))
|
||||
logger.warn("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
|
||||
|
||||
try:
|
||||
bb.parse.handle(fn, data, True)
|
||||
@@ -187,16 +185,6 @@ def feeder(lineno, s, fn, statements):
|
||||
ast.handleExport(statements, fn, lineno, m)
|
||||
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);
|
||||
|
||||
# Add us to the handlers list
|
||||
|
||||
@@ -28,7 +28,11 @@ import sys
|
||||
import warnings
|
||||
from bb.compat import total_ordering
|
||||
from collections import Mapping
|
||||
import sqlite3
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
except ImportError:
|
||||
from pysqlite2 import dbapi2 as sqlite3
|
||||
|
||||
sqlversion = sqlite3.sqlite_version_info
|
||||
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
|
||||
@@ -88,9 +92,9 @@ class SQLTable(collections.MutableMapping):
|
||||
self._execute("DELETE from %s where key=?;" % self.table, [key])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if not isinstance(key, str):
|
||||
if not isinstance(key, basestring):
|
||||
raise TypeError('Only string keys are supported')
|
||||
elif not isinstance(value, str):
|
||||
elif not isinstance(value, basestring):
|
||||
raise TypeError('Only string values are supported')
|
||||
|
||||
data = self._execute("SELECT * from %s where key=?;" %
|
||||
@@ -174,7 +178,7 @@ class PersistData(object):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
@@ -203,8 +207,8 @@ def connect(database):
|
||||
def persist(domain, d):
|
||||
"""Convenience factory for SQLTable objects based upon metadata"""
|
||||
import bb.utils
|
||||
cachedir = (d.getVar("PERSISTENT_DIR") or
|
||||
d.getVar("CACHE"))
|
||||
cachedir = (d.getVar("PERSISTENT_DIR", True) or
|
||||
d.getVar("CACHE", True))
|
||||
if not cachedir:
|
||||
logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -17,7 +17,7 @@ class CmdError(RuntimeError):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
if not isinstance(self.command, str):
|
||||
if not isinstance(self.command, basestring):
|
||||
cmd = subprocess.list2cmdline(self.command)
|
||||
else:
|
||||
cmd = self.command
|
||||
@@ -97,8 +97,6 @@ def _logged_communicate(pipe, log, input, extrafiles):
|
||||
try:
|
||||
while pipe.poll() is None:
|
||||
rlist = rin
|
||||
stdoutbuf = b""
|
||||
stderrbuf = b""
|
||||
try:
|
||||
r,w,e = select.select (rlist, [], [], 1)
|
||||
except OSError as e:
|
||||
@@ -106,26 +104,16 @@ def _logged_communicate(pipe, log, input, extrafiles):
|
||||
raise
|
||||
|
||||
if pipe.stdout in r:
|
||||
data = stdoutbuf + pipe.stdout.read()
|
||||
if data is not None and len(data) > 0:
|
||||
try:
|
||||
data = data.decode("utf-8")
|
||||
outdata.append(data)
|
||||
log.write(data)
|
||||
stdoutbuf = b""
|
||||
except UnicodeDecodeError:
|
||||
stdoutbuf = data
|
||||
data = pipe.stdout.read()
|
||||
if data is not None:
|
||||
outdata.append(data)
|
||||
log.write(data)
|
||||
|
||||
if pipe.stderr in r:
|
||||
data = stderrbuf + pipe.stderr.read()
|
||||
if data is not None and len(data) > 0:
|
||||
try:
|
||||
data = data.decode("utf-8")
|
||||
errdata.append(data)
|
||||
log.write(data)
|
||||
stderrbuf = b""
|
||||
except UnicodeDecodeError:
|
||||
stderrbuf = data
|
||||
data = pipe.stderr.read()
|
||||
if data is not None:
|
||||
errdata.append(data)
|
||||
log.write(data)
|
||||
|
||||
readextras(r)
|
||||
|
||||
@@ -147,7 +135,7 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
|
||||
if not extrafiles:
|
||||
extrafiles = []
|
||||
|
||||
if isinstance(cmd, str) and not "shell" in options:
|
||||
if isinstance(cmd, basestring) and not "shell" in options:
|
||||
options["shell"] = True
|
||||
|
||||
try:
|
||||
@@ -162,10 +150,6 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
|
||||
stdout, stderr = _logged_communicate(pipe, log, input, extrafiles)
|
||||
else:
|
||||
stdout, stderr = pipe.communicate(input)
|
||||
if not stdout is None:
|
||||
stdout = stdout.decode("utf-8")
|
||||
if not stderr is None:
|
||||
stderr = stderr.decode("utf-8")
|
||||
|
||||
if pipe.returncode != 0:
|
||||
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
|
||||
@@ -48,6 +48,7 @@ def findProviders(cfgData, dataCache, pkg_pn = None):
|
||||
|
||||
# Need to ensure data store is expanded
|
||||
localdata = data.createCopy(cfgData)
|
||||
bb.data.update_data(localdata)
|
||||
bb.data.expandKeys(localdata)
|
||||
|
||||
preferred_versions = {}
|
||||
@@ -122,11 +123,11 @@ def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
|
||||
|
||||
# pn can contain '_', e.g. gcc-cross-x86_64 and an override cannot
|
||||
# hence we do this manually rather than use OVERRIDES
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn)
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn, True)
|
||||
if not preferred_v:
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn)
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn, True)
|
||||
if not preferred_v:
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION")
|
||||
preferred_v = cfgData.getVar("PREFERRED_VERSION", True)
|
||||
|
||||
if preferred_v:
|
||||
m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
|
||||
@@ -244,17 +245,17 @@ def _filterProviders(providers, item, cfgData, dataCache):
|
||||
pkg_pn[pn] = []
|
||||
pkg_pn[pn].append(p)
|
||||
|
||||
logger.debug(1, "providers for %s are: %s", item, list(sorted(pkg_pn.keys())))
|
||||
logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
|
||||
|
||||
# First add PREFERRED_VERSIONS
|
||||
for pn in sorted(pkg_pn):
|
||||
for pn in pkg_pn:
|
||||
sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
|
||||
preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
|
||||
if preferred_versions[pn][1]:
|
||||
eligible.append(preferred_versions[pn][1])
|
||||
|
||||
# Now add latest versions
|
||||
for pn in sorted(sortpkg_pn):
|
||||
for pn in sortpkg_pn:
|
||||
if pn in preferred_versions and preferred_versions[pn][1]:
|
||||
continue
|
||||
preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
|
||||
@@ -288,7 +289,7 @@ def filterProviders(providers, item, cfgData, dataCache):
|
||||
|
||||
eligible = _filterProviders(providers, item, cfgData, dataCache)
|
||||
|
||||
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item)
|
||||
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
|
||||
if prefervar:
|
||||
dataCache.preferred[item] = prefervar
|
||||
|
||||
@@ -317,7 +318,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
||||
eligible = _filterProviders(providers, item, cfgData, dataCache)
|
||||
|
||||
# First try and match any PREFERRED_RPROVIDER entry
|
||||
prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item)
|
||||
prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item, True)
|
||||
foundUnique = False
|
||||
if prefervar:
|
||||
for p in eligible:
|
||||
@@ -344,7 +345,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
||||
pn = dataCache.pkg_fn[p]
|
||||
provides = dataCache.pn_provides[pn]
|
||||
for provide in provides:
|
||||
prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide)
|
||||
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)
|
||||
@@ -401,7 +402,7 @@ def getRuntimeProviders(dataCache, rdepend):
|
||||
return rproviders
|
||||
|
||||
|
||||
def buildWorldTargetList(dataCache, task=None):
|
||||
def buildWorldTargetList(dataCache):
|
||||
"""
|
||||
Build package list for "bitbake world"
|
||||
"""
|
||||
@@ -412,9 +413,6 @@ def buildWorldTargetList(dataCache, task=None):
|
||||
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/'):
|
||||
|
||||
@@ -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')
|
||||
|
||||
# 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]):
|
||||
continue
|
||||
args[i] = args[i] + ' '
|
||||
|
||||
@@ -474,7 +474,7 @@ class Environment:
|
||||
"""
|
||||
# Save and remove previous arguments
|
||||
prevargs = []
|
||||
for i in range(int(self._env['#'])):
|
||||
for i in xrange(int(self._env['#'])):
|
||||
i = str(i+1)
|
||||
prevargs.append(self._env[i])
|
||||
del self._env[i]
|
||||
@@ -488,7 +488,7 @@ class Environment:
|
||||
return prevargs
|
||||
|
||||
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):
|
||||
return dict(self._env)
|
||||
|
||||
@@ -20,7 +20,7 @@ except NameError:
|
||||
from Set import Set as set
|
||||
|
||||
from ply import lex
|
||||
from bb.pysh.sherrors import *
|
||||
from sherrors import *
|
||||
|
||||
class NeedMore(Exception):
|
||||
pass
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import bb.pysh.pyshlex as pyshlex
|
||||
import pyshlex
|
||||
tokens = pyshlex.tokens
|
||||
|
||||
from ply import yacc
|
||||
import bb.pysh.sherrors as sherrors
|
||||
import sherrors
|
||||
|
||||
class IORedirect:
|
||||
def __init__(self, op, filename, io_number=None):
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
"""
|
||||
BitBake 'remotedata' module
|
||||
|
||||
Provides support for using a datastore from the bitbake client
|
||||
"""
|
||||
|
||||
# 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 bb.data
|
||||
|
||||
class RemoteDatastores:
|
||||
"""Used on the server side to manage references to server-side datastores"""
|
||||
def __init__(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.datastores = {}
|
||||
self.locked = []
|
||||
self.nextindex = 1
|
||||
|
||||
def __len__(self):
|
||||
return len(self.datastores)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key is None:
|
||||
return self.cooker.data
|
||||
else:
|
||||
return self.datastores[key]
|
||||
|
||||
def items(self):
|
||||
return self.datastores.items()
|
||||
|
||||
def store(self, d, locked=False):
|
||||
"""
|
||||
Put a datastore into the collection. If locked=True then the datastore
|
||||
is understood to be managed externally and cannot be released by calling
|
||||
release().
|
||||
"""
|
||||
idx = self.nextindex
|
||||
self.datastores[idx] = d
|
||||
if locked:
|
||||
self.locked.append(idx)
|
||||
self.nextindex += 1
|
||||
return idx
|
||||
|
||||
def check_store(self, d, locked=False):
|
||||
"""
|
||||
Put a datastore into the collection if it's not already in there;
|
||||
in either case return the index
|
||||
"""
|
||||
for key, val in self.datastores.items():
|
||||
if val is d:
|
||||
idx = key
|
||||
break
|
||||
else:
|
||||
idx = self.store(d, locked)
|
||||
return idx
|
||||
|
||||
def release(self, idx):
|
||||
"""Discard a datastore in the collection"""
|
||||
if idx in self.locked:
|
||||
raise Exception('Tried to release locked datastore %d' % idx)
|
||||
del self.datastores[idx]
|
||||
|
||||
def receive_datastore(self, remote_data):
|
||||
"""Receive a datastore object sent from the client (as prepared by transmit_datastore())"""
|
||||
dct = dict(remote_data)
|
||||
d = bb.data_smart.DataSmart()
|
||||
d.dict = dct
|
||||
while True:
|
||||
if '_remote_data' in dct:
|
||||
dsindex = dct['_remote_data']['_content']
|
||||
del dct['_remote_data']
|
||||
if dsindex is None:
|
||||
dct['_data'] = self.cooker.data.dict
|
||||
else:
|
||||
dct['_data'] = self.datastores[dsindex].dict
|
||||
break
|
||||
elif '_data' in dct:
|
||||
idct = dict(dct['_data'])
|
||||
dct['_data'] = idct
|
||||
dct = idct
|
||||
else:
|
||||
break
|
||||
return d
|
||||
|
||||
@staticmethod
|
||||
def transmit_datastore(d):
|
||||
"""Prepare a datastore object for sending over IPC from the client end"""
|
||||
# FIXME content might be a dict, need to turn that into a list as well
|
||||
def copy_dicts(dct):
|
||||
if '_remote_data' in dct:
|
||||
dsindex = dct['_remote_data']['_content'].dsindex
|
||||
newdct = dct.copy()
|
||||
newdct['_remote_data'] = {'_content': dsindex}
|
||||
return list(newdct.items())
|
||||
elif '_data' in dct:
|
||||
newdct = dct.copy()
|
||||
newdata = copy_dicts(dct['_data'])
|
||||
if newdata:
|
||||
newdct['_data'] = newdata
|
||||
return list(newdct.items())
|
||||
return None
|
||||
main_dict = copy_dicts(d.dict)
|
||||
return main_dict
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ import signal
|
||||
import sys
|
||||
import time
|
||||
import select
|
||||
from queue import Empty
|
||||
from Queue import Empty
|
||||
from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
|
||||
|
||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||
@@ -92,8 +92,6 @@ class ProcessServer(Process, BaseImplServer):
|
||||
self.event = EventAdapter(event_queue)
|
||||
self.featurelist = featurelist
|
||||
self.quit = False
|
||||
self.heartbeat_seconds = 1 # default, BB_HEARTBEAT_EVENT will be checked once we have a datastore.
|
||||
self.next_heartbeat = time.time()
|
||||
|
||||
self.quitin, self.quitout = Pipe()
|
||||
self.event_handle = multiprocessing.Value("i")
|
||||
@@ -103,14 +101,6 @@ class ProcessServer(Process, BaseImplServer):
|
||||
self.event_queue.put(event)
|
||||
self.event_handle.value = bb.event.register_UIHhandler(self, True)
|
||||
|
||||
heartbeat_event = self.cooker.data.getVar('BB_HEARTBEAT_EVENT')
|
||||
if heartbeat_event:
|
||||
try:
|
||||
self.heartbeat_seconds = float(heartbeat_event)
|
||||
except:
|
||||
# Throwing an exception here causes bitbake to hang.
|
||||
# Just warn about the invalid setting and continue
|
||||
bb.warn('Ignoring invalid BB_HEARTBEAT_EVENT=%s, must be a float specifying seconds.' % heartbeat_event)
|
||||
bb.cooker.server_main(self.cooker, self.main)
|
||||
|
||||
def main(self):
|
||||
@@ -147,7 +137,7 @@ class ProcessServer(Process, BaseImplServer):
|
||||
if not fds:
|
||||
fds = []
|
||||
|
||||
for function, data in list(self._idlefuns.items()):
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
if retval is False:
|
||||
@@ -155,7 +145,7 @@ class ProcessServer(Process, BaseImplServer):
|
||||
nextsleep = None
|
||||
elif retval is True:
|
||||
nextsleep = None
|
||||
elif isinstance(retval, float) and nextsleep:
|
||||
elif isinstance(retval, float):
|
||||
if (retval < nextsleep):
|
||||
nextsleep = retval
|
||||
elif nextsleep is None:
|
||||
@@ -170,21 +160,6 @@ class ProcessServer(Process, BaseImplServer):
|
||||
del self._idlefuns[function]
|
||||
self.quit = True
|
||||
|
||||
# Create new heartbeat event?
|
||||
now = time.time()
|
||||
if now >= self.next_heartbeat:
|
||||
# We might have missed heartbeats. Just trigger once in
|
||||
# that case and continue after the usual delay.
|
||||
self.next_heartbeat += self.heartbeat_seconds
|
||||
if self.next_heartbeat <= now:
|
||||
self.next_heartbeat = now + self.heartbeat_seconds
|
||||
heartbeat = bb.event.HeartbeatEvent(now)
|
||||
bb.event.fire(heartbeat, self.cooker.data)
|
||||
if nextsleep and now + nextsleep > self.next_heartbeat:
|
||||
# Shorten timeout so that we we wake up in time for
|
||||
# the heartbeat.
|
||||
nextsleep = self.next_heartbeat - now
|
||||
|
||||
if nextsleep is not None:
|
||||
select.select(fds,[],[],nextsleep)
|
||||
|
||||
@@ -224,6 +199,7 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
self.procserver.stop()
|
||||
|
||||
while self.procserver.is_alive():
|
||||
@@ -233,14 +209,11 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
|
||||
self.ui_channel.close()
|
||||
self.event_queue.close()
|
||||
self.event_queue.setexit()
|
||||
# XXX: Call explicity close in _writer to avoid
|
||||
# fd leakage because isn't called on Queue.close()
|
||||
self.event_queue._writer.close()
|
||||
|
||||
# Wrap Queue to provide API which isn't server implementation specific
|
||||
class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
def __init__(self, maxsize):
|
||||
multiprocessing.queues.Queue.__init__(self, maxsize, ctx=multiprocessing.get_context())
|
||||
multiprocessing.queues.Queue.__init__(self, maxsize)
|
||||
self.exit = False
|
||||
bb.utils.set_process_name("ProcessEQueue")
|
||||
|
||||
@@ -249,10 +222,11 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
|
||||
def waitEvent(self, timeout):
|
||||
if self.exit:
|
||||
return self.getEvent()
|
||||
sys.exit(1)
|
||||
try:
|
||||
if not self.server.is_alive():
|
||||
return self.getEvent()
|
||||
self.setexit()
|
||||
return None
|
||||
return self.get(True, timeout)
|
||||
except Empty:
|
||||
return None
|
||||
@@ -261,12 +235,12 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
try:
|
||||
if not self.server.is_alive():
|
||||
self.setexit()
|
||||
return None
|
||||
return self.get(False)
|
||||
except Empty:
|
||||
if self.exit:
|
||||
sys.exit(1)
|
||||
return None
|
||||
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
def initServer(self, single_use=True):
|
||||
# establish communication channels. We use bidirectional pipes for
|
||||
|
||||
@@ -31,33 +31,31 @@
|
||||
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 xmlrpclib, sys
|
||||
from bb import daemonize
|
||||
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
|
||||
|
||||
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):
|
||||
self.timeout = timeout
|
||||
self.connection_token = None
|
||||
xmlrpc.client.Transport.__init__(self)
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
|
||||
# Modified from default to pass timeout to HTTPConnection
|
||||
def make_connection(self, host):
|
||||
@@ -69,7 +67,7 @@ class BBTransport(xmlrpc.client.Transport):
|
||||
# create a HTTP connection object from a host descriptor
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
#store the host argument along with the connection object
|
||||
self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout)
|
||||
self._connection = host, httplib.HTTPConnection(chost, timeout=self.timeout)
|
||||
return self._connection[1]
|
||||
|
||||
def set_connection_token(self, token):
|
||||
@@ -78,30 +76,13 @@ class BBTransport(xmlrpc.client.Transport):
|
||||
def send_content(self, h, body):
|
||||
if 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):
|
||||
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
|
||||
|
||||
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():
|
||||
|
||||
def __init__(self, server):
|
||||
@@ -147,7 +128,7 @@ class BitBakeServerCommands():
|
||||
def addClient(self):
|
||||
if self.has_client:
|
||||
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.has_client = True
|
||||
return token
|
||||
@@ -190,14 +171,14 @@ class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(bytes(response, 'utf-8'))
|
||||
self.wfile.write(response)
|
||||
|
||||
|
||||
class XMLRPCProxyServer(BaseImplServer):
|
||||
""" not a real working server, but a stub for a proxy server connection
|
||||
|
||||
"""
|
||||
def __init__(self, host, port, use_builtin_types=True):
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
@@ -205,7 +186,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
# remove this when you're done with debugging
|
||||
# allow_reuse_address = True
|
||||
|
||||
def __init__(self, interface, single_use=False, idle_timeout=0):
|
||||
def __init__(self, interface, single_use=False):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
@@ -223,10 +204,6 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
self.commands = BitBakeServerCommands(self)
|
||||
self.autoregister_all_functions(self.commands, "")
|
||||
self.interface = interface
|
||||
self.time = time.time()
|
||||
self.idle_timeout = idle_timeout
|
||||
if idle_timeout:
|
||||
self.register_idle_function(self.handle_idle_timeout, self)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
BaseImplServer.addcooker(self, cooker)
|
||||
@@ -242,12 +219,6 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
if name.startswith(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):
|
||||
# Start the actual XMLRPC server
|
||||
@@ -261,7 +232,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
while not self.quit:
|
||||
fds = [self]
|
||||
nextsleep = 0.1
|
||||
for function, data in list(self._idlefuns.items()):
|
||||
for function, data in self._idlefuns.items():
|
||||
retval = None
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
@@ -290,15 +261,13 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
try:
|
||||
fd_sets = select.select(fds, [], [], socktimeout)
|
||||
if fd_sets[0] and self in fd_sets[0]:
|
||||
if self.idle_timeout:
|
||||
self.time = time.time()
|
||||
self._handle_request_noblock()
|
||||
except IOError:
|
||||
# we ignore interrupted calls
|
||||
pass
|
||||
|
||||
# Tell idle functions we're exiting
|
||||
for function, data in list(self._idlefuns.items()):
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, True)
|
||||
except:
|
||||
@@ -363,10 +332,9 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||
pass
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
def initServer(self, interface = ("localhost", 0),
|
||||
single_use = False, idle_timeout=0):
|
||||
def initServer(self, interface = ("localhost", 0), single_use = False):
|
||||
self.interface = interface
|
||||
self.serverImpl = XMLRPCServer(interface, single_use, idle_timeout)
|
||||
self.serverImpl = XMLRPCServer(interface, single_use)
|
||||
|
||||
def detach(self):
|
||||
daemonize.createDaemon(self.serverImpl.serve_forever, "bitbake-cookerdaemon.log")
|
||||
@@ -411,7 +379,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
|
||||
bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
|
||||
raise e
|
||||
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)
|
||||
return self.connection.connect(self.token)
|
||||
except Exception as e:
|
||||
|
||||
@@ -3,19 +3,22 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import pickle
|
||||
import bb.data
|
||||
import difflib
|
||||
import simplediff
|
||||
from bb.checksum import FileChecksumCache
|
||||
|
||||
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):
|
||||
siggens = [obj for obj in globals().values()
|
||||
siggens = [obj for obj in globals().itervalues()
|
||||
if type(obj) is type and issubclass(obj, SignatureGenerator)]
|
||||
|
||||
desired = d.getVar("BB_SIGNATURE_HANDLER") or "noop"
|
||||
desired = d.getVar("BB_SIGNATURE_HANDLER", True) or "noop"
|
||||
for sg in siggens:
|
||||
if desired == sg.name:
|
||||
return sg(d)
|
||||
@@ -84,10 +87,10 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
self.gendeps = {}
|
||||
self.lookupcache = {}
|
||||
self.pkgnameextract = re.compile("(?P<fn>.*)\..*")
|
||||
self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST") or "").split())
|
||||
self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST", True) or "").split())
|
||||
self.taskwhitelist = None
|
||||
self.init_rundepcheck(data)
|
||||
checksum_cache_file = data.getVar("BB_HASH_CHECKSUM_CACHE_FILE")
|
||||
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)
|
||||
@@ -95,7 +98,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
self.checksum_cache = None
|
||||
|
||||
def init_rundepcheck(self, data):
|
||||
self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST") or None
|
||||
self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST", True) or None
|
||||
if self.taskwhitelist:
|
||||
self.twl = re.compile(self.taskwhitelist)
|
||||
else:
|
||||
@@ -103,7 +106,6 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
|
||||
def _build_data(self, fn, d):
|
||||
|
||||
ignore_mismatch = ((d.getVar("BB_HASH_IGNORE_MISMATCH") or '') == '1')
|
||||
tasklist, gendeps, lookupcache = bb.data.generate_dependencies(d)
|
||||
|
||||
taskdeps = {}
|
||||
@@ -136,9 +138,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
var = lookupcache[dep]
|
||||
if var is not None:
|
||||
data = data + str(var)
|
||||
datahash = hashlib.md5(data.encode("utf-8")).hexdigest()
|
||||
datahash = hashlib.md5(data).hexdigest()
|
||||
k = fn + "." + task
|
||||
if not ignore_mismatch and k in self.basehash and self.basehash[k] != datahash:
|
||||
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
|
||||
@@ -151,21 +153,18 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
|
||||
def finalise(self, fn, d, variant):
|
||||
|
||||
mc = d.getVar("__BBMULTICONFIG", False) or ""
|
||||
if variant or mc:
|
||||
fn = bb.cache.realfn2virtual(fn, variant, mc)
|
||||
if variant:
|
||||
fn = "virtual:" + variant + ":" + fn
|
||||
|
||||
try:
|
||||
taskdeps = self._build_data(fn, d)
|
||||
except bb.parse.SkipRecipe:
|
||||
raise
|
||||
except:
|
||||
bb.warn("Error during finalise of %s" % fn)
|
||||
raise
|
||||
|
||||
#Slow but can be useful for debugging mismatched basehashes
|
||||
#for task in self.taskdeps[fn]:
|
||||
# self.dump_sigtask(fn, task, d.getVar("STAMP"), False)
|
||||
# self.dump_sigtask(fn, task, d.getVar("STAMP", True), False)
|
||||
|
||||
for task in taskdeps:
|
||||
d.setVar("BB_BASEHASH_task-%s" % task, self.basehash[fn + "." + task])
|
||||
@@ -227,9 +226,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
if taint:
|
||||
data = data + 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
|
||||
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
||||
return h
|
||||
@@ -302,7 +301,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
with os.fdopen(fd, "wb") as stream:
|
||||
p = pickle.dump(data, stream, -1)
|
||||
stream.flush()
|
||||
os.chmod(tmpfile, 0o664)
|
||||
os.chmod(tmpfile, 0664)
|
||||
os.rename(tmpfile, sigfile)
|
||||
except (OSError, IOError) as err:
|
||||
try:
|
||||
@@ -311,18 +310,16 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
pass
|
||||
raise err
|
||||
|
||||
def dump_sigfn(self, fn, dataCaches, options):
|
||||
if fn in self.taskdeps:
|
||||
def dump_sigs(self, dataCache, options):
|
||||
for fn in self.taskdeps:
|
||||
for task in self.taskdeps[fn]:
|
||||
tid = fn + ":" + task
|
||||
(mc, _, _) = bb.runqueue.split_tid(tid)
|
||||
k = fn + "." + task
|
||||
if k not in self.taskhash:
|
||||
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("The mismatched hashes were %s and %s" % (dataCaches[mc].basetaskhash[k], self.basehash[k]))
|
||||
self.dump_sigtask(fn, task, dataCaches[mc].stamp[fn], True)
|
||||
bb.error("The mismatched hashes were %s and %s" % (dataCache.basetaskhash[k], self.basehash[k]))
|
||||
self.dump_sigtask(fn, task, dataCache.stamp[fn], True)
|
||||
|
||||
class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
|
||||
name = "basichash"
|
||||
@@ -350,71 +347,15 @@ class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
|
||||
|
||||
def dump_this_task(outfile, d):
|
||||
import bb.parse
|
||||
fn = d.getVar("BB_FILENAME")
|
||||
task = "do_" + d.getVar("BB_CURRENTTASK")
|
||||
fn = d.getVar("BB_FILENAME", 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:" + referencestamp)
|
||||
|
||||
def init_colors(enable_color):
|
||||
"""Initialise colour dict for passing to compare_sigfiles()"""
|
||||
# First set up the colours
|
||||
colors = {'color_title': '\033[1;37;40m',
|
||||
'color_default': '\033[0;37;40m',
|
||||
'color_add': '\033[1;32;40m',
|
||||
'color_remove': '\033[1;31;40m',
|
||||
}
|
||||
# Leave all keys present but clear the values
|
||||
if not enable_color:
|
||||
for k in colors.keys():
|
||||
colors[k] = ''
|
||||
return colors
|
||||
|
||||
def worddiff_str(oldstr, newstr, colors=None):
|
||||
if not colors:
|
||||
colors = init_colors(False)
|
||||
diff = simplediff.diff(oldstr.split(' '), newstr.split(' '))
|
||||
ret = []
|
||||
for change, value in diff:
|
||||
value = ' '.join(value)
|
||||
if change == '=':
|
||||
ret.append(value)
|
||||
elif change == '+':
|
||||
item = '{color_add}{{+{value}+}}{color_default}'.format(value=value, **colors)
|
||||
ret.append(item)
|
||||
elif change == '-':
|
||||
item = '{color_remove}[-{value}-]{color_default}'.format(value=value, **colors)
|
||||
ret.append(item)
|
||||
whitespace_note = ''
|
||||
if oldstr != newstr and ' '.join(oldstr.split()) == ' '.join(newstr.split()):
|
||||
whitespace_note = ' (whitespace changed)'
|
||||
return '"%s"%s' % (' '.join(ret), whitespace_note)
|
||||
|
||||
def list_inline_diff(oldlist, newlist, colors=None):
|
||||
if not colors:
|
||||
colors = init_colors(False)
|
||||
diff = simplediff.diff(oldlist, newlist)
|
||||
ret = []
|
||||
for change, value in diff:
|
||||
value = ' '.join(value)
|
||||
if change == '=':
|
||||
ret.append("'%s'" % value)
|
||||
elif change == '+':
|
||||
item = '{color_add}+{value}{color_default}'.format(value=value, **colors)
|
||||
ret.append(item)
|
||||
elif change == '-':
|
||||
item = '{color_remove}-{value}{color_default}'.format(value=value, **colors)
|
||||
ret.append(item)
|
||||
return '[%s]' % (', '.join(ret))
|
||||
|
||||
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:"):
|
||||
b = b + ":" + a.rsplit(":", 1)[0]
|
||||
if mc:
|
||||
b = b + ":multiconfig:" + mc
|
||||
return b
|
||||
|
||||
def clean_basepaths(a):
|
||||
@@ -429,32 +370,13 @@ def clean_basepaths_list(a):
|
||||
b.append(clean_basepath(x))
|
||||
return b
|
||||
|
||||
def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
|
||||
def compare_sigfiles(a, b, recursecb = None):
|
||||
output = []
|
||||
|
||||
colors = init_colors(color)
|
||||
def color_format(formatstr, **values):
|
||||
"""
|
||||
Return colour formatted string.
|
||||
NOTE: call with the format string, not an already formatted string
|
||||
containing values (otherwise you could have trouble with { and }
|
||||
characters)
|
||||
"""
|
||||
if not formatstr.endswith('{color_default}'):
|
||||
formatstr += '{color_default}'
|
||||
# In newer python 3 versions you can pass both of these directly,
|
||||
# but we only require 3.4 at the moment
|
||||
formatparams = {}
|
||||
formatparams.update(colors)
|
||||
formatparams.update(values)
|
||||
return formatstr.format(**formatparams)
|
||||
|
||||
with open(a, 'rb') as f:
|
||||
p1 = pickle.Unpickler(f)
|
||||
a_data = p1.load()
|
||||
with open(b, 'rb') as f:
|
||||
p2 = pickle.Unpickler(f)
|
||||
b_data = p2.load()
|
||||
p1 = pickle.Unpickler(open(a, "rb"))
|
||||
a_data = p1.load()
|
||||
p2 = pickle.Unpickler(open(b, "rb"))
|
||||
b_data = p2.load()
|
||||
|
||||
def dict_diff(a, b, whitelist=set()):
|
||||
sa = set(a.keys())
|
||||
@@ -502,100 +424,65 @@ def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
|
||||
return changed, added, removed
|
||||
|
||||
if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
|
||||
output.append(color_format("{color_title}basewhitelist changed{color_default} from '%s' to '%s'") % (a_data['basewhitelist'], b_data['basewhitelist']))
|
||||
output.append("basewhitelist changed from '%s' to '%s'" % (a_data['basewhitelist'], b_data['basewhitelist']))
|
||||
if a_data['basewhitelist'] and b_data['basewhitelist']:
|
||||
output.append("changed items: %s" % a_data['basewhitelist'].symmetric_difference(b_data['basewhitelist']))
|
||||
|
||||
if 'taskwhitelist' in a_data and a_data['taskwhitelist'] != b_data['taskwhitelist']:
|
||||
output.append(color_format("{color_title}taskwhitelist changed{color_default} from '%s' to '%s'") % (a_data['taskwhitelist'], b_data['taskwhitelist']))
|
||||
output.append("taskwhitelist changed from '%s' to '%s'" % (a_data['taskwhitelist'], b_data['taskwhitelist']))
|
||||
if a_data['taskwhitelist'] and b_data['taskwhitelist']:
|
||||
output.append("changed items: %s" % a_data['taskwhitelist'].symmetric_difference(b_data['taskwhitelist']))
|
||||
|
||||
if a_data['taskdeps'] != b_data['taskdeps']:
|
||||
output.append(color_format("{color_title}Task dependencies changed{color_default} from:\n%s\nto:\n%s") % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
|
||||
output.append("Task dependencies changed from:\n%s\nto:\n%s" % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
|
||||
|
||||
if a_data['basehash'] != b_data['basehash'] and not collapsed:
|
||||
output.append(color_format("{color_title}basehash changed{color_default} from %s to %s") % (a_data['basehash'], b_data['basehash']))
|
||||
if a_data['basehash'] != b_data['basehash']:
|
||||
output.append("basehash changed from %s to %s" % (a_data['basehash'], b_data['basehash']))
|
||||
|
||||
changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'], a_data['basewhitelist'] & b_data['basewhitelist'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
output.append(color_format("{color_title}List of dependencies for variable %s changed from '{color_default}%s{color_title}' to '{color_default}%s{color_title}'") % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
|
||||
output.append("List of dependencies for variable %s changed from '%s' to '%s'" % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
|
||||
if a_data['gendeps'][dep] and b_data['gendeps'][dep]:
|
||||
output.append("changed items: %s" % a_data['gendeps'][dep].symmetric_difference(b_data['gendeps'][dep]))
|
||||
if added:
|
||||
for dep in added:
|
||||
output.append(color_format("{color_title}Dependency on variable %s was added") % (dep))
|
||||
output.append("Dependency on variable %s was added" % (dep))
|
||||
if removed:
|
||||
for dep in removed:
|
||||
output.append(color_format("{color_title}Dependency on Variable %s was removed") % (dep))
|
||||
output.append("Dependency on Variable %s was removed" % (dep))
|
||||
|
||||
|
||||
changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
|
||||
if changed:
|
||||
for dep in changed:
|
||||
oldval = a_data['varvals'][dep]
|
||||
newval = b_data['varvals'][dep]
|
||||
if newval and oldval and ('\n' in oldval or '\n' in newval):
|
||||
diff = difflib.unified_diff(oldval.splitlines(), newval.splitlines(), lineterm='')
|
||||
# Cut off the first two lines, since we aren't interested in
|
||||
# the old/new filename (they are blank anyway in this case)
|
||||
difflines = list(diff)[2:]
|
||||
if color:
|
||||
# Add colour to diff output
|
||||
for i, line in enumerate(difflines):
|
||||
if line.startswith('+'):
|
||||
line = color_format('{color_add}{line}', line=line)
|
||||
difflines[i] = line
|
||||
elif line.startswith('-'):
|
||||
line = color_format('{color_remove}{line}', line=line)
|
||||
difflines[i] = line
|
||||
output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff='\n'.join(difflines)))
|
||||
elif newval and oldval and (' ' in oldval or ' ' in newval):
|
||||
output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff=worddiff_str(oldval, newval, colors)))
|
||||
else:
|
||||
output.append(color_format("{color_title}Variable {var} value changed from '{color_default}{oldval}{color_title}' to '{color_default}{newval}{color_title}'{color_default}", var=dep, oldval=oldval, newval=newval))
|
||||
|
||||
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'] = {}
|
||||
output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
|
||||
|
||||
changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
|
||||
if changed:
|
||||
for f, old, new in changed:
|
||||
output.append(color_format("{color_title}Checksum for file %s changed{color_default} from %s to %s") % (f, old, new))
|
||||
output.append("Checksum for file %s changed from %s to %s" % (f, old, new))
|
||||
if added:
|
||||
for f in added:
|
||||
output.append(color_format("{color_title}Dependency on checksum of file %s was added") % (f))
|
||||
output.append("Dependency on checksum of file %s was added" % (f))
|
||||
if removed:
|
||||
for f in removed:
|
||||
output.append(color_format("{color_title}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 not collapsed:
|
||||
if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
|
||||
changed = ["Number of task dependencies changed"]
|
||||
else:
|
||||
changed = []
|
||||
for idx, task in enumerate(a_data['runtaskdeps']):
|
||||
a = a_data['runtaskdeps'][idx]
|
||||
b = b_data['runtaskdeps'][idx]
|
||||
if a_data['runtaskhashes'][a] != b_data['runtaskhashes'][b] and not collapsed:
|
||||
changed.append("%s with hash %s\n changed to\n%s with hash %s" % (clean_basepath(a), a_data['runtaskhashes'][a], clean_basepath(b), b_data['runtaskhashes'][b]))
|
||||
if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
|
||||
changed = ["Number of task dependencies changed"]
|
||||
else:
|
||||
changed = []
|
||||
for idx, task in enumerate(a_data['runtaskdeps']):
|
||||
a = a_data['runtaskdeps'][idx]
|
||||
b = b_data['runtaskdeps'][idx]
|
||||
if a_data['runtaskhashes'][a] != b_data['runtaskhashes'][b]:
|
||||
changed.append("%s with hash %s\n changed to\n%s with hash %s" % (a, a_data['runtaskhashes'][a], b, b_data['runtaskhashes'][b]))
|
||||
|
||||
if changed:
|
||||
clean_a = clean_basepaths_list(a_data['runtaskdeps'])
|
||||
clean_b = clean_basepaths_list(b_data['runtaskdeps'])
|
||||
if clean_a != clean_b:
|
||||
output.append(color_format("{color_title}runtaskdeps changed:{color_default}\n%s") % list_inline_diff(clean_a, clean_b, colors))
|
||||
else:
|
||||
output.append(color_format("{color_title}runtaskdeps changed:"))
|
||||
output.append("\n".join(changed))
|
||||
if changed:
|
||||
output.append("runtaskdeps changed from %s to %s" % (clean_basepaths_list(a_data['runtaskdeps']), clean_basepaths_list(b_data['runtaskdeps'])))
|
||||
output.append("\n".join(changed))
|
||||
|
||||
|
||||
if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data:
|
||||
@@ -611,7 +498,7 @@ def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
|
||||
#output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
|
||||
bdep_found = True
|
||||
if not bdep_found:
|
||||
output.append(color_format("{color_title}Dependency on task %s was added{color_default} with hash %s") % (clean_basepath(dep), b[dep]))
|
||||
output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), b[dep]))
|
||||
if removed:
|
||||
for dep in removed:
|
||||
adep_found = False
|
||||
@@ -621,25 +508,21 @@ def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
|
||||
#output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
|
||||
adep_found = True
|
||||
if not adep_found:
|
||||
output.append(color_format("{color_title}Dependency on task %s was removed{color_default} with hash %s") % (clean_basepath(dep), a[dep]))
|
||||
output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), a[dep]))
|
||||
if changed:
|
||||
for dep in changed:
|
||||
if not collapsed:
|
||||
output.append(color_format("{color_title}Hash for dependent task %s changed{color_default} from %s to %s") % (clean_basepath(dep), a[dep], b[dep]))
|
||||
output.append("Hash for dependent task %s changed from %s to %s" % (clean_basepath(dep), a[dep], b[dep]))
|
||||
if callable(recursecb):
|
||||
# If a dependent hash changed, might as well print the line above and then defer to the changes in
|
||||
# that hash since in all likelyhood, they're the same changes this task also saw.
|
||||
recout = recursecb(dep, a[dep], b[dep])
|
||||
if recout:
|
||||
if collapsed:
|
||||
output.extend(recout)
|
||||
else:
|
||||
# If a dependent hash changed, might as well print the line above and then defer to the changes in
|
||||
# that hash since in all likelyhood, they're the same changes this task also saw.
|
||||
output = [output[-1]] + recout
|
||||
output = [output[-1]] + recout
|
||||
|
||||
a_taint = a_data.get('taint', None)
|
||||
b_taint = b_data.get('taint', None)
|
||||
if a_taint != b_taint:
|
||||
output.append(color_format("{color_title}Taint (by forced/invalidated task) changed{color_default} from %s to %s") % (a_taint, b_taint))
|
||||
output.append("Taint (by forced/invalidated task) changed from %s to %s" % (a_taint, b_taint))
|
||||
|
||||
return output
|
||||
|
||||
@@ -658,7 +541,7 @@ def calc_basehash(sigdata):
|
||||
if val is not None:
|
||||
basedata = basedata + str(val)
|
||||
|
||||
return hashlib.md5(basedata.encode("utf-8")).hexdigest()
|
||||
return hashlib.md5(basedata).hexdigest()
|
||||
|
||||
def calc_taskhash(sigdata):
|
||||
data = sigdata['basehash']
|
||||
@@ -676,15 +559,14 @@ def calc_taskhash(sigdata):
|
||||
else:
|
||||
data = data + sigdata['taint']
|
||||
|
||||
return hashlib.md5(data.encode("utf-8")).hexdigest()
|
||||
return hashlib.md5(data).hexdigest()
|
||||
|
||||
|
||||
def dump_sigfile(a):
|
||||
output = []
|
||||
|
||||
with open(a, 'rb') as f:
|
||||
p1 = pickle.Unpickler(f)
|
||||
a_data = p1.load()
|
||||
p1 = pickle.Unpickler(open(a, "rb"))
|
||||
a_data = p1.load()
|
||||
|
||||
output.append("basewhitelist: %s" % (a_data['basewhitelist']))
|
||||
|
||||
|
||||
@@ -37,24 +37,27 @@ def re_match_strings(target, strings):
|
||||
return any(name == target or re.match(name, target)
|
||||
for name in strings)
|
||||
|
||||
class TaskEntry:
|
||||
def __init__(self):
|
||||
self.tdepends = []
|
||||
self.idepends = []
|
||||
self.irdepends = []
|
||||
|
||||
class TaskData:
|
||||
"""
|
||||
BitBake Task Data implementation
|
||||
"""
|
||||
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.run_targets = {}
|
||||
|
||||
self.external_targets = []
|
||||
|
||||
self.seenfns = []
|
||||
self.taskentries = {}
|
||||
self.tasks_fnid = []
|
||||
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.rdepids = {}
|
||||
@@ -63,7 +66,7 @@ class TaskData:
|
||||
|
||||
self.failed_deps = []
|
||||
self.failed_rdeps = []
|
||||
self.failed_fns = []
|
||||
self.failed_fnids = []
|
||||
|
||||
self.abort = abort
|
||||
self.tryaltconfigs = tryaltconfigs
|
||||
@@ -71,6 +74,88 @@ class TaskData:
|
||||
|
||||
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):
|
||||
"""
|
||||
Add tasks for a given fn to the database
|
||||
@@ -78,60 +163,60 @@ class TaskData:
|
||||
|
||||
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...")
|
||||
|
||||
# Check if we've already seen this fn
|
||||
if fn in self.seenfns:
|
||||
if fnid in self.tasks_fnid:
|
||||
return
|
||||
|
||||
self.seenfns.append(fn)
|
||||
|
||||
self.add_extra_deps(fn, dataCache)
|
||||
|
||||
# Common code for dep_name/depends = 'depends'/idepends and 'rdepends'/irdepends
|
||||
def handle_deps(task, dep_name, depends, seen):
|
||||
if dep_name in task_deps and task in task_deps[dep_name]:
|
||||
ids = []
|
||||
for dep in task_deps[dep_name][task].split():
|
||||
if dep:
|
||||
parts = dep.split(":")
|
||||
if len(parts) != 2:
|
||||
bb.msg.fatal("TaskData", "Error for %s:%s[%s], dependency %s in '%s' does not contain exactly one ':' character.\n Task '%s' should be specified in the form 'packagename:task'" % (fn, task, dep_name, dep, task_deps[dep_name][task], dep_name))
|
||||
ids.append((parts[0], parts[1]))
|
||||
seen(parts[0])
|
||||
depends.extend(ids)
|
||||
|
||||
for task in task_deps['tasks']:
|
||||
|
||||
tid = "%s:%s" % (fn, task)
|
||||
self.taskentries[tid] = TaskEntry()
|
||||
|
||||
# Work out task dependencies
|
||||
parentids = []
|
||||
for dep in task_deps['parents'][task]:
|
||||
if dep not in task_deps['tasks']:
|
||||
bb.debug(2, "Not adding dependeny of %s on %s since %s does not exist" % (task, dep, dep))
|
||||
continue
|
||||
parentid = "%s:%s" % (fn, dep)
|
||||
parentid = self.gettask_id(fn, dep)
|
||||
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
|
||||
handle_deps(task, 'depends', self.taskentries[tid].idepends, self.seen_build_target)
|
||||
handle_deps(task, 'rdepends', self.taskentries[tid].irdepends, self.seen_run_target)
|
||||
if 'depends' in task_deps and task in task_deps['depends']:
|
||||
ids = []
|
||||
for dep in task_deps['depends'][task].split():
|
||||
if dep:
|
||||
if ":" not in dep:
|
||||
bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (fn, dep))
|
||||
ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
|
||||
self.tasks_idepends[taskid].extend(ids)
|
||||
if 'rdepends' in task_deps and task in task_deps['rdepends']:
|
||||
ids = []
|
||||
for dep in task_deps['rdepends'][task].split():
|
||||
if dep:
|
||||
if ":" not in dep:
|
||||
bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'rdepends' should be specified in the form 'packagename:task'" % (fn, dep))
|
||||
ids.append(((self.getrun_id(dep.split(":")[0])), dep.split(":")[1]))
|
||||
self.tasks_irdepends[taskid].extend(ids)
|
||||
|
||||
|
||||
# Work out build dependencies
|
||||
if not fn in self.depids:
|
||||
dependids = set()
|
||||
if not fnid in self.depids:
|
||||
dependids = {}
|
||||
for depend in dataCache.deps[fn]:
|
||||
dependids.add(depend)
|
||||
self.depids[fn] = list(dependids)
|
||||
dependids[self.getbuild_id(depend)] = None
|
||||
self.depids[fnid] = dependids.keys()
|
||||
logger.debug(2, "Added dependencies %s for %s", str(dataCache.deps[fn]), fn)
|
||||
|
||||
# Work out runtime dependencies
|
||||
if not fn in self.rdepids:
|
||||
rdependids = set()
|
||||
if not fnid in self.rdepids:
|
||||
rdependids = {}
|
||||
rdepends = dataCache.rundeps[fn]
|
||||
rrecs = dataCache.runrecs[fn]
|
||||
rdependlist = []
|
||||
@@ -139,26 +224,24 @@ class TaskData:
|
||||
for package in rdepends:
|
||||
for rdepend in rdepends[package]:
|
||||
rdependlist.append(rdepend)
|
||||
rdependids.add(rdepend)
|
||||
rdependids[self.getrun_id(rdepend)] = None
|
||||
for package in rrecs:
|
||||
for rdepend in rrecs[package]:
|
||||
rreclist.append(rdepend)
|
||||
rdependids.add(rdepend)
|
||||
rdependids[self.getrun_id(rdepend)] = None
|
||||
if rdependlist:
|
||||
logger.debug(2, "Added runtime dependencies %s for %s", str(rdependlist), fn)
|
||||
if rreclist:
|
||||
logger.debug(2, "Added runtime recommendations %s for %s", str(rreclist), fn)
|
||||
self.rdepids[fn] = list(rdependids)
|
||||
self.rdepids[fnid] = rdependids.keys()
|
||||
|
||||
for dep in self.depids[fn]:
|
||||
self.seen_build_target(dep)
|
||||
for dep in self.depids[fnid]:
|
||||
if dep in self.failed_deps:
|
||||
self.fail_fn(fn)
|
||||
self.fail_fnid(fnid)
|
||||
return
|
||||
for dep in self.rdepids[fn]:
|
||||
self.seen_run_target(dep)
|
||||
for dep in self.rdepids[fnid]:
|
||||
if dep in self.failed_rdeps:
|
||||
self.fail_fn(fn)
|
||||
self.fail_fnid(fnid)
|
||||
return
|
||||
|
||||
def add_extra_deps(self, fn, dataCache):
|
||||
@@ -180,7 +263,9 @@ class TaskData:
|
||||
"""
|
||||
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 False
|
||||
|
||||
@@ -188,54 +273,50 @@ class TaskData:
|
||||
"""
|
||||
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 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):
|
||||
"""
|
||||
Add a build target.
|
||||
If already present, append the provider fn to the list
|
||||
"""
|
||||
if item in self.build_targets:
|
||||
if fn in self.build_targets[item]:
|
||||
return
|
||||
self.build_targets[item].append(fn)
|
||||
return
|
||||
self.build_targets[item] = [fn]
|
||||
targetid = self.getbuild_id(item)
|
||||
fnid = self.getfn_id(fn)
|
||||
|
||||
def seen_run_target(self, name):
|
||||
"""
|
||||
Maintain a list of runtime build targets
|
||||
"""
|
||||
if name not in self.run_targets:
|
||||
self.run_targets[name] = []
|
||||
if targetid in self.build_targets:
|
||||
if fnid in self.build_targets[targetid]:
|
||||
return
|
||||
self.build_targets[targetid].append(fnid)
|
||||
return
|
||||
self.build_targets[targetid] = [fnid]
|
||||
|
||||
def add_runtime_target(self, fn, item):
|
||||
"""
|
||||
Add a runtime target.
|
||||
If already present, append the provider fn to the list
|
||||
"""
|
||||
if item in self.run_targets:
|
||||
if fn in self.run_targets[item]:
|
||||
return
|
||||
self.run_targets[item].append(fn)
|
||||
return
|
||||
self.run_targets[item] = [fn]
|
||||
targetid = self.getrun_id(item)
|
||||
fnid = self.getfn_id(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
|
||||
"""
|
||||
if target not in self.external_targets:
|
||||
self.external_targets.append(target)
|
||||
targetid = self.getbuild_id(item)
|
||||
|
||||
if targetid not in self.external_targets:
|
||||
self.external_targets.append(targetid)
|
||||
|
||||
def get_unresolved_build_targets(self, dataCache):
|
||||
"""
|
||||
@@ -243,12 +324,12 @@ class TaskData:
|
||||
are unknown.
|
||||
"""
|
||||
unresolved = []
|
||||
for target in self.build_targets:
|
||||
for target in self.build_names_index:
|
||||
if re_match_strings(target, dataCache.ignored_dependencies):
|
||||
continue
|
||||
if target in self.failed_deps:
|
||||
if self.build_names_index.index(target) in self.failed_deps:
|
||||
continue
|
||||
if not self.build_targets[target]:
|
||||
if not self.have_build_target(target):
|
||||
unresolved.append(target)
|
||||
return unresolved
|
||||
|
||||
@@ -258,12 +339,12 @@ class TaskData:
|
||||
are unknown.
|
||||
"""
|
||||
unresolved = []
|
||||
for target in self.run_targets:
|
||||
for target in self.run_names_index:
|
||||
if re_match_strings(target, dataCache.ignored_dependencies):
|
||||
continue
|
||||
if target in self.failed_rdeps:
|
||||
if self.run_names_index.index(target) in self.failed_rdeps:
|
||||
continue
|
||||
if not self.run_targets[target]:
|
||||
if not self.have_runtime_target(target):
|
||||
unresolved.append(target)
|
||||
return unresolved
|
||||
|
||||
@@ -271,26 +352,50 @@ class TaskData:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
dependees = []
|
||||
for fn in self.depids:
|
||||
if item in self.depids[fn]:
|
||||
dependees.append(fn)
|
||||
for fnid in self.depids:
|
||||
if itemid in self.depids[fnid]:
|
||||
dependees.append(fnid)
|
||||
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
|
||||
"""
|
||||
dependees = []
|
||||
for fn in self.rdepids:
|
||||
if item in self.rdepids[fn]:
|
||||
dependees.append(fn)
|
||||
for fnid in self.rdepids:
|
||||
if itemid in self.rdepids[fnid]:
|
||||
dependees.append(fnid)
|
||||
return dependees
|
||||
|
||||
def get_rdependees_str(self, item):
|
||||
"""
|
||||
Return a list of targets which depend on runtime item as a user readable string
|
||||
"""
|
||||
itemid = self.getrun_id(item)
|
||||
dependees = []
|
||||
for fnid in self.rdepids:
|
||||
if itemid in self.rdepids[fnid]:
|
||||
dependees.append(self.fn_index[fnid])
|
||||
return dependees
|
||||
|
||||
def get_reasons(self, item, runtime=False):
|
||||
@@ -326,7 +431,7 @@ class TaskData:
|
||||
except bb.providers.NoProvider:
|
||||
if self.abort:
|
||||
raise
|
||||
self.remove_buildtarget(item)
|
||||
self.remove_buildtarget(self.getbuild_id(item))
|
||||
|
||||
self.mark_external_target(item)
|
||||
|
||||
@@ -341,14 +446,14 @@ class TaskData:
|
||||
return
|
||||
|
||||
if not item in dataCache.providers:
|
||||
close_matches = self.get_close_matches(item, list(dataCache.providers.keys()))
|
||||
close_matches = self.get_close_matches(item, dataCache.providers.keys())
|
||||
# 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)
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item), close_matches=close_matches), cfgData)
|
||||
raise bb.providers.NoProvider(item)
|
||||
|
||||
if self.have_build_target(item):
|
||||
@@ -357,10 +462,10 @@ class TaskData:
|
||||
all_p = dataCache.providers[item]
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
if len(eligible) > 1 and foundUnique == False:
|
||||
@@ -372,7 +477,8 @@ class TaskData:
|
||||
self.consider_msgs_cache.append(item)
|
||||
|
||||
for fn in eligible:
|
||||
if fn in self.failed_fns:
|
||||
fnid = self.getfn_id(fn)
|
||||
if fnid in self.failed_fnids:
|
||||
continue
|
||||
logger.debug(2, "adding %s to satisfy %s", fn, item)
|
||||
self.add_build_target(fn, item)
|
||||
@@ -396,14 +502,14 @@ class TaskData:
|
||||
all_p = bb.providers.getRuntimeProviders(dataCache, item)
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
if len(eligible) > 1 and numberPreferred == 0:
|
||||
@@ -425,80 +531,82 @@ class TaskData:
|
||||
|
||||
# run through the list until we find one that we can build
|
||||
for fn in eligible:
|
||||
if fn in self.failed_fns:
|
||||
fnid = self.getfn_id(fn)
|
||||
if fnid in self.failed_fnids:
|
||||
continue
|
||||
logger.debug(2, "adding '%s' to satisfy runtime '%s'", fn, item)
|
||||
self.add_runtime_target(fn, item)
|
||||
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)
|
||||
Remove any references from build and runtime provider lists
|
||||
|
||||
missing_list, A list of missing requirements for this target
|
||||
"""
|
||||
if fn in self.failed_fns:
|
||||
if fnid in self.failed_fnids:
|
||||
return
|
||||
if not missing_list:
|
||||
missing_list = []
|
||||
logger.debug(1, "File '%s' is unbuildable, removing...", fn)
|
||||
self.failed_fns.append(fn)
|
||||
logger.debug(1, "File '%s' is unbuildable, removing...", self.fn_index[fnid])
|
||||
self.failed_fnids.append(fnid)
|
||||
for target in self.build_targets:
|
||||
if fn in self.build_targets[target]:
|
||||
self.build_targets[target].remove(fn)
|
||||
if fnid in self.build_targets[target]:
|
||||
self.build_targets[target].remove(fnid)
|
||||
if len(self.build_targets[target]) == 0:
|
||||
self.remove_buildtarget(target, missing_list)
|
||||
for target in self.run_targets:
|
||||
if fn in self.run_targets[target]:
|
||||
self.run_targets[target].remove(fn)
|
||||
if fnid in self.run_targets[target]:
|
||||
self.run_targets[target].remove(fnid)
|
||||
if len(self.run_targets[target]) == 0:
|
||||
self.remove_runtarget(target, missing_list)
|
||||
|
||||
def remove_buildtarget(self, target, missing_list=None):
|
||||
def remove_buildtarget(self, targetid, missing_list=None):
|
||||
"""
|
||||
Mark a build target as failed (unbuildable)
|
||||
Trigger removal of any files that have this as a dependency
|
||||
"""
|
||||
if not missing_list:
|
||||
missing_list = [target]
|
||||
missing_list = [self.build_names_index[targetid]]
|
||||
else:
|
||||
missing_list = [target] + missing_list
|
||||
logger.verbose("Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", target, missing_list)
|
||||
self.failed_deps.append(target)
|
||||
dependees = self.get_dependees(target)
|
||||
for fn in dependees:
|
||||
self.fail_fn(fn, missing_list)
|
||||
for tid in self.taskentries:
|
||||
for (idepend, idependtask) in self.taskentries[tid].idepends:
|
||||
if idepend == target:
|
||||
fn = tid.rsplit(":",1)[0]
|
||||
self.fail_fn(fn, 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", self.build_names_index[targetid], missing_list)
|
||||
self.failed_deps.append(targetid)
|
||||
dependees = self.get_dependees(targetid)
|
||||
for fnid in dependees:
|
||||
self.fail_fnid(fnid, missing_list)
|
||||
for taskid in xrange(len(self.tasks_idepends)):
|
||||
idepends = self.tasks_idepends[taskid]
|
||||
for (idependid, idependtask) in idepends:
|
||||
if idependid == targetid:
|
||||
self.fail_fnid(self.tasks_fnid[taskid], missing_list)
|
||||
|
||||
if self.abort and 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)
|
||||
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)
|
||||
Trigger removal of any files that have this as a dependency
|
||||
"""
|
||||
if not missing_list:
|
||||
missing_list = [target]
|
||||
missing_list = [self.run_names_index[targetid]]
|
||||
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)
|
||||
self.failed_rdeps.append(target)
|
||||
dependees = self.get_rdependees(target)
|
||||
for fn in dependees:
|
||||
self.fail_fn(fn, missing_list)
|
||||
for tid in self.taskentries:
|
||||
for (idepend, idependtask) in self.taskentries[tid].irdepends:
|
||||
if idepend == target:
|
||||
fn = tid.rsplit(":",1)[0]
|
||||
self.fail_fn(fn, 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(targetid)
|
||||
dependees = self.get_rdependees(targetid)
|
||||
for fnid in dependees:
|
||||
self.fail_fnid(fnid, missing_list)
|
||||
for taskid in xrange(len(self.tasks_irdepends)):
|
||||
irdepends = self.tasks_irdepends[taskid]
|
||||
for (idependid, idependtask) in irdepends:
|
||||
if idependid == targetid:
|
||||
self.fail_fnid(self.tasks_fnid[taskid], missing_list)
|
||||
|
||||
def add_unresolved(self, cfgData, dataCache):
|
||||
"""
|
||||
@@ -512,16 +620,17 @@ class TaskData:
|
||||
self.add_provider_internal(cfgData, dataCache, target)
|
||||
added = added + 1
|
||||
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
|
||||
if not self.allowincomplete:
|
||||
self.remove_buildtarget(target)
|
||||
self.remove_buildtarget(targetid)
|
||||
for target in self.get_unresolved_run_targets(dataCache):
|
||||
try:
|
||||
self.add_rprovider(cfgData, dataCache, target)
|
||||
added = added + 1
|
||||
except (bb.providers.NoRProvider, bb.providers.MultipleRProvider):
|
||||
self.remove_runtarget(target)
|
||||
self.remove_runtarget(self.getrun_id(target))
|
||||
logger.debug(1, "Resolved " + str(added) + " extra dependencies")
|
||||
if added == 0:
|
||||
break
|
||||
@@ -529,13 +638,13 @@ class TaskData:
|
||||
|
||||
def get_providermap(self, prefix=None):
|
||||
provmap = {}
|
||||
for name in self.build_targets:
|
||||
for name in self.build_names_index:
|
||||
if prefix and not name.startswith(prefix):
|
||||
continue
|
||||
if self.have_build_target(name):
|
||||
provider = self.get_provider(name)
|
||||
if provider:
|
||||
provmap[name] = provider[0]
|
||||
provmap[name] = self.fn_index[provider[0]]
|
||||
return provmap
|
||||
|
||||
def dump_data(self):
|
||||
@@ -543,37 +652,39 @@ class TaskData:
|
||||
Dump some debug information on the internal data structures
|
||||
"""
|
||||
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, ", ".join(self.run_targets))
|
||||
logger.debug(3, ", ".join(self.run_names_index))
|
||||
|
||||
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"
|
||||
if target in self.build_targets:
|
||||
targets = self.build_targets[target]
|
||||
logger.debug(3, " %s: %s", target, targets)
|
||||
if buildid in self.build_targets:
|
||||
targets = self.build_targets[buildid]
|
||||
logger.debug(3, " (%s)%s: %s", buildid, target, 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"
|
||||
if target in self.run_targets:
|
||||
targets = self.run_targets[target]
|
||||
logger.debug(3, " %s: %s", target, targets)
|
||||
if runid in self.run_targets:
|
||||
targets = self.run_targets[runid]
|
||||
logger.debug(3, " (%s)%s: %s", runid, target, targets)
|
||||
|
||||
logger.debug(3, "tasks:")
|
||||
for tid in self.taskentries:
|
||||
logger.debug(3, " %s: %s %s %s",
|
||||
tid,
|
||||
self.taskentries[tid].idepends,
|
||||
self.taskentries[tid].irdepends,
|
||||
self.taskentries[tid].tdepends)
|
||||
for task in xrange(len(self.tasks_name)):
|
||||
logger.debug(3, " (%s)%s - %s: %s",
|
||||
task,
|
||||
self.fn_index[self.tasks_fnid[task]],
|
||||
self.tasks_name[task],
|
||||
self.tasks_tdepends[task])
|
||||
|
||||
logger.debug(3, "dependency ids (per fn):")
|
||||
for fn in self.depids:
|
||||
logger.debug(3, " %s: %s", fn, self.depids[fn])
|
||||
for fnid in self.depids:
|
||||
logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.depids[fnid])
|
||||
|
||||
logger.debug(3, "runtime dependency ids (per fn):")
|
||||
for fn in self.rdepids:
|
||||
logger.debug(3, " %s: %s", fn, self.rdepids[fn])
|
||||
for fnid in self.rdepids:
|
||||
logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.rdepids[fnid])
|
||||
|
||||
@@ -49,9 +49,6 @@ class ReferenceTest(unittest.TestCase):
|
||||
def assertExecs(self, execs):
|
||||
self.assertEqual(self.execs, execs)
|
||||
|
||||
def assertContains(self, contains):
|
||||
self.assertEqual(self.contains, contains)
|
||||
|
||||
class VariableReferenceTest(ReferenceTest):
|
||||
|
||||
def parseExpression(self, exp):
|
||||
@@ -71,7 +68,7 @@ class VariableReferenceTest(ReferenceTest):
|
||||
|
||||
def test_python_reference(self):
|
||||
self.setEmptyVars(["BAR"])
|
||||
self.parseExpression("${@d.getVar('BAR') + 'foo'}")
|
||||
self.parseExpression("${@bb.data.getVar('BAR', d, True) + 'foo'}")
|
||||
self.assertReferences(set(["BAR"]))
|
||||
|
||||
class ShellReferenceTest(ReferenceTest):
|
||||
@@ -194,8 +191,8 @@ class PythonReferenceTest(ReferenceTest):
|
||||
if hasattr(bb.utils, "_context"):
|
||||
self.context = bb.utils._context
|
||||
else:
|
||||
import builtins
|
||||
self.context = builtins.__dict__
|
||||
import __builtin__
|
||||
self.context = __builtin__.__dict__
|
||||
|
||||
def parseExpression(self, exp):
|
||||
parsedvar = self.d.expandWithRefs(exp, None)
|
||||
@@ -204,7 +201,6 @@ class PythonReferenceTest(ReferenceTest):
|
||||
|
||||
self.references = parsedvar.references | parser.references
|
||||
self.execs = parser.execs
|
||||
self.contains = parser.contains
|
||||
|
||||
@staticmethod
|
||||
def indent(value):
|
||||
@@ -213,17 +209,17 @@ be. These unit tests are testing snippets."""
|
||||
return " " + value
|
||||
|
||||
def test_getvar_reference(self):
|
||||
self.parseExpression("d.getVar('foo')")
|
||||
self.parseExpression("bb.data.getVar('foo', d, True)")
|
||||
self.assertReferences(set(["foo"]))
|
||||
self.assertExecs(set())
|
||||
|
||||
def test_getvar_computed_reference(self):
|
||||
self.parseExpression("d.getVar('f' + 'o' + 'o')")
|
||||
self.parseExpression("bb.data.getVar('f' + 'o' + 'o', d, True)")
|
||||
self.assertReferences(set())
|
||||
self.assertExecs(set())
|
||||
|
||||
def test_getvar_exec_reference(self):
|
||||
self.parseExpression("eval('d.getVar(\"foo\")')")
|
||||
self.parseExpression("eval('bb.data.getVar(\"foo\", d, True)')")
|
||||
self.assertReferences(set())
|
||||
self.assertExecs(set(["eval"]))
|
||||
|
||||
@@ -269,35 +265,15 @@ be. These unit tests are testing snippets."""
|
||||
self.assertExecs(set(["testget"]))
|
||||
del self.context["testget"]
|
||||
|
||||
def test_contains(self):
|
||||
self.parseExpression('bb.utils.contains("TESTVAR", "one", "true", "false", d)')
|
||||
self.assertContains({'TESTVAR': {'one'}})
|
||||
|
||||
def test_contains_multi(self):
|
||||
self.parseExpression('bb.utils.contains("TESTVAR", "one two", "true", "false", d)')
|
||||
self.assertContains({'TESTVAR': {'one two'}})
|
||||
|
||||
def test_contains_any(self):
|
||||
self.parseExpression('bb.utils.contains_any("TESTVAR", "hello", "true", "false", d)')
|
||||
self.assertContains({'TESTVAR': {'hello'}})
|
||||
|
||||
def test_contains_any_multi(self):
|
||||
self.parseExpression('bb.utils.contains_any("TESTVAR", "one two three", "true", "false", d)')
|
||||
self.assertContains({'TESTVAR': {'one', 'two', 'three'}})
|
||||
|
||||
def test_contains_filter(self):
|
||||
self.parseExpression('bb.utils.filter("TESTVAR", "hello there world", d)')
|
||||
self.assertContains({'TESTVAR': {'hello', 'there', 'world'}})
|
||||
|
||||
|
||||
class DependencyReferenceTest(ReferenceTest):
|
||||
|
||||
pydata = """
|
||||
d.getVar('somevar')
|
||||
bb.data.getVar('somevar', d, True)
|
||||
def test(d):
|
||||
foo = 'bar %s' % 'foo'
|
||||
def test2(d):
|
||||
d.getVar(foo)
|
||||
d.getVar(foo, True)
|
||||
d.getVar('bar', False)
|
||||
test2(d)
|
||||
|
||||
@@ -309,9 +285,9 @@ def a():
|
||||
|
||||
test(d)
|
||||
|
||||
d.expand(d.getVar("something", False))
|
||||
d.expand("${inexpand} somethingelse")
|
||||
d.getVar(a(), False)
|
||||
bb.data.expand(bb.data.getVar("something", False, d), d)
|
||||
bb.data.expand("${inexpand} somethingelse", d)
|
||||
bb.data.getVar(a(), d, False)
|
||||
"""
|
||||
|
||||
def test_python(self):
|
||||
@@ -326,7 +302,7 @@ d.getVar(a(), False)
|
||||
|
||||
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 = """
|
||||
@@ -373,7 +349,7 @@ esac
|
||||
|
||||
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):
|
||||
@@ -383,7 +359,7 @@ esac
|
||||
|
||||
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):
|
||||
self.d.setVar("oe_libinstall", "echo test")
|
||||
@@ -392,31 +368,7 @@ esac
|
||||
|
||||
deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
|
||||
|
||||
self.assertEqual(deps, set(["oe_libinstall"]))
|
||||
|
||||
def test_contains_vardeps(self):
|
||||
expr = '${@bb.utils.filter("TESTVAR", "somevalue anothervalue", d)} \
|
||||
${@bb.utils.contains("TESTVAR", "testval testval2", "yetanothervalue", "", d)} \
|
||||
${@bb.utils.contains("TESTVAR", "testval2 testval3", "blah", "", d)} \
|
||||
${@bb.utils.contains_any("TESTVAR", "testval2 testval3", "lastone", "", d)}'
|
||||
parsedvar = self.d.expandWithRefs(expr, None)
|
||||
# Check contains
|
||||
self.assertEqual(parsedvar.contains, {'TESTVAR': {'testval2 testval3', 'anothervalue', 'somevalue', 'testval testval2', 'testval2', 'testval3'}})
|
||||
# Check dependencies
|
||||
self.d.setVar('ANOTHERVAR', expr)
|
||||
self.d.setVar('TESTVAR', 'anothervalue testval testval2')
|
||||
deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), self.d)
|
||||
self.assertEqual(sorted(values.splitlines()),
|
||||
sorted([expr,
|
||||
'TESTVAR{anothervalue} = Set',
|
||||
'TESTVAR{somevalue} = Unset',
|
||||
'TESTVAR{testval testval2} = Set',
|
||||
'TESTVAR{testval2 testval3} = Unset',
|
||||
'TESTVAR{testval2} = Set',
|
||||
'TESTVAR{testval3} = Unset'
|
||||
]))
|
||||
# Check final value
|
||||
self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['anothervalue', 'yetanothervalue', 'lastone'])
|
||||
self.assertEquals(deps, set(["oe_libinstall"]))
|
||||
|
||||
#Currently no wildcard support
|
||||
#def test_vardeps_wildcards(self):
|
||||
|
||||
@@ -34,14 +34,14 @@ class COWTestCase(unittest.TestCase):
|
||||
from bb.COW import COWDictBase
|
||||
a = COWDictBase.copy()
|
||||
|
||||
self.assertEqual(False, 'a' in a)
|
||||
self.assertEquals(False, a.has_key('a'))
|
||||
|
||||
a['a'] = 'a'
|
||||
a['b'] = 'b'
|
||||
self.assertEqual(True, 'a' in a)
|
||||
self.assertEqual(True, 'b' in a)
|
||||
self.assertEqual('a', a['a'] )
|
||||
self.assertEqual('b', a['b'] )
|
||||
self.assertEquals(True, a.has_key('a'))
|
||||
self.assertEquals(True, a.has_key('b'))
|
||||
self.assertEquals('a', a['a'] )
|
||||
self.assertEquals('b', a['b'] )
|
||||
|
||||
def testCopyCopy(self):
|
||||
"""
|
||||
@@ -60,31 +60,31 @@ class COWTestCase(unittest.TestCase):
|
||||
c['a'] = 30
|
||||
|
||||
# test separation of the two instances
|
||||
self.assertEqual(False, 'c' in c)
|
||||
self.assertEqual(30, c['a'])
|
||||
self.assertEqual(10, b['a'])
|
||||
self.assertEquals(False, c.has_key('c'))
|
||||
self.assertEquals(30, c['a'])
|
||||
self.assertEquals(10, b['a'])
|
||||
|
||||
# test copy
|
||||
b_2 = b.copy()
|
||||
c_2 = c.copy()
|
||||
|
||||
self.assertEqual(False, 'c' in c_2)
|
||||
self.assertEqual(10, b_2['a'])
|
||||
self.assertEquals(False, c_2.has_key('c'))
|
||||
self.assertEquals(10, b_2['a'])
|
||||
|
||||
b_2['d'] = 40
|
||||
self.assertEqual(False, 'd' in c_2)
|
||||
self.assertEqual(True, 'd' in b_2)
|
||||
self.assertEqual(40, b_2['d'])
|
||||
self.assertEqual(False, 'd' in b)
|
||||
self.assertEqual(False, 'd' in c)
|
||||
self.assertEquals(False, c_2.has_key('d'))
|
||||
self.assertEquals(True, b_2.has_key('d'))
|
||||
self.assertEquals(40, b_2['d'])
|
||||
self.assertEquals(False, b.has_key('d'))
|
||||
self.assertEquals(False, c.has_key('d'))
|
||||
|
||||
c_2['d'] = 30
|
||||
self.assertEqual(True, 'd' in c_2)
|
||||
self.assertEqual(True, 'd' in b_2)
|
||||
self.assertEqual(30, c_2['d'])
|
||||
self.assertEqual(40, b_2['d'])
|
||||
self.assertEqual(False, 'd' in b)
|
||||
self.assertEqual(False, 'd' in c)
|
||||
self.assertEquals(True, c_2.has_key('d'))
|
||||
self.assertEquals(True, b_2.has_key('d'))
|
||||
self.assertEquals(30, c_2['d'])
|
||||
self.assertEquals(40, b_2['d'])
|
||||
self.assertEquals(False, b.has_key('d'))
|
||||
self.assertEquals(False, c.has_key('d'))
|
||||
|
||||
# test copy of the copy
|
||||
c_3 = c_2.copy()
|
||||
@@ -92,19 +92,19 @@ class COWTestCase(unittest.TestCase):
|
||||
b_3_2 = b_2.copy()
|
||||
|
||||
c_3['e'] = 4711
|
||||
self.assertEqual(4711, c_3['e'])
|
||||
self.assertEqual(False, 'e' in c_2)
|
||||
self.assertEqual(False, 'e' in b_3)
|
||||
self.assertEqual(False, 'e' in b_3_2)
|
||||
self.assertEqual(False, 'e' in b_2)
|
||||
self.assertEquals(4711, c_3['e'])
|
||||
self.assertEquals(False, c_2.has_key('e'))
|
||||
self.assertEquals(False, b_3.has_key('e'))
|
||||
self.assertEquals(False, b_3_2.has_key('e'))
|
||||
self.assertEquals(False, b_2.has_key('e'))
|
||||
|
||||
b_3['e'] = 'viel'
|
||||
self.assertEqual('viel', b_3['e'])
|
||||
self.assertEqual(4711, c_3['e'])
|
||||
self.assertEqual(False, 'e' in c_2)
|
||||
self.assertEqual(True, 'e' in b_3)
|
||||
self.assertEqual(False, 'e' in b_3_2)
|
||||
self.assertEqual(False, 'e' in b_2)
|
||||
self.assertEquals('viel', b_3['e'])
|
||||
self.assertEquals(4711, c_3['e'])
|
||||
self.assertEquals(False, c_2.has_key('e'))
|
||||
self.assertEquals(True, b_3.has_key('e'))
|
||||
self.assertEquals(False, b_3_2.has_key('e'))
|
||||
self.assertEquals(False, b_2.has_key('e'))
|
||||
|
||||
def testCow(self):
|
||||
from bb.COW import COWDictBase
|
||||
@@ -115,12 +115,12 @@ class COWTestCase(unittest.TestCase):
|
||||
|
||||
copy = c.copy()
|
||||
|
||||
self.assertEqual(1027, c['123'])
|
||||
self.assertEqual(4711, c['other'])
|
||||
self.assertEqual({'abc':10, 'bcd':20}, c['d'])
|
||||
self.assertEqual(1027, copy['123'])
|
||||
self.assertEqual(4711, copy['other'])
|
||||
self.assertEqual({'abc':10, 'bcd':20}, copy['d'])
|
||||
self.assertEquals(1027, c['123'])
|
||||
self.assertEquals(4711, c['other'])
|
||||
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
|
||||
self.assertEquals(1027, copy['123'])
|
||||
self.assertEquals(4711, copy['other'])
|
||||
self.assertEquals({'abc':10, 'bcd':20}, copy['d'])
|
||||
|
||||
# cow it now
|
||||
copy['123'] = 1028
|
||||
@@ -128,9 +128,9 @@ class COWTestCase(unittest.TestCase):
|
||||
copy['d']['abc'] = 20
|
||||
|
||||
|
||||
self.assertEqual(1027, c['123'])
|
||||
self.assertEqual(4711, c['other'])
|
||||
self.assertEqual({'abc':10, 'bcd':20}, c['d'])
|
||||
self.assertEqual(1028, copy['123'])
|
||||
self.assertEqual(4712, copy['other'])
|
||||
self.assertEqual({'abc':20, 'bcd':20}, copy['d'])
|
||||
self.assertEquals(1027, c['123'])
|
||||
self.assertEquals(4711, c['other'])
|
||||
self.assertEquals({'abc':10, 'bcd':20}, c['d'])
|
||||
self.assertEquals(1028, copy['123'])
|
||||
self.assertEquals(4712, copy['other'])
|
||||
self.assertEquals({'abc':20, 'bcd':20}, copy['d'])
|
||||
|
||||
@@ -77,13 +77,13 @@ class DataExpansions(unittest.TestCase):
|
||||
self.assertEqual(str(val), "boo value_of_foo")
|
||||
|
||||
def test_python_snippet_getvar(self):
|
||||
val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
|
||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${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') + ' ${bar}'}")
|
||||
self.assertEqual(str(val), "${@d.getVar('foo') + ' ${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):
|
||||
self.d.setVar("FOO", "${@foo = 5}")
|
||||
@@ -99,7 +99,7 @@ class DataExpansions(unittest.TestCase):
|
||||
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
|
||||
|
||||
def test_value_containing_value(self):
|
||||
val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
|
||||
val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
|
||||
self.assertEqual(str(val), "value_of_foo value_of_bar")
|
||||
|
||||
def test_reference_undefined_var(self):
|
||||
@@ -109,7 +109,7 @@ class DataExpansions(unittest.TestCase):
|
||||
def test_double_reference(self):
|
||||
self.d.setVar("BAR", "bar value")
|
||||
self.d.setVar("FOO", "${BAR} foo ${BAR}")
|
||||
val = self.d.getVar("FOO")
|
||||
val = self.d.getVar("FOO", True)
|
||||
self.assertEqual(str(val), "bar value foo bar value")
|
||||
|
||||
def test_direct_recursion(self):
|
||||
@@ -129,12 +129,12 @@ class DataExpansions(unittest.TestCase):
|
||||
|
||||
def test_incomplete_varexp_single_quotes(self):
|
||||
self.d.setVar("FOO", "sed -i -e 's:IP{:I${:g' $pc")
|
||||
val = self.d.getVar("FOO")
|
||||
val = self.d.getVar("FOO", True)
|
||||
self.assertEqual(str(val), "sed -i -e 's:IP{:I${:g' $pc")
|
||||
|
||||
def test_nonstring(self):
|
||||
self.d.setVar("TEST", 5)
|
||||
val = self.d.getVar("TEST")
|
||||
val = self.d.getVar("TEST", True)
|
||||
self.assertEqual(str(val), "5")
|
||||
|
||||
def test_rename(self):
|
||||
@@ -147,14 +147,14 @@ class DataExpansions(unittest.TestCase):
|
||||
self.assertEqual(self.d.getVar("foo", False), None)
|
||||
|
||||
def test_keys(self):
|
||||
keys = list(self.d.keys())
|
||||
self.assertCountEqual(keys, ['value_of_foo', 'foo', 'bar'])
|
||||
keys = self.d.keys()
|
||||
self.assertEqual(keys, ['value_of_foo', 'foo', 'bar'])
|
||||
|
||||
def test_keys_deletion(self):
|
||||
newd = bb.data.createCopy(self.d)
|
||||
newd.delVar("bar")
|
||||
keys = list(newd.keys())
|
||||
self.assertCountEqual(keys, ['value_of_foo', 'foo'])
|
||||
keys = newd.keys()
|
||||
self.assertEqual(keys, ['value_of_foo', 'foo'])
|
||||
|
||||
class TestNestedExpansions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -234,19 +234,19 @@ class TestConcat(unittest.TestCase):
|
||||
def test_prepend(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.prependVar("TEST", "${FOO}:")
|
||||
self.assertEqual(self.d.getVar("TEST"), "foo:val")
|
||||
self.assertEqual(self.d.getVar("TEST", True), "foo:val")
|
||||
|
||||
def test_append(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.appendVar("TEST", ":${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "val:bar")
|
||||
self.assertEqual(self.d.getVar("TEST", True), "val:bar")
|
||||
|
||||
def test_multiple_append(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.prependVar("TEST", "${FOO}:")
|
||||
self.d.appendVar("TEST", ":val2")
|
||||
self.d.appendVar("TEST", ":${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
|
||||
self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
|
||||
|
||||
class TestConcatOverride(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -258,66 +258,62 @@ class TestConcatOverride(unittest.TestCase):
|
||||
def test_prepend(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.setVar("TEST_prepend", "${FOO}:")
|
||||
self.assertEqual(self.d.getVar("TEST"), "foo:val")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "foo:val")
|
||||
|
||||
def test_append(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.setVar("TEST_append", ":${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "val:bar")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "val:bar")
|
||||
|
||||
def test_multiple_append(self):
|
||||
self.d.setVar("TEST", "${VAL}")
|
||||
self.d.setVar("TEST_prepend", "${FOO}:")
|
||||
self.d.setVar("TEST_append", ":val2")
|
||||
self.d.setVar("TEST_append", ":${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
|
||||
|
||||
def test_append_unset(self):
|
||||
self.d.setVar("TEST_prepend", "${FOO}:")
|
||||
self.d.setVar("TEST_append", ":val2")
|
||||
self.d.setVar("TEST_append", ":${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "foo::val2:bar")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "foo::val2:bar")
|
||||
|
||||
def test_remove(self):
|
||||
self.d.setVar("TEST", "${VAL} ${BAR}")
|
||||
self.d.setVar("TEST_remove", "val")
|
||||
self.assertEqual(self.d.getVar("TEST"), "bar")
|
||||
|
||||
def test_remove_cleared(self):
|
||||
self.d.setVar("TEST", "${VAL} ${BAR}")
|
||||
self.d.setVar("TEST_remove", "val")
|
||||
self.d.setVar("TEST", "${VAL} ${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "val bar")
|
||||
|
||||
# Ensure the value is unchanged if we have an inactive remove override
|
||||
# (including that whitespace is preserved)
|
||||
def test_remove_inactive_override(self):
|
||||
self.d.setVar("TEST", "${VAL} ${BAR} 123")
|
||||
self.d.setVar("TEST_remove_inactiveoverride", "val")
|
||||
self.assertEqual(self.d.getVar("TEST"), "val bar 123")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "bar")
|
||||
|
||||
def test_doubleref_remove(self):
|
||||
self.d.setVar("TEST", "${VAL} ${BAR}")
|
||||
self.d.setVar("TEST_remove", "val")
|
||||
self.d.setVar("TEST_TEST", "${TEST} ${TEST}")
|
||||
self.assertEqual(self.d.getVar("TEST_TEST"), "bar bar")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST_TEST", True), "bar bar")
|
||||
|
||||
def test_empty_remove(self):
|
||||
self.d.setVar("TEST", "")
|
||||
self.d.setVar("TEST_remove", "val")
|
||||
self.assertEqual(self.d.getVar("TEST"), "")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "")
|
||||
|
||||
def test_remove_expansion(self):
|
||||
self.d.setVar("BAR", "Z")
|
||||
self.d.setVar("TEST", "${BAR}/X Y")
|
||||
self.d.setVar("TEST_remove", "${BAR}/X")
|
||||
self.assertEqual(self.d.getVar("TEST"), "Y")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "Y")
|
||||
|
||||
def test_remove_expansion_items(self):
|
||||
self.d.setVar("TEST", "A B C D")
|
||||
self.d.setVar("BAR", "B D")
|
||||
self.d.setVar("TEST_remove", "${BAR}")
|
||||
self.assertEqual(self.d.getVar("TEST"), "A C")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "A C")
|
||||
|
||||
class TestOverrides(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -326,53 +322,60 @@ class TestOverrides(unittest.TestCase):
|
||||
self.d.setVar("TEST", "testvalue")
|
||||
|
||||
def test_no_override(self):
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue")
|
||||
|
||||
def test_one_override(self):
|
||||
self.d.setVar("TEST_bar", "testvalue2")
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue2")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue2")
|
||||
|
||||
def test_one_override_unset(self):
|
||||
self.d.setVar("TEST2_bar", "testvalue2")
|
||||
|
||||
self.assertEqual(self.d.getVar("TEST2"), "testvalue2")
|
||||
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST2", True), "testvalue2")
|
||||
self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
|
||||
|
||||
def test_multiple_override(self):
|
||||
self.d.setVar("TEST_bar", "testvalue2")
|
||||
self.d.setVar("TEST_local", "testvalue3")
|
||||
self.d.setVar("TEST_foo", "testvalue4")
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue3")
|
||||
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
||||
self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
|
||||
|
||||
def test_multiple_combined_overrides(self):
|
||||
self.d.setVar("TEST_local_foo_bar", "testvalue3")
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue3")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
||||
|
||||
def test_multiple_overrides_unset(self):
|
||||
self.d.setVar("TEST2_local_foo_bar", "testvalue3")
|
||||
self.assertEqual(self.d.getVar("TEST2"), "testvalue3")
|
||||
bb.data.update_data(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST2", True), "testvalue3")
|
||||
|
||||
def test_keyexpansion_override(self):
|
||||
self.d.setVar("LOCAL", "local")
|
||||
self.d.setVar("TEST_bar", "testvalue2")
|
||||
self.d.setVar("TEST_${LOCAL}", "testvalue3")
|
||||
self.d.setVar("TEST_foo", "testvalue4")
|
||||
bb.data.update_data(self.d)
|
||||
bb.data.expandKeys(self.d)
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue3")
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
||||
|
||||
def test_rename_override(self):
|
||||
self.d.setVar("ALTERNATIVE_ncurses-tools_class-target", "a")
|
||||
self.d.setVar("OVERRIDES", "class-target")
|
||||
bb.data.update_data(self.d)
|
||||
self.d.renameVar("ALTERNATIVE_ncurses-tools", "ALTERNATIVE_lib32-ncurses-tools")
|
||||
self.assertEqual(self.d.getVar("ALTERNATIVE_lib32-ncurses-tools"), "a")
|
||||
self.assertEqual(self.d.getVar("ALTERNATIVE_lib32-ncurses-tools", True), "a")
|
||||
|
||||
def test_underscore_override(self):
|
||||
self.d.setVar("TEST_bar", "testvalue2")
|
||||
self.d.setVar("TEST_some_val", "testvalue3")
|
||||
self.d.setVar("TEST_foo", "testvalue4")
|
||||
self.d.setVar("OVERRIDES", "foo:bar:some_val")
|
||||
self.assertEqual(self.d.getVar("TEST"), "testvalue3")
|
||||
self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
|
||||
|
||||
class TestKeyExpansion(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -386,7 +389,7 @@ class TestKeyExpansion(unittest.TestCase):
|
||||
with LogRecord() as logs:
|
||||
bb.data.expandKeys(self.d)
|
||||
self.assertTrue(logContains("Variable key VAL_${FOO} (A) replaces original key VAL_foo (B)", logs))
|
||||
self.assertEqual(self.d.getVar("VAL_foo"), "A")
|
||||
self.assertEqual(self.d.getVar("VAL_foo", True), "A")
|
||||
|
||||
class TestFlags(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -441,167 +444,3 @@ class Contains(unittest.TestCase):
|
||||
|
||||
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x", True, False, self.d))
|
||||
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x y z", True, False, self.d))
|
||||
|
||||
|
||||
class Serialize(unittest.TestCase):
|
||||
|
||||
def test_serialize(self):
|
||||
import tempfile
|
||||
import pickle
|
||||
d = bb.data.init()
|
||||
d.enableTracking()
|
||||
d.setVar('HELLO', 'world')
|
||||
d.setVarFlag('HELLO', 'other', 'planet')
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
|
||||
tmpfilename = tmpfile.name
|
||||
pickle.dump(d, tmpfile)
|
||||
|
||||
with open(tmpfilename, 'rb') as f:
|
||||
newd = pickle.load(f)
|
||||
|
||||
os.remove(tmpfilename)
|
||||
|
||||
self.assertEqual(d, newd)
|
||||
self.assertEqual(newd.getVar('HELLO'), 'world')
|
||||
self.assertEqual(newd.getVarFlag('HELLO', 'other'), 'planet')
|
||||
|
||||
|
||||
# Remote datastore tests
|
||||
# These really only test the interface, since in actual usage we have a
|
||||
# tinfoil connector that does everything over RPC, and this doesn't test
|
||||
# that.
|
||||
|
||||
class TestConnector:
|
||||
d = None
|
||||
def __init__(self, d):
|
||||
self.d = d
|
||||
def getVar(self, name):
|
||||
return self.d._findVar(name)
|
||||
def getKeys(self):
|
||||
return set(self.d.keys())
|
||||
def getVarHistory(self, name):
|
||||
return self.d.varhistory.variable(name)
|
||||
def expandPythonRef(self, varname, expr, d):
|
||||
localdata = self.d.createCopy()
|
||||
for key in d.localkeys():
|
||||
localdata.setVar(d.getVar(key))
|
||||
varparse = bb.data_smart.VariableParse(varname, localdata)
|
||||
return varparse.python_sub(expr)
|
||||
def setVar(self, name, value):
|
||||
self.d.setVar(name, value)
|
||||
def setVarFlag(self, name, flag, value):
|
||||
self.d.setVarFlag(name, flag, value)
|
||||
def delVar(self, name):
|
||||
self.d.delVar(name)
|
||||
return False
|
||||
def delVarFlag(self, name, flag):
|
||||
self.d.delVarFlag(name, flag)
|
||||
return False
|
||||
def renameVar(self, name, newname):
|
||||
self.d.renameVar(name, newname)
|
||||
return False
|
||||
|
||||
class Remote(unittest.TestCase):
|
||||
def test_remote(self):
|
||||
|
||||
d1 = bb.data.init()
|
||||
d1.enableTracking()
|
||||
d2 = bb.data.init()
|
||||
d2.enableTracking()
|
||||
connector = TestConnector(d1)
|
||||
|
||||
d2.setVar('_remote_data', connector)
|
||||
|
||||
d1.setVar('HELLO', 'world')
|
||||
d1.setVarFlag('OTHER', 'flagname', 'flagvalue')
|
||||
self.assertEqual(d2.getVar('HELLO'), 'world')
|
||||
self.assertEqual(d2.expand('${HELLO}'), 'world')
|
||||
self.assertEqual(d2.expand('${@d.getVar("HELLO")}'), 'world')
|
||||
self.assertIn('flagname', d2.getVarFlags('OTHER'))
|
||||
self.assertEqual(d2.getVarFlag('OTHER', 'flagname'), 'flagvalue')
|
||||
self.assertEqual(d1.varhistory.variable('HELLO'), d2.varhistory.variable('HELLO'))
|
||||
# Test setVar on client side affects server
|
||||
d2.setVar('HELLO', 'other-world')
|
||||
self.assertEqual(d1.getVar('HELLO'), 'other-world')
|
||||
# Test setVarFlag on client side affects server
|
||||
d2.setVarFlag('HELLO', 'flagname', 'flagvalue')
|
||||
self.assertEqual(d1.getVarFlag('HELLO', 'flagname'), 'flagvalue')
|
||||
# Test client side data is incorporated in python expansion (which is done on server)
|
||||
d2.setVar('FOO', 'bar')
|
||||
self.assertEqual(d2.expand('${@d.getVar("FOO")}'), 'bar')
|
||||
# Test overrides work
|
||||
d1.setVar('FOO_test', 'baz')
|
||||
d1.appendVar('OVERRIDES', ':test')
|
||||
self.assertEqual(d2.getVar('FOO'), 'baz')
|
||||
|
||||
|
||||
# Remote equivalents of local test classes
|
||||
# Note that these aren't perfect since we only test in one direction
|
||||
|
||||
class RemoteDataExpansions(DataExpansions):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1["foo"] = "value_of_foo"
|
||||
self.d1["bar"] = "value_of_bar"
|
||||
self.d1["value_of_foo"] = "value_of_'value_of_foo'"
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteNestedExpansions(TestNestedExpansions):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1["foo"] = "foo"
|
||||
self.d1["bar"] = "bar"
|
||||
self.d1["value_of_foobar"] = "187"
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteConcat(TestConcat):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1.setVar("FOO", "foo")
|
||||
self.d1.setVar("VAL", "val")
|
||||
self.d1.setVar("BAR", "bar")
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteConcatOverride(TestConcatOverride):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1.setVar("FOO", "foo")
|
||||
self.d1.setVar("VAL", "val")
|
||||
self.d1.setVar("BAR", "bar")
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteOverrides(TestOverrides):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1.setVar("OVERRIDES", "foo:bar:local")
|
||||
self.d1.setVar("TEST", "testvalue")
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteKeyExpansion(TestKeyExpansion):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1.setVar("FOO", "foo")
|
||||
self.d1.setVar("BAR", "foo")
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
class TestRemoteFlags(TestFlags):
|
||||
def setUp(self):
|
||||
self.d1 = bb.data.init()
|
||||
self.d = bb.data.init()
|
||||
self.d1.setVar("foo", "value of foo")
|
||||
self.d1.setVarFlag("foo", "flag1", "value of flag1")
|
||||
self.d1.setVarFlag("foo", "flag2", "value of flag2")
|
||||
connector = TestConnector(self.d1)
|
||||
self.d.setVar('_remote_data', connector)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
import unittest
|
||||
import tempfile
|
||||
import subprocess
|
||||
import collections
|
||||
import os
|
||||
from bb.fetch2 import URI
|
||||
from bb.fetch2 import FetchMethod
|
||||
@@ -134,10 +133,10 @@ class URITest(unittest.TestCase):
|
||||
'userinfo': 'anoncvs:anonymous',
|
||||
'username': 'anoncvs',
|
||||
'password': 'anonymous',
|
||||
'params': collections.OrderedDict([
|
||||
('tag', 'V0-99-81'),
|
||||
('module', 'familiar/dist/ipkg')
|
||||
]),
|
||||
'params': {
|
||||
'tag': 'V0-99-81',
|
||||
'module': 'familiar/dist/ipkg'
|
||||
},
|
||||
'query': {},
|
||||
'relative': False
|
||||
},
|
||||
@@ -360,10 +359,7 @@ class FetcherTest(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.origdir)
|
||||
if os.environ.get("BB_TMPDIR_NOCLEAN") == "yes":
|
||||
print("Not cleaning up %s. Please remove manually." % self.tempdir)
|
||||
else:
|
||||
bb.utils.prunedir(self.tempdir)
|
||||
bb.utils.prunedir(self.tempdir)
|
||||
|
||||
class MirrorUriTest(FetcherTest):
|
||||
|
||||
@@ -454,7 +450,7 @@ class MirrorUriTest(FetcherTest):
|
||||
class FetcherLocalTest(FetcherTest):
|
||||
def setUp(self):
|
||||
def touch(fn):
|
||||
with open(fn, 'a'):
|
||||
with file(fn, 'a'):
|
||||
os.utime(fn, None)
|
||||
|
||||
super(FetcherLocalTest, self).setUp()
|
||||
@@ -508,15 +504,6 @@ class FetcherLocalTest(FetcherTest):
|
||||
tree = self.fetchUnpack(['file://dir/subdir/e;subdir=bar'])
|
||||
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):
|
||||
|
||||
if os.environ.get("BB_SKIP_NETTESTS") == "yes":
|
||||
@@ -597,36 +584,6 @@ class FetcherNetworkTest(FetcherTest):
|
||||
url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;tag=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
|
||||
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):
|
||||
url1 = "git://git.openembedded.org/bitbake"
|
||||
url2 = "git://someserver.org/bitbake"
|
||||
@@ -703,7 +660,7 @@ class URLHandle(unittest.TestCase):
|
||||
datatable = {
|
||||
"http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}),
|
||||
"cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}),
|
||||
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', 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'}),
|
||||
"file://somelocation;someparam=1": ('file', '', 'somelocation', '', '', {'someparam': '1'}),
|
||||
}
|
||||
@@ -740,12 +697,12 @@ class FetchLatestVersionTest(FetcherTest):
|
||||
("dtc", "git://git.qemu.org/dtc.git", "65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf", "")
|
||||
: "1.4.0",
|
||||
# combination version pattern
|
||||
("sysprof", "git://gitlab.gnome.org/GNOME/sysprof;protocol=https", "cd44ee6644c3641507fb53b8a2a69137f2971219", "")
|
||||
("sysprof", "git://git.gnome.org/sysprof", "cd44ee6644c3641507fb53b8a2a69137f2971219", "")
|
||||
: "1.2.0",
|
||||
("u-boot-mkimage", "git://git.denx.de/u-boot.git;branch=master;protocol=git", "62c175fbb8a0f9a926c88294ea9f7e88eb898f6c", "")
|
||||
: "2014.01",
|
||||
# version pattern "yyyymmdd"
|
||||
("mobile-broadband-provider-info", "git://gitlab.gnome.org/GNOME/mobile-broadband-provider-info;protocol=https", "4ed19e11c2975105b71b956440acdb25d46a347d", "")
|
||||
("mobile-broadband-provider-info", "git://git.gnome.org/mobile-broadband-provider-info", "4ed19e11c2975105b71b956440acdb25d46a347d", "")
|
||||
: "20120614",
|
||||
# packages with a valid UPSTREAM_CHECK_GITTAGREGEX
|
||||
("xf86-video-omap", "git://anongit.freedesktop.org/xorg/driver/xf86-video-omap", "ae0394e687f1a77e966cf72f895da91840dffb8f", "(?P<pver>(\d+\.(\d\.?)*))")
|
||||
@@ -793,7 +750,6 @@ class FetchLatestVersionTest(FetcherTest):
|
||||
ud = bb.fetch2.FetchData(k[1], self.d)
|
||||
pupver= ud.method.latest_versionstring(ud, self.d)
|
||||
verstring = pupver[0]
|
||||
self.assertTrue(verstring, msg="Could not find upstream version")
|
||||
r = bb.utils.vercmp_string(v, verstring)
|
||||
self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
|
||||
|
||||
@@ -805,7 +761,6 @@ class FetchLatestVersionTest(FetcherTest):
|
||||
ud = bb.fetch2.FetchData(k[1], self.d)
|
||||
pupver = ud.method.latest_versionstring(ud, self.d)
|
||||
verstring = pupver[0]
|
||||
self.assertTrue(verstring, msg="Could not find upstream version")
|
||||
r = bb.utils.vercmp_string(v, verstring)
|
||||
self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ C = "3"
|
||||
def parsehelper(self, content, suffix = ".bb"):
|
||||
|
||||
f = tempfile.NamedTemporaryFile(suffix = suffix)
|
||||
f.write(bytes(content, "utf-8"))
|
||||
f.write(content)
|
||||
f.flush()
|
||||
os.chdir(os.path.dirname(f.name))
|
||||
return f
|
||||
@@ -58,9 +58,9 @@ C = "3"
|
||||
def test_parse_simple(self):
|
||||
f = self.parsehelper(self.testfile)
|
||||
d = bb.parse.handle(f.name, self.d)['']
|
||||
self.assertEqual(d.getVar("A"), "1")
|
||||
self.assertEqual(d.getVar("B"), "2")
|
||||
self.assertEqual(d.getVar("C"), "3")
|
||||
self.assertEqual(d.getVar("A", True), "1")
|
||||
self.assertEqual(d.getVar("B", True), "2")
|
||||
self.assertEqual(d.getVar("C", True), "3")
|
||||
|
||||
def test_parse_incomplete_function(self):
|
||||
testfileB = self.testfile.replace("}", "")
|
||||
@@ -68,23 +68,6 @@ C = "3"
|
||||
with self.assertRaises(bb.parse.ParseError):
|
||||
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"), None)
|
||||
self.assertEqual(d.getVarFlag("A","flag"), None)
|
||||
self.assertEqual(d.getVar("B"), "2")
|
||||
|
||||
|
||||
overridetest = """
|
||||
RRECOMMENDS_${PN} = "a"
|
||||
RRECOMMENDS_${PN}_libc = "b"
|
||||
@@ -95,11 +78,11 @@ PN = "gtk+"
|
||||
def test_parse_overrides(self):
|
||||
f = self.parsehelper(self.overridetest)
|
||||
d = bb.parse.handle(f.name, self.d)['']
|
||||
self.assertEqual(d.getVar("RRECOMMENDS"), "b")
|
||||
self.assertEqual(d.getVar("RRECOMMENDS", True), "b")
|
||||
bb.data.expandKeys(d)
|
||||
self.assertEqual(d.getVar("RRECOMMENDS"), "b")
|
||||
self.assertEqual(d.getVar("RRECOMMENDS", True), "b")
|
||||
d.setVar("RRECOMMENDS_gtk+", "c")
|
||||
self.assertEqual(d.getVar("RRECOMMENDS"), "c")
|
||||
self.assertEqual(d.getVar("RRECOMMENDS", True), "c")
|
||||
|
||||
overridetest2 = """
|
||||
EXTRA_OECONF = ""
|
||||
@@ -112,7 +95,7 @@ EXTRA_OECONF_append = " c"
|
||||
d = bb.parse.handle(f.name, self.d)['']
|
||||
d.appendVar("EXTRA_OECONF", " d")
|
||||
d.setVar("OVERRIDES", "class-target")
|
||||
self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d")
|
||||
self.assertEqual(d.getVar("EXTRA_OECONF", True), "b c d")
|
||||
|
||||
overridetest3 = """
|
||||
DESCRIPTION = "A"
|
||||
@@ -124,11 +107,11 @@ PN = "bc"
|
||||
f = self.parsehelper(self.overridetest3)
|
||||
d = bb.parse.handle(f.name, self.d)['']
|
||||
bb.data.expandKeys(d)
|
||||
self.assertEqual(d.getVar("DESCRIPTION_bc-dev"), "A B")
|
||||
self.assertEqual(d.getVar("DESCRIPTION_bc-dev", True), "A B")
|
||||
d.setVar("DESCRIPTION", "E")
|
||||
d.setVar("DESCRIPTION_bc-dev", "C D")
|
||||
d.setVar("OVERRIDES", "bc-dev")
|
||||
self.assertEqual(d.getVar("DESCRIPTION"), "C D")
|
||||
self.assertEqual(d.getVar("DESCRIPTION", True), "C D")
|
||||
|
||||
|
||||
classextend = """
|
||||
@@ -159,6 +142,6 @@ python () {
|
||||
alldata = bb.parse.handle(f.name, self.d)
|
||||
d1 = alldata['']
|
||||
d2 = alldata[cls.name]
|
||||
self.assertEqual(d1.getVar("VAR_var"), "B")
|
||||
self.assertEqual(d2.getVar("VAR_var"), None)
|
||||
self.assertEqual(d1.getVar("VAR_var", True), "B")
|
||||
self.assertEqual(d2.getVar("VAR_var", True), None)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# tinfoil: a simple wrapper around cooker for bitbake-based command-line utilities
|
||||
#
|
||||
# Copyright (C) 2012-2017 Intel Corporation
|
||||
# Copyright (C) 2012 Intel Corporation
|
||||
# Copyright (C) 2011 Mentor Graphics Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,466 +17,89 @@
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
import os
|
||||
import sys
|
||||
import atexit
|
||||
import re
|
||||
from collections import OrderedDict, defaultdict
|
||||
|
||||
import bb.cache
|
||||
import bb.cooker
|
||||
import bb.providers
|
||||
import bb.taskdata
|
||||
import bb.utils
|
||||
import bb.command
|
||||
import bb.remotedata
|
||||
from bb.cooker import state, BBCooker, CookerFeatures
|
||||
from bb.cookerdata import CookerConfiguration, ConfigParameters
|
||||
from bb.main import setup_bitbake, BitBakeConfigParameters, BBMainException
|
||||
import bb.fetch2
|
||||
|
||||
|
||||
# We need this in order to shut down the connection to the bitbake server,
|
||||
# otherwise the process will never properly exit
|
||||
_server_connections = []
|
||||
def _terminate_connections():
|
||||
for connection in _server_connections:
|
||||
connection.terminate()
|
||||
atexit.register(_terminate_connections)
|
||||
|
||||
class TinfoilUIException(Exception):
|
||||
"""Exception raised when the UI returns non-zero from its main function"""
|
||||
def __init__(self, returncode):
|
||||
self.returncode = returncode
|
||||
def __repr__(self):
|
||||
return 'UI module main returned %d' % self.returncode
|
||||
|
||||
class TinfoilCommandFailed(Exception):
|
||||
"""Exception raised when run_command fails"""
|
||||
|
||||
class TinfoilDataStoreConnector:
|
||||
|
||||
def __init__(self, tinfoil, dsindex):
|
||||
self.tinfoil = tinfoil
|
||||
self.dsindex = dsindex
|
||||
def getVar(self, name):
|
||||
value = self.tinfoil.run_command('dataStoreConnectorFindVar', self.dsindex, name)
|
||||
overrides = None
|
||||
if isinstance(value, dict):
|
||||
if '_connector_origtype' in value:
|
||||
value['_content'] = self.tinfoil._reconvert_type(value['_content'], value['_connector_origtype'])
|
||||
del value['_connector_origtype']
|
||||
if '_connector_overrides' in value:
|
||||
overrides = value['_connector_overrides']
|
||||
del value['_connector_overrides']
|
||||
return value, overrides
|
||||
def getKeys(self):
|
||||
return set(self.tinfoil.run_command('dataStoreConnectorGetKeys', self.dsindex))
|
||||
def getVarHistory(self, name):
|
||||
return self.tinfoil.run_command('dataStoreConnectorGetVarHistory', self.dsindex, name)
|
||||
def expandPythonRef(self, varname, expr, d):
|
||||
ds = bb.remotedata.RemoteDatastores.transmit_datastore(d)
|
||||
ret = self.tinfoil.run_command('dataStoreConnectorExpandPythonRef', ds, varname, expr)
|
||||
return ret
|
||||
def setVar(self, varname, value):
|
||||
if self.dsindex is None:
|
||||
self.tinfoil.run_command('setVariable', varname, value)
|
||||
else:
|
||||
# Not currently implemented - indicate that setting should
|
||||
# be redirected to local side
|
||||
return True
|
||||
def setVarFlag(self, varname, flagname, value):
|
||||
if self.dsindex is None:
|
||||
self.tinfoil.run_command('dataStoreConnectorSetVarFlag', self.dsindex, varname, flagname, value)
|
||||
else:
|
||||
# Not currently implemented - indicate that setting should
|
||||
# be redirected to local side
|
||||
return True
|
||||
def delVar(self, varname):
|
||||
if self.dsindex is None:
|
||||
self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname)
|
||||
else:
|
||||
# Not currently implemented - indicate that setting should
|
||||
# be redirected to local side
|
||||
return True
|
||||
def delVarFlag(self, varname, flagname):
|
||||
if self.dsindex is None:
|
||||
self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname, flagname)
|
||||
else:
|
||||
# Not currently implemented - indicate that setting should
|
||||
# be redirected to local side
|
||||
return True
|
||||
def renameVar(self, name, newname):
|
||||
if self.dsindex is None:
|
||||
self.tinfoil.run_command('dataStoreConnectorRenameVar', self.dsindex, name, newname)
|
||||
else:
|
||||
# Not currently implemented - indicate that setting should
|
||||
# be redirected to local side
|
||||
return True
|
||||
|
||||
class TinfoilCookerAdapter:
|
||||
"""
|
||||
Provide an adapter for existing code that expects to access a cooker object via Tinfoil,
|
||||
since now Tinfoil is on the client side it no longer has direct access.
|
||||
"""
|
||||
|
||||
class TinfoilCookerCollectionAdapter:
|
||||
""" cooker.collection adapter """
|
||||
def __init__(self, tinfoil):
|
||||
self.tinfoil = tinfoil
|
||||
def get_file_appends(self, fn):
|
||||
return self.tinfoil.get_file_appends(fn)
|
||||
def __getattr__(self, name):
|
||||
if name == 'overlayed':
|
||||
return self.tinfoil.get_overlayed_recipes()
|
||||
elif name == 'bbappends':
|
||||
return self.tinfoil.run_command('getAllAppends')
|
||||
else:
|
||||
raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
|
||||
|
||||
class TinfoilRecipeCacheAdapter:
|
||||
""" cooker.recipecache adapter """
|
||||
def __init__(self, tinfoil):
|
||||
self.tinfoil = tinfoil
|
||||
self._cache = {}
|
||||
|
||||
def get_pkg_pn_fn(self):
|
||||
pkg_pn = defaultdict(list, self.tinfoil.run_command('getRecipes') or [])
|
||||
pkg_fn = {}
|
||||
for pn, fnlist in pkg_pn.items():
|
||||
for fn in fnlist:
|
||||
pkg_fn[fn] = pn
|
||||
self._cache['pkg_pn'] = pkg_pn
|
||||
self._cache['pkg_fn'] = pkg_fn
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Grab these only when they are requested since they aren't always used
|
||||
if name in self._cache:
|
||||
return self._cache[name]
|
||||
elif name == 'pkg_pn':
|
||||
self.get_pkg_pn_fn()
|
||||
return self._cache[name]
|
||||
elif name == 'pkg_fn':
|
||||
self.get_pkg_pn_fn()
|
||||
return self._cache[name]
|
||||
elif name == 'deps':
|
||||
attrvalue = defaultdict(list, self.tinfoil.run_command('getRecipeDepends') or [])
|
||||
elif name == 'rundeps':
|
||||
attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeDepends') or [])
|
||||
elif name == 'runrecs':
|
||||
attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeRecommends') or [])
|
||||
elif name == 'pkg_pepvpr':
|
||||
attrvalue = self.tinfoil.run_command('getRecipeVersions') or {}
|
||||
elif name == 'inherits':
|
||||
attrvalue = self.tinfoil.run_command('getRecipeInherits') or {}
|
||||
elif name == 'bbfile_priority':
|
||||
attrvalue = self.tinfoil.run_command('getBbFilePriority') or {}
|
||||
elif name == 'pkg_dp':
|
||||
attrvalue = self.tinfoil.run_command('getDefaultPreference') or {}
|
||||
else:
|
||||
raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
|
||||
|
||||
self._cache[name] = attrvalue
|
||||
return attrvalue
|
||||
|
||||
def __init__(self, tinfoil):
|
||||
self.tinfoil = tinfoil
|
||||
self.collection = self.TinfoilCookerCollectionAdapter(tinfoil)
|
||||
self.recipecaches = {}
|
||||
# FIXME all machines
|
||||
self.recipecaches[''] = self.TinfoilRecipeCacheAdapter(tinfoil)
|
||||
self._cache = {}
|
||||
def __getattr__(self, name):
|
||||
# Grab these only when they are requested since they aren't always used
|
||||
if name in self._cache:
|
||||
return self._cache[name]
|
||||
elif name == 'skiplist':
|
||||
attrvalue = self.tinfoil.get_skipped_recipes()
|
||||
elif name == 'bbfile_config_priorities':
|
||||
ret = self.tinfoil.run_command('getLayerPriorities')
|
||||
bbfile_config_priorities = []
|
||||
for collection, pattern, regex, pri in ret:
|
||||
bbfile_config_priorities.append((collection, pattern, re.compile(regex), pri))
|
||||
|
||||
attrvalue = bbfile_config_priorities
|
||||
else:
|
||||
raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
|
||||
|
||||
self._cache[name] = attrvalue
|
||||
return attrvalue
|
||||
|
||||
def findBestProvider(self, pn):
|
||||
return self.tinfoil.find_best_provider(pn)
|
||||
|
||||
|
||||
class Tinfoil:
|
||||
def __init__(self, output=sys.stdout, tracking=False):
|
||||
# Needed to avoid deprecation warnings with python 2.6
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
def __init__(self, output=sys.stdout, tracking=False, setup_logging=True):
|
||||
# Set up logging
|
||||
self.logger = logging.getLogger('BitBake')
|
||||
self.config_data = None
|
||||
self.cooker = None
|
||||
self.tracking = tracking
|
||||
self.ui_module = None
|
||||
self.server_connection = None
|
||||
if setup_logging:
|
||||
# This is the *client-side* logger, nothing to do with
|
||||
# logging messages from the server
|
||||
bb.msg.logger_create('BitBake', output)
|
||||
self._log_hdlr = logging.StreamHandler(output)
|
||||
bb.msg.addDefaultlogFilter(self._log_hdlr)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
if output.isatty():
|
||||
format.enable_color()
|
||||
self._log_hdlr.setFormatter(format)
|
||||
self.logger.addHandler(self._log_hdlr)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
self.config = CookerConfiguration()
|
||||
configparams = TinfoilConfigParameters(parse_only=True)
|
||||
self.config.setConfigParameters(configparams)
|
||||
self.config.setServerRegIdleCallback(self.register_idle_function)
|
||||
features = []
|
||||
if tracking:
|
||||
features.append(CookerFeatures.BASEDATASTORE_TRACKING)
|
||||
self.cooker = BBCooker(self.config, features)
|
||||
self.config_data = self.cooker.data
|
||||
bb.providers.logger.setLevel(logging.ERROR)
|
||||
self.cooker_data = None
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.shutdown()
|
||||
|
||||
def prepare(self, config_only=False, config_params=None, quiet=0):
|
||||
if self.tracking:
|
||||
extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
|
||||
else:
|
||||
extrafeatures = []
|
||||
|
||||
if not config_params:
|
||||
config_params = TinfoilConfigParameters(config_only=config_only, quiet=quiet)
|
||||
|
||||
cookerconfig = CookerConfiguration()
|
||||
cookerconfig.setConfigParameters(config_params)
|
||||
|
||||
server, self.server_connection, ui_module = setup_bitbake(config_params,
|
||||
cookerconfig,
|
||||
extrafeatures)
|
||||
|
||||
self.ui_module = ui_module
|
||||
|
||||
# Ensure the path to bitbake's bin directory is in PATH so that things like
|
||||
# bitbake-worker can be run (usually this is the case, but it doesn't have to be)
|
||||
path = os.getenv('PATH').split(':')
|
||||
bitbakebinpath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'bin'))
|
||||
for entry in path:
|
||||
if entry.endswith(os.sep):
|
||||
entry = entry[:-1]
|
||||
if os.path.abspath(entry) == bitbakebinpath:
|
||||
break
|
||||
else:
|
||||
path.insert(0, bitbakebinpath)
|
||||
os.environ['PATH'] = ':'.join(path)
|
||||
|
||||
if self.server_connection:
|
||||
_server_connections.append(self.server_connection)
|
||||
if config_only:
|
||||
config_params.updateToServer(self.server_connection.connection, os.environ.copy())
|
||||
self.run_command('parseConfiguration')
|
||||
else:
|
||||
self.run_actions(config_params)
|
||||
|
||||
self.config_data = bb.data.init()
|
||||
connector = TinfoilDataStoreConnector(self, None)
|
||||
self.config_data.setVar('_remote_data', connector)
|
||||
self.cooker = TinfoilCookerAdapter(self)
|
||||
self.cooker_data = self.cooker.recipecaches['']
|
||||
else:
|
||||
raise Exception('Failed to start bitbake server')
|
||||
|
||||
def run_actions(self, config_params):
|
||||
"""
|
||||
Run the actions specified in config_params through the UI.
|
||||
"""
|
||||
ret = self.ui_module.main(self.server_connection.connection, self.server_connection.events, config_params)
|
||||
if ret:
|
||||
raise TinfoilUIException(ret)
|
||||
def register_idle_function(self, function, data):
|
||||
pass
|
||||
|
||||
def parseRecipes(self):
|
||||
"""
|
||||
Force a parse of all recipes. Normally you should specify
|
||||
config_only=False when calling prepare() instead of using this
|
||||
function; this function is designed for situations where you need
|
||||
to initialise Tinfoil and use it with config_only=True first and
|
||||
then conditionally call this function to parse recipes later.
|
||||
"""
|
||||
config_params = TinfoilConfigParameters(config_only=False)
|
||||
self.run_actions(config_params)
|
||||
sys.stderr.write("Parsing recipes..")
|
||||
self.logger.setLevel(logging.WARNING)
|
||||
|
||||
def run_command(self, command, *params):
|
||||
"""
|
||||
Run a command on the server (as implemented in bb.command).
|
||||
Note that there are two types of command - synchronous and
|
||||
asynchronous; in order to receive the results of asynchronous
|
||||
commands you will need to set an appropriate event mask
|
||||
using set_event_mask() and listen for the result using
|
||||
wait_event() - with the correct event mask you'll at least get
|
||||
bb.command.CommandCompleted and possibly other events before
|
||||
that depending on the command.
|
||||
"""
|
||||
if not self.server_connection:
|
||||
raise Exception('Not connected to server (did you call .prepare()?)')
|
||||
try:
|
||||
while self.cooker.state in (state.initial, state.parsing):
|
||||
self.cooker.updateCache()
|
||||
except KeyboardInterrupt:
|
||||
self.cooker.shutdown()
|
||||
self.cooker.updateCache()
|
||||
sys.exit(2)
|
||||
|
||||
commandline = [command]
|
||||
if params:
|
||||
commandline.extend(params)
|
||||
result = self.server_connection.connection.runCommand(commandline)
|
||||
if result[1]:
|
||||
raise TinfoilCommandFailed(result[1])
|
||||
return result[0]
|
||||
self.logger.setLevel(logging.INFO)
|
||||
sys.stderr.write("done.\n")
|
||||
|
||||
def set_event_mask(self, eventlist):
|
||||
"""Set the event mask which will be applied within wait_event()"""
|
||||
if not self.server_connection:
|
||||
raise Exception('Not connected to server (did you call .prepare()?)')
|
||||
llevel, debug_domains = bb.msg.constructLogOptions()
|
||||
ret = self.run_command('setEventMask', self.server_connection.connection.getEventHandle(), llevel, debug_domains, eventlist)
|
||||
if not ret:
|
||||
raise Exception('setEventMask failed')
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
|
||||
def wait_event(self, timeout=0):
|
||||
"""
|
||||
Wait for an event from the server for the specified time.
|
||||
A timeout of 0 means don't wait if there are no events in the queue.
|
||||
Returns the next event in the queue or None if the timeout was
|
||||
reached. Note that in order to recieve any events you will
|
||||
first need to set the internal event mask using set_event_mask()
|
||||
(otherwise whatever event mask the UI set up will be in effect).
|
||||
"""
|
||||
if not self.server_connection:
|
||||
raise Exception('Not connected to server (did you call .prepare()?)')
|
||||
return self.server_connection.events.waitEvent(timeout)
|
||||
|
||||
def get_overlayed_recipes(self):
|
||||
return defaultdict(list, self.run_command('getOverlayedRecipes'))
|
||||
|
||||
def get_skipped_recipes(self):
|
||||
return OrderedDict(self.run_command('getSkippedRecipes'))
|
||||
|
||||
def get_all_providers(self):
|
||||
return defaultdict(list, self.run_command('allProviders'))
|
||||
|
||||
def find_providers(self):
|
||||
return self.run_command('findProviders')
|
||||
|
||||
def find_best_provider(self, pn):
|
||||
return self.run_command('findBestProvider', pn)
|
||||
|
||||
def get_runtime_providers(self, rdep):
|
||||
return self.run_command('getRuntimeProviders', rdep)
|
||||
|
||||
def get_recipe_file(self, pn):
|
||||
"""
|
||||
Get the file name for the specified recipe/target. Raises
|
||||
bb.providers.NoProvider if there is no match or the recipe was
|
||||
skipped.
|
||||
"""
|
||||
best = self.find_best_provider(pn)
|
||||
if not best or (len(best) > 3 and not best[3]):
|
||||
skiplist = self.get_skipped_recipes()
|
||||
taskdata = bb.taskdata.TaskData(None, skiplist=skiplist)
|
||||
skipreasons = taskdata.get_reasons(pn)
|
||||
if skipreasons:
|
||||
raise bb.providers.NoProvider('%s is unavailable:\n %s' % (pn, ' \n'.join(skipreasons)))
|
||||
def prepare(self, config_only = False):
|
||||
if not self.cooker_data:
|
||||
if config_only:
|
||||
self.cooker.parseConfiguration()
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
else:
|
||||
raise bb.providers.NoProvider('Unable to find any recipe file matching "%s"' % pn)
|
||||
return best[3]
|
||||
|
||||
def get_file_appends(self, fn):
|
||||
return self.run_command('getFileAppends', fn)
|
||||
|
||||
def parse_recipe(self, pn):
|
||||
"""
|
||||
Parse the specified recipe and return a datastore object
|
||||
representing the environment for the recipe.
|
||||
"""
|
||||
fn = self.get_recipe_file(pn)
|
||||
return self.parse_recipe_file(fn)
|
||||
|
||||
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 config_data:
|
||||
dctr = bb.remotedata.RemoteDatastores.transmit_datastore(config_data)
|
||||
dscon = self.run_command('parseRecipeFile', fn, appends, appendlist, dctr)
|
||||
else:
|
||||
dscon = self.run_command('parseRecipeFile', fn, appends, appendlist)
|
||||
if dscon:
|
||||
return self._reconvert_type(dscon, 'DataStoreConnectionHandle')
|
||||
else:
|
||||
return None
|
||||
|
||||
def build_file(self, buildfile, task):
|
||||
"""
|
||||
Runs the specified task for just a single recipe (i.e. no dependencies).
|
||||
This is equivalent to bitbake -b, except no warning will be printed.
|
||||
"""
|
||||
return self.run_command('buildFile', buildfile, task, True)
|
||||
self.parseRecipes()
|
||||
|
||||
def shutdown(self):
|
||||
if self.server_connection:
|
||||
self.run_command('clientComplete')
|
||||
_server_connections.remove(self.server_connection)
|
||||
bb.event.ui_queue = []
|
||||
self.server_connection.terminate()
|
||||
self.server_connection = None
|
||||
self.cooker.shutdown(force=True)
|
||||
self.cooker.post_serve()
|
||||
self.cooker.unlockBitbake()
|
||||
self.logger.removeHandler(self._log_hdlr)
|
||||
|
||||
def _reconvert_type(self, obj, origtypename):
|
||||
"""
|
||||
Convert an object back to the right type, in the case
|
||||
that marshalling has changed it (especially with xmlrpc)
|
||||
"""
|
||||
supported_types = {
|
||||
'set': set,
|
||||
'DataStoreConnectionHandle': bb.command.DataStoreConnectionHandle,
|
||||
}
|
||||
class TinfoilConfigParameters(ConfigParameters):
|
||||
|
||||
origtype = supported_types.get(origtypename, None)
|
||||
if origtype is None:
|
||||
raise Exception('Unsupported type "%s"' % origtypename)
|
||||
if type(obj) == origtype:
|
||||
newobj = obj
|
||||
elif isinstance(obj, dict):
|
||||
# New style class
|
||||
newobj = origtype()
|
||||
for k,v in obj.items():
|
||||
setattr(newobj, k, v)
|
||||
else:
|
||||
# Assume we can coerce the type
|
||||
newobj = origtype(obj)
|
||||
|
||||
if isinstance(newobj, bb.command.DataStoreConnectionHandle):
|
||||
connector = TinfoilDataStoreConnector(self, newobj.dsindex)
|
||||
newobj = bb.data.init()
|
||||
newobj.setVar('_remote_data', connector)
|
||||
|
||||
return newobj
|
||||
|
||||
|
||||
class TinfoilConfigParameters(BitBakeConfigParameters):
|
||||
|
||||
def __init__(self, config_only, **options):
|
||||
def __init__(self, **options):
|
||||
self.initial_options = options
|
||||
# Apply some sane defaults
|
||||
if not 'parse_only' in options:
|
||||
self.initial_options['parse_only'] = not config_only
|
||||
#if not 'status_only' in options:
|
||||
# self.initial_options['status_only'] = config_only
|
||||
if not 'ui' in options:
|
||||
self.initial_options['ui'] = 'knotty'
|
||||
if not 'argv' in options:
|
||||
self.initial_options['argv'] = []
|
||||
|
||||
super(TinfoilConfigParameters, self).__init__()
|
||||
|
||||
def parseCommandLine(self, argv=None):
|
||||
# We don't want any parameters parsed from the command line
|
||||
opts = super(TinfoilConfigParameters, self).parseCommandLine([])
|
||||
for key, val in self.initial_options.items():
|
||||
setattr(opts[0], key, val)
|
||||
return opts
|
||||
def parseCommandLine(self, argv=sys.argv):
|
||||
class DummyOptions:
|
||||
def __init__(self, initial_options):
|
||||
for key, val in initial_options.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
return DummyOptions(self.initial_options), None
|
||||
|
||||
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.
|
||||
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)
|
||||
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()
|
||||
122
bitbake/lib/bb/ui/crumbs/hig/settingsuihelper.py
Normal file
122
bitbake/lib/bb/ui/crumbs/hig/settingsuihelper.py
Normal file
@@ -0,0 +1,122 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011-2012 Intel Corporation
|
||||
#
|
||||
# Authored by Joshua Lock <josh@linux.intel.com>
|
||||
# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
|
||||
# Authored by Shane Wang <shane.wang@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
import os
|
||||
from bb.ui.crumbs.hobwidget import HobInfoButton, HobButton, HobAltButton
|
||||
|
||||
"""
|
||||
The following are convenience classes for implementing GNOME HIG compliant
|
||||
BitBake GUI's
|
||||
In summary: spacing = 12px, border-width = 6px
|
||||
"""
|
||||
|
||||
class SettingsUIHelper():
|
||||
|
||||
def gen_label_widget(self, content):
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup(content)
|
||||
label.show()
|
||||
return label
|
||||
|
||||
def gen_label_info_widget(self, content, tooltip):
|
||||
table = gtk.Table(1, 10, False)
|
||||
label = self.gen_label_widget(content)
|
||||
info = HobInfoButton(tooltip, self)
|
||||
table.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL)
|
||||
table.attach(info, 1, 2, 0, 1, xoptions=gtk.FILL, xpadding=10)
|
||||
return table
|
||||
|
||||
def gen_spinner_widget(self, content, lower, upper, tooltip=""):
|
||||
hbox = gtk.HBox(False, 12)
|
||||
adjust = gtk.Adjustment(value=content, lower=lower, upper=upper, step_incr=1)
|
||||
spinner = gtk.SpinButton(adjustment=adjust, climb_rate=1, digits=0)
|
||||
|
||||
spinner.set_value(content)
|
||||
hbox.pack_start(spinner, expand=False, fill=False)
|
||||
|
||||
info = HobInfoButton(tooltip, self)
|
||||
hbox.pack_start(info, expand=False, fill=False)
|
||||
|
||||
hbox.show_all()
|
||||
return hbox, spinner
|
||||
|
||||
def gen_combo_widget(self, curr_item, all_item, tooltip=""):
|
||||
hbox = gtk.HBox(False, 12)
|
||||
combo = gtk.combo_box_new_text()
|
||||
hbox.pack_start(combo, expand=False, fill=False)
|
||||
|
||||
index = 0
|
||||
for item in all_item or []:
|
||||
combo.append_text(item)
|
||||
if item == curr_item:
|
||||
combo.set_active(index)
|
||||
index += 1
|
||||
|
||||
info = HobInfoButton(tooltip, self)
|
||||
hbox.pack_start(info, expand=False, fill=False)
|
||||
|
||||
hbox.show_all()
|
||||
return hbox, combo
|
||||
|
||||
def entry_widget_select_path_cb(self, action, parent, entry):
|
||||
dialog = gtk.FileChooserDialog("", parent,
|
||||
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
|
||||
text = entry.get_text()
|
||||
dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
|
||||
button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
|
||||
HobAltButton.style_button(button)
|
||||
button = dialog.add_button("Open", gtk.RESPONSE_YES)
|
||||
HobButton.style_button(button)
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_YES:
|
||||
path = dialog.get_filename()
|
||||
entry.set_text(path)
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
def gen_entry_widget(self, content, parent, tooltip="", need_button=True):
|
||||
hbox = gtk.HBox(False, 12)
|
||||
entry = gtk.Entry()
|
||||
entry.set_text(content)
|
||||
entry.set_size_request(350,30)
|
||||
|
||||
if need_button:
|
||||
table = gtk.Table(1, 10, False)
|
||||
hbox.pack_start(table, expand=True, fill=True)
|
||||
table.attach(entry, 0, 9, 0, 1, xoptions=gtk.SHRINK)
|
||||
image = gtk.Image()
|
||||
image.set_from_stock(gtk.STOCK_OPEN,gtk.ICON_SIZE_BUTTON)
|
||||
open_button = gtk.Button()
|
||||
open_button.set_image(image)
|
||||
open_button.connect("clicked", self.entry_widget_select_path_cb, parent, entry)
|
||||
table.attach(open_button, 9, 10, 0, 1, xoptions=gtk.SHRINK)
|
||||
else:
|
||||
hbox.pack_start(entry, expand=True, fill=True)
|
||||
|
||||
if tooltip != "":
|
||||
info = HobInfoButton(tooltip, self)
|
||||
hbox.pack_start(info, expand=False, fill=False)
|
||||
|
||||
hbox.show_all()
|
||||
return hbox, entry
|
||||
38
bitbake/lib/bb/ui/crumbs/hobcolor.py
Normal file
38
bitbake/lib/bb/ui/crumbs/hobcolor.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2012 Intel Corporation
|
||||
#
|
||||
# Authored by Shane Wang <shane.wang@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
class HobColors:
|
||||
WHITE = "#ffffff"
|
||||
PALE_GREEN = "#aaffaa"
|
||||
ORANGE = "#eb8e68"
|
||||
PALE_RED = "#ffaaaa"
|
||||
GRAY = "#aaaaaa"
|
||||
LIGHT_GRAY = "#dddddd"
|
||||
SLIGHT_DARK = "#5f5f5f"
|
||||
DARK = "#3c3b37"
|
||||
BLACK = "#000000"
|
||||
PALE_BLUE = "#53b8ff"
|
||||
DEEP_RED = "#aa3e3e"
|
||||
KHAKI = "#fff68f"
|
||||
|
||||
OK = WHITE
|
||||
RUNNING = PALE_GREEN
|
||||
WARNING = ORANGE
|
||||
ERROR = PALE_RED
|
||||
904
bitbake/lib/bb/ui/crumbs/hobwidget.py
Normal file
904
bitbake/lib/bb/ui/crumbs/hobwidget.py
Normal file
@@ -0,0 +1,904 @@
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011-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 gobject
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import pango, pangocairo
|
||||
import cairo
|
||||
import math
|
||||
|
||||
from bb.ui.crumbs.hobcolor import HobColors
|
||||
from bb.ui.crumbs.persistenttooltip import PersistentTooltip
|
||||
|
||||
class hwc:
|
||||
|
||||
MAIN_WIN_WIDTH = 1024
|
||||
MAIN_WIN_HEIGHT = 700
|
||||
|
||||
class hic:
|
||||
|
||||
HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("ui/icons/"))
|
||||
|
||||
ICON_RCIPE_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_display.png'))
|
||||
ICON_RCIPE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_hover.png'))
|
||||
ICON_PACKAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_display.png'))
|
||||
ICON_PACKAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_hover.png'))
|
||||
ICON_LAYERS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_display.png'))
|
||||
ICON_LAYERS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_hover.png'))
|
||||
ICON_IMAGES_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_display.png'))
|
||||
ICON_IMAGES_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('images/images_hover.png'))
|
||||
ICON_SETTINGS_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_display.png'))
|
||||
ICON_SETTINGS_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_hover.png'))
|
||||
ICON_INFO_DISPLAY_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
|
||||
ICON_INFO_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('info/info_hover.png'))
|
||||
ICON_INDI_CONFIRM_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/confirmation.png'))
|
||||
ICON_INDI_ERROR_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/denied.png'))
|
||||
ICON_INDI_REMOVE_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove.png'))
|
||||
ICON_INDI_REMOVE_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove-hover.png'))
|
||||
ICON_INDI_ADD_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add.png'))
|
||||
ICON_INDI_ADD_HOVER_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add-hover.png'))
|
||||
ICON_INDI_REFRESH_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/refresh.png'))
|
||||
ICON_INDI_ALERT_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/alert.png'))
|
||||
ICON_INDI_TICK_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/tick.png'))
|
||||
ICON_INDI_INFO_FILE = os.path.join(HOB_ICON_BASE_DIR, ('indicators/info.png'))
|
||||
|
||||
class HobViewTable (gtk.VBox):
|
||||
"""
|
||||
A VBox to contain the table for different recipe views and package view
|
||||
"""
|
||||
__gsignals__ = {
|
||||
"toggled" : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_PYOBJECT,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_INT,
|
||||
gobject.TYPE_PYOBJECT,)),
|
||||
"row-activated" : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_PYOBJECT,
|
||||
gobject.TYPE_PYOBJECT,)),
|
||||
"cell-fadeinout-stopped" : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_PYOBJECT,
|
||||
gobject.TYPE_PYOBJECT,
|
||||
gobject.TYPE_PYOBJECT,)),
|
||||
}
|
||||
|
||||
def __init__(self, columns, name):
|
||||
gtk.VBox.__init__(self, False, 6)
|
||||
self.table_tree = gtk.TreeView()
|
||||
self.table_tree.set_headers_visible(True)
|
||||
self.table_tree.set_headers_clickable(True)
|
||||
self.table_tree.set_rules_hint(True)
|
||||
self.table_tree.set_enable_tree_lines(True)
|
||||
self.table_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
||||
self.toggle_columns = []
|
||||
self.table_tree.connect("row-activated", self.row_activated_cb)
|
||||
self.top_bar = None
|
||||
self.tab_name = name
|
||||
|
||||
for i, column in enumerate(columns):
|
||||
col_name = column['col_name']
|
||||
col = gtk.TreeViewColumn(col_name)
|
||||
col.set_clickable(True)
|
||||
col.set_resizable(True)
|
||||
if self.tab_name.startswith('Included'):
|
||||
if col_name!='Included':
|
||||
col.set_sort_column_id(column['col_id'])
|
||||
else:
|
||||
col.set_sort_column_id(column['col_id'])
|
||||
if 'col_min' in column.keys():
|
||||
col.set_min_width(column['col_min'])
|
||||
if 'col_max' in column.keys():
|
||||
col.set_max_width(column['col_max'])
|
||||
if 'expand' in column.keys():
|
||||
col.set_expand(True)
|
||||
self.table_tree.append_column(col)
|
||||
|
||||
if (not 'col_style' in column.keys()) or column['col_style'] == 'text':
|
||||
cell = gtk.CellRendererText()
|
||||
col.pack_start(cell, True)
|
||||
col.set_attributes(cell, text=column['col_id'])
|
||||
if 'col_t_id' in column.keys():
|
||||
col.add_attribute(cell, 'font', column['col_t_id'])
|
||||
elif column['col_style'] == 'check toggle':
|
||||
cell = HobCellRendererToggle()
|
||||
cell.set_property('activatable', True)
|
||||
cell.connect("toggled", self.toggled_cb, i, self.table_tree)
|
||||
cell.connect_render_state_changed(self.stop_cell_fadeinout_cb, self.table_tree)
|
||||
self.toggle_id = i
|
||||
col.pack_end(cell, True)
|
||||
col.set_attributes(cell, active=column['col_id'])
|
||||
self.toggle_columns.append(col_name)
|
||||
if 'col_group' in column.keys():
|
||||
col.set_cell_data_func(cell, self.set_group_number_cb)
|
||||
elif column['col_style'] == 'radio toggle':
|
||||
cell = gtk.CellRendererToggle()
|
||||
cell.set_property('activatable', True)
|
||||
cell.set_radio(True)
|
||||
cell.connect("toggled", self.toggled_cb, i, self.table_tree)
|
||||
self.toggle_id = i
|
||||
col.pack_end(cell, True)
|
||||
col.set_attributes(cell, active=column['col_id'])
|
||||
self.toggle_columns.append(col_name)
|
||||
elif column['col_style'] == 'binb':
|
||||
cell = gtk.CellRendererText()
|
||||
col.pack_start(cell, True)
|
||||
col.set_cell_data_func(cell, self.display_binb_cb, column['col_id'])
|
||||
if 'col_t_id' in column.keys():
|
||||
col.add_attribute(cell, 'font', column['col_t_id'])
|
||||
|
||||
self.scroll = gtk.ScrolledWindow()
|
||||
self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
self.scroll.add(self.table_tree)
|
||||
|
||||
self.pack_end(self.scroll, True, True, 0)
|
||||
|
||||
def add_no_result_bar(self, entry):
|
||||
color = HobColors.KHAKI
|
||||
self.top_bar = gtk.EventBox()
|
||||
self.top_bar.set_size_request(-1, 70)
|
||||
self.top_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
|
||||
self.top_bar.set_flags(gtk.CAN_DEFAULT)
|
||||
self.top_bar.grab_default()
|
||||
|
||||
no_result_tab = gtk.Table(5, 20, True)
|
||||
self.top_bar.add(no_result_tab)
|
||||
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0.0, 0.5)
|
||||
title = "No results matching your search"
|
||||
label.set_markup("<span size='x-large'><b>%s</b></span>" % title)
|
||||
no_result_tab.attach(label, 1, 14, 1, 4)
|
||||
|
||||
clear_button = HobButton("Clear search")
|
||||
clear_button.set_tooltip_text("Clear search query")
|
||||
clear_button.connect('clicked', self.set_search_entry_clear_cb, entry)
|
||||
no_result_tab.attach(clear_button, 16, 19, 1, 4)
|
||||
|
||||
self.pack_start(self.top_bar, False, True, 12)
|
||||
self.top_bar.show_all()
|
||||
|
||||
def set_search_entry_clear_cb(self, button, search):
|
||||
if search.get_editable() == True:
|
||||
search.set_text("")
|
||||
search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
search.grab_focus()
|
||||
|
||||
def display_binb_cb(self, col, cell, model, it, col_id):
|
||||
binb = model.get_value(it, col_id)
|
||||
# Just display the first item
|
||||
if binb:
|
||||
bin = binb.split(', ')
|
||||
total_no = len(bin)
|
||||
if total_no > 1 and bin[0] == "User Selected":
|
||||
if total_no > 2:
|
||||
present_binb = bin[1] + ' (+' + str(total_no - 1) + ')'
|
||||
else:
|
||||
present_binb = bin[1]
|
||||
else:
|
||||
if total_no > 1:
|
||||
present_binb = bin[0] + ' (+' + str(total_no - 1) + ')'
|
||||
else:
|
||||
present_binb = bin[0]
|
||||
cell.set_property('text', present_binb)
|
||||
else:
|
||||
cell.set_property('text', "")
|
||||
return True
|
||||
|
||||
def set_model(self, tree_model):
|
||||
self.table_tree.set_model(tree_model)
|
||||
|
||||
def toggle_default(self):
|
||||
model = self.table_tree.get_model()
|
||||
if not model:
|
||||
return
|
||||
iter = model.get_iter_first()
|
||||
if iter:
|
||||
rowpath = model.get_path(iter)
|
||||
model[rowpath][self.toggle_id] = True
|
||||
|
||||
def toggled_cb(self, cell, path, columnid, tree):
|
||||
self.emit("toggled", cell, path, columnid, tree)
|
||||
|
||||
def row_activated_cb(self, tree, path, view_column):
|
||||
if not view_column.get_title() in self.toggle_columns:
|
||||
self.emit("row-activated", tree.get_model(), path)
|
||||
|
||||
def stop_cell_fadeinout_cb(self, ctrl, cell, tree):
|
||||
self.emit("cell-fadeinout-stopped", ctrl, cell, tree)
|
||||
|
||||
def set_group_number_cb(self, col, cell, model, iter):
|
||||
if model and (model.iter_parent(iter) == None):
|
||||
cell.cell_attr["number_of_children"] = model.iter_n_children(iter)
|
||||
else:
|
||||
cell.cell_attr["number_of_children"] = 0
|
||||
|
||||
def connect_group_selection(self, cb_func):
|
||||
self.table_tree.get_selection().connect("changed", cb_func)
|
||||
|
||||
"""
|
||||
A method to calculate a softened value for the colour of widget when in the
|
||||
provided state.
|
||||
|
||||
widget: the widget whose style to use
|
||||
state: the state of the widget to use the style for
|
||||
|
||||
Returns a string value representing the softened colour
|
||||
"""
|
||||
def soften_color(widget, state=gtk.STATE_NORMAL):
|
||||
# this colour munging routine is heavily inspired bu gdu_util_get_mix_color()
|
||||
# from gnome-disk-utility:
|
||||
# http://git.gnome.org/browse/gnome-disk-utility/tree/src/gdu-gtk/gdu-gtk.c?h=gnome-3-0
|
||||
blend = 0.7
|
||||
style = widget.get_style()
|
||||
color = style.text[state]
|
||||
color.red = color.red * blend + style.base[state].red * (1.0 - blend)
|
||||
color.green = color.green * blend + style.base[state].green * (1.0 - blend)
|
||||
color.blue = color.blue * blend + style.base[state].blue * (1.0 - blend)
|
||||
return color.to_string()
|
||||
|
||||
class BaseHobButton(gtk.Button):
|
||||
"""
|
||||
A gtk.Button subclass which follows the visual design of Hob for primary
|
||||
action buttons
|
||||
|
||||
label: the text to display as the button's label
|
||||
"""
|
||||
def __init__(self, label):
|
||||
gtk.Button.__init__(self, label)
|
||||
HobButton.style_button(self)
|
||||
|
||||
@staticmethod
|
||||
def style_button(button):
|
||||
style = button.get_style()
|
||||
style = gtk.rc_get_style_by_paths(gtk.settings_get_default(), 'gtk-button', 'gtk-button', gobject.TYPE_NONE)
|
||||
|
||||
button.set_flags(gtk.CAN_DEFAULT)
|
||||
button.grab_default()
|
||||
|
||||
# label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
|
||||
label = button.get_label()
|
||||
button.set_label(label)
|
||||
button.child.set_use_markup(True)
|
||||
|
||||
class HobButton(BaseHobButton):
|
||||
"""
|
||||
A gtk.Button subclass which follows the visual design of Hob for primary
|
||||
action buttons
|
||||
|
||||
label: the text to display as the button's label
|
||||
"""
|
||||
def __init__(self, label):
|
||||
BaseHobButton.__init__(self, label)
|
||||
HobButton.style_button(self)
|
||||
|
||||
class HobAltButton(BaseHobButton):
|
||||
"""
|
||||
A gtk.Button subclass which has no relief, and so is more discrete
|
||||
"""
|
||||
def __init__(self, label):
|
||||
BaseHobButton.__init__(self, label)
|
||||
HobAltButton.style_button(self)
|
||||
|
||||
"""
|
||||
A callback for the state-changed event to ensure the text is displayed
|
||||
differently when the widget is not sensitive
|
||||
"""
|
||||
@staticmethod
|
||||
def desensitise_on_state_change_cb(button, state):
|
||||
if not button.get_property("sensitive"):
|
||||
HobAltButton.set_text(button, False)
|
||||
else:
|
||||
HobAltButton.set_text(button, True)
|
||||
|
||||
"""
|
||||
Set the button label with an appropriate colour for the current widget state
|
||||
"""
|
||||
@staticmethod
|
||||
def set_text(button, sensitive=True):
|
||||
if sensitive:
|
||||
colour = HobColors.PALE_BLUE
|
||||
else:
|
||||
colour = HobColors.LIGHT_GRAY
|
||||
button.set_label("<span size='large' color='%s'><b>%s</b></span>" % (colour, gobject.markup_escape_text(button.text)))
|
||||
button.child.set_use_markup(True)
|
||||
|
||||
class HobImageButton(gtk.Button):
|
||||
"""
|
||||
A gtk.Button with an icon and two rows of text, the second of which is
|
||||
displayed in a blended colour.
|
||||
|
||||
primary_text: the main button label
|
||||
secondary_text: optional second line of text
|
||||
icon_path: path to the icon file to display on the button
|
||||
"""
|
||||
def __init__(self, primary_text, secondary_text="", icon_path="", hover_icon_path=""):
|
||||
gtk.Button.__init__(self)
|
||||
self.set_relief(gtk.RELIEF_NONE)
|
||||
|
||||
self.icon_path = icon_path
|
||||
self.hover_icon_path = hover_icon_path
|
||||
|
||||
hbox = gtk.HBox(False, 10)
|
||||
hbox.show()
|
||||
self.add(hbox)
|
||||
self.icon = gtk.Image()
|
||||
self.icon.set_from_file(self.icon_path)
|
||||
self.icon.set_alignment(0.5, 0.0)
|
||||
self.icon.show()
|
||||
if self.hover_icon_path and len(self.hover_icon_path):
|
||||
self.connect("enter-notify-event", self.set_hover_icon_cb)
|
||||
self.connect("leave-notify-event", self.set_icon_cb)
|
||||
hbox.pack_start(self.icon, False, False, 0)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0.0, 0.5)
|
||||
colour = soften_color(label)
|
||||
mark = "<span size='x-large'>%s</span>\n<span size='medium' fgcolor='%s' weight='ultralight'>%s</span>" % (primary_text, colour, secondary_text)
|
||||
label.set_markup(mark)
|
||||
label.show()
|
||||
hbox.pack_start(label, True, True, 0)
|
||||
|
||||
def set_hover_icon_cb(self, widget, event):
|
||||
self.icon.set_from_file(self.hover_icon_path)
|
||||
|
||||
def set_icon_cb(self, widget, event):
|
||||
self.icon.set_from_file(self.icon_path)
|
||||
|
||||
class HobInfoButton(gtk.EventBox):
|
||||
"""
|
||||
This class implements a button-like widget per the Hob visual and UX designs
|
||||
which will display a persistent tooltip, with the contents of tip_markup, when
|
||||
clicked.
|
||||
|
||||
tip_markup: the Pango Markup to be displayed in the persistent tooltip
|
||||
"""
|
||||
def __init__(self, tip_markup, parent=None):
|
||||
gtk.EventBox.__init__(self)
|
||||
self.image = gtk.Image()
|
||||
self.image.set_from_file(
|
||||
hic.ICON_INFO_DISPLAY_FILE)
|
||||
self.image.show()
|
||||
self.add(self.image)
|
||||
self.tip_markup = tip_markup
|
||||
self.my_parent = parent
|
||||
|
||||
self.set_events(gtk.gdk.BUTTON_RELEASE |
|
||||
gtk.gdk.ENTER_NOTIFY_MASK |
|
||||
gtk.gdk.LEAVE_NOTIFY_MASK)
|
||||
|
||||
self.connect("button-release-event", self.button_release_cb)
|
||||
self.connect("enter-notify-event", self.mouse_in_cb)
|
||||
self.connect("leave-notify-event", self.mouse_out_cb)
|
||||
|
||||
"""
|
||||
When the mouse click is released emulate a button-click and show the associated
|
||||
PersistentTooltip
|
||||
"""
|
||||
def button_release_cb(self, widget, event):
|
||||
from bb.ui.crumbs.hig.propertydialog import PropertyDialog
|
||||
self.dialog = PropertyDialog(title = '',
|
||||
parent = self.my_parent,
|
||||
information = self.tip_markup,
|
||||
flags = gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.DIALOG_NO_SEPARATOR)
|
||||
|
||||
button = self.dialog.add_button("Close", gtk.RESPONSE_CANCEL)
|
||||
HobAltButton.style_button(button)
|
||||
button.connect("clicked", lambda w: self.dialog.destroy())
|
||||
self.dialog.show_all()
|
||||
self.dialog.run()
|
||||
|
||||
"""
|
||||
Change to the prelight image when the mouse enters the widget
|
||||
"""
|
||||
def mouse_in_cb(self, widget, event):
|
||||
self.image.set_from_file(hic.ICON_INFO_HOVER_FILE)
|
||||
|
||||
"""
|
||||
Change to the stock image when the mouse enters the widget
|
||||
"""
|
||||
def mouse_out_cb(self, widget, event):
|
||||
self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
|
||||
|
||||
class HobIndicator(gtk.DrawingArea):
|
||||
def __init__(self, count):
|
||||
gtk.DrawingArea.__init__(self)
|
||||
# Set no window for transparent background
|
||||
self.set_has_window(False)
|
||||
self.set_size_request(38,38)
|
||||
# We need to pass through button clicks
|
||||
self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
|
||||
|
||||
self.connect('expose-event', self.expose)
|
||||
|
||||
self.count = count
|
||||
self.color = HobColors.GRAY
|
||||
|
||||
def expose(self, widget, event):
|
||||
if self.count and self.count > 0:
|
||||
ctx = widget.window.cairo_create()
|
||||
|
||||
x, y, w, h = self.allocation
|
||||
|
||||
ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
ctx.set_source_color(gtk.gdk.color_parse(self.color))
|
||||
ctx.translate(w/2, h/2)
|
||||
ctx.arc(x, y, min(w,h)/2 - 2, 0, 2*math.pi)
|
||||
ctx.fill_preserve()
|
||||
|
||||
layout = self.create_pango_layout(str(self.count))
|
||||
textw, texth = layout.get_pixel_size()
|
||||
x = (w/2)-(textw/2) + x
|
||||
y = (h/2) - (texth/2) + y
|
||||
ctx.move_to(x, y)
|
||||
self.window.draw_layout(self.style.light_gc[gtk.STATE_NORMAL], int(x), int(y), layout)
|
||||
|
||||
def set_count(self, count):
|
||||
self.count = count
|
||||
|
||||
def set_active(self, active):
|
||||
if active:
|
||||
self.color = HobColors.DEEP_RED
|
||||
else:
|
||||
self.color = HobColors.GRAY
|
||||
|
||||
class HobTabLabel(gtk.HBox):
|
||||
def __init__(self, text, count=0):
|
||||
gtk.HBox.__init__(self, False, 0)
|
||||
self.indicator = HobIndicator(count)
|
||||
self.indicator.show()
|
||||
self.pack_end(self.indicator, False, False)
|
||||
self.lbl = gtk.Label(text)
|
||||
self.lbl.set_alignment(0.0, 0.5)
|
||||
self.lbl.show()
|
||||
self.pack_end(self.lbl, True, True, 6)
|
||||
|
||||
def set_count(self, count):
|
||||
self.indicator.set_count(count)
|
||||
|
||||
def set_active(self, active=True):
|
||||
self.indicator.set_active(active)
|
||||
|
||||
class HobNotebook(gtk.Notebook):
|
||||
def __init__(self):
|
||||
gtk.Notebook.__init__(self)
|
||||
self.set_property('homogeneous', True)
|
||||
|
||||
self.pages = []
|
||||
|
||||
self.search = None
|
||||
self.search_focus = False
|
||||
self.page_changed = False
|
||||
|
||||
self.connect("switch-page", self.page_changed_cb)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def page_changed_cb(self, nb, page, page_num):
|
||||
for p, lbl in enumerate(self.pages):
|
||||
if p == page_num:
|
||||
lbl.set_active()
|
||||
else:
|
||||
lbl.set_active(False)
|
||||
|
||||
if self.search:
|
||||
self.page_changed = True
|
||||
self.reset_entry(self.search, page_num)
|
||||
|
||||
def append_page(self, child, tab_label, tab_tooltip=None):
|
||||
label = HobTabLabel(tab_label)
|
||||
if tab_tooltip:
|
||||
label.set_tooltip_text(tab_tooltip)
|
||||
label.set_active(False)
|
||||
self.pages.append(label)
|
||||
gtk.Notebook.append_page(self, child, label)
|
||||
|
||||
def set_entry(self, names, tips):
|
||||
self.search = gtk.Entry()
|
||||
self.search_names = names
|
||||
self.search_tips = tips
|
||||
style = self.search.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
||||
self.search.set_style(style)
|
||||
self.search.set_text(names[0])
|
||||
self.search.set_tooltip_text(self.search_tips[0])
|
||||
self.search.props.has_tooltip = True
|
||||
|
||||
self.search.set_editable(False)
|
||||
self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR)
|
||||
self.search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
self.search.connect("icon-release", self.set_search_entry_clear_cb)
|
||||
self.search.set_width_chars(30)
|
||||
self.search.show()
|
||||
|
||||
self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
|
||||
self.search.connect("focus-out-event", self.set_search_entry_reset_cb)
|
||||
self.set_action_widget(self.search, gtk.PACK_END)
|
||||
|
||||
def show_indicator_icon(self, title, number):
|
||||
for child in self.pages:
|
||||
if child.lbl.get_label() == title:
|
||||
child.set_count(number)
|
||||
|
||||
def hide_indicator_icon(self, title):
|
||||
for child in self.pages:
|
||||
if child.lbl.get_label() == title:
|
||||
child.set_count(0)
|
||||
|
||||
def set_search_entry_editable_cb(self, search, event):
|
||||
self.search_focus = True
|
||||
search.set_editable(True)
|
||||
text = search.get_text()
|
||||
if text in self.search_names:
|
||||
search.set_text("")
|
||||
style = self.search.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False)
|
||||
search.set_style(style)
|
||||
|
||||
def set_search_entry_reset_cb(self, search, event):
|
||||
page_num = self.get_current_page()
|
||||
text = search.get_text()
|
||||
if not text:
|
||||
self.reset_entry(search, page_num)
|
||||
|
||||
def reset_entry(self, entry, page_num):
|
||||
style = entry.get_style()
|
||||
style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
|
||||
entry.set_style(style)
|
||||
entry.set_text(self.search_names[page_num])
|
||||
entry.set_tooltip_text(self.search_tips[page_num])
|
||||
entry.set_editable(False)
|
||||
entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
|
||||
def set_search_entry_clear_cb(self, search, icon_pos, event):
|
||||
if search.get_editable() == True:
|
||||
search.set_text("")
|
||||
search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
|
||||
search.grab_focus()
|
||||
|
||||
def set_page(self, title):
|
||||
for child in self.pages:
|
||||
if child.lbl.get_label() == title:
|
||||
child.grab_focus()
|
||||
self.set_current_page(self.pages.index(child))
|
||||
return
|
||||
|
||||
class HobWarpCellRendererText(gtk.CellRendererText):
|
||||
def __init__(self, col_number):
|
||||
gtk.CellRendererText.__init__(self)
|
||||
self.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
||||
self.set_property("wrap-width", 300) # default value wrap width is 300
|
||||
self.col_n = col_number
|
||||
|
||||
def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
|
||||
if widget:
|
||||
self.props.wrap_width = self.get_resized_wrap_width(widget, widget.get_column(self.col_n))
|
||||
return gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
|
||||
|
||||
def get_resized_wrap_width(self, treeview, column):
|
||||
otherCols = []
|
||||
for col in treeview.get_columns():
|
||||
if col != column:
|
||||
otherCols.append(col)
|
||||
adjwidth = treeview.allocation.width - sum(c.get_width() for c in otherCols)
|
||||
adjwidth -= treeview.style_get_property("horizontal-separator") * 4
|
||||
if self.props.wrap_width == adjwidth or adjwidth <= 0:
|
||||
adjwidth = self.props.wrap_width
|
||||
return adjwidth
|
||||
|
||||
gobject.type_register(HobWarpCellRendererText)
|
||||
|
||||
class HobIconChecker(hic):
|
||||
def set_hob_icon_to_stock_icon(self, file_path, stock_id=""):
|
||||
try:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(file_path)
|
||||
except Exception, e:
|
||||
return None
|
||||
|
||||
if stock_id and (gtk.icon_factory_lookup_default(stock_id) == None):
|
||||
icon_factory = gtk.IconFactory()
|
||||
icon_factory.add_default()
|
||||
icon_factory.add(stock_id, gtk.IconSet(pixbuf))
|
||||
gtk.stock_add([(stock_id, '_label', 0, 0, '')])
|
||||
|
||||
return icon_factory.lookup(stock_id)
|
||||
|
||||
return None
|
||||
|
||||
"""
|
||||
For make hob icon consistently by request, and avoid icon view diff by system or gtk version, we use some 'hob icon' to replace the 'gtk icon'.
|
||||
this function check the stock_id and make hob_id to replaced the gtk_id then return it or ""
|
||||
"""
|
||||
def check_stock_icon(self, stock_name=""):
|
||||
HOB_CHECK_STOCK_NAME = {
|
||||
('hic-dialog-info', 'gtk-dialog-info', 'dialog-info') : self.ICON_INDI_INFO_FILE,
|
||||
('hic-ok', 'gtk-ok', 'ok') : self.ICON_INDI_TICK_FILE,
|
||||
('hic-dialog-error', 'gtk-dialog-error', 'dialog-error') : self.ICON_INDI_ERROR_FILE,
|
||||
('hic-dialog-warning', 'gtk-dialog-warning', 'dialog-warning') : self.ICON_INDI_ALERT_FILE,
|
||||
('hic-task-refresh', 'gtk-execute', 'execute') : self.ICON_INDI_REFRESH_FILE,
|
||||
}
|
||||
valid_stock_id = stock_name
|
||||
if stock_name:
|
||||
for names, path in HOB_CHECK_STOCK_NAME.iteritems():
|
||||
if stock_name in names:
|
||||
valid_stock_id = names[0]
|
||||
if not gtk.icon_factory_lookup_default(valid_stock_id):
|
||||
self.set_hob_icon_to_stock_icon(path, valid_stock_id)
|
||||
|
||||
return valid_stock_id
|
||||
|
||||
class HobCellRendererController(gobject.GObject):
|
||||
(MODE_CYCLE_RUNNING, MODE_ONE_SHORT) = range(2)
|
||||
__gsignals__ = {
|
||||
"run-timer-stopped" : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
}
|
||||
def __init__(self, runningmode=MODE_CYCLE_RUNNING, is_draw_row=False):
|
||||
gobject.GObject.__init__(self)
|
||||
self.timeout_id = None
|
||||
self.current_angle_pos = 0.0
|
||||
self.step_angle = 0.0
|
||||
self.tree_headers_height = 0
|
||||
self.running_cell_areas = []
|
||||
self.running_mode = runningmode
|
||||
self.is_queue_draw_row_area = is_draw_row
|
||||
self.force_stop_enable = False
|
||||
|
||||
def is_active(self):
|
||||
if self.timeout_id:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def reset_run(self):
|
||||
self.force_stop()
|
||||
self.running_cell_areas = []
|
||||
self.current_angle_pos = 0.0
|
||||
self.step_angle = 0.0
|
||||
|
||||
''' time_iterval: (1~1000)ms, which will be as the basic interval count for timer
|
||||
init_usrdata: the current data which related the progress-bar will be at
|
||||
min_usrdata: the range of min of user data
|
||||
max_usrdata: the range of max of user data
|
||||
step: each step which you want to progress
|
||||
Note: the init_usrdata should in the range of from min to max, and max should > min
|
||||
step should < (max - min)
|
||||
'''
|
||||
def start_run(self, time_iterval, init_usrdata, min_usrdata, max_usrdata, step, tree):
|
||||
if (not time_iterval) or (not max_usrdata):
|
||||
return
|
||||
usr_range = (max_usrdata - min_usrdata) * 1.0
|
||||
self.current_angle_pos = (init_usrdata * 1.0) / usr_range
|
||||
self.step_angle = (step * 1) / usr_range
|
||||
self.timeout_id = gobject.timeout_add(int(time_iterval),
|
||||
self.make_image_on_progressing_cb, tree)
|
||||
self.tree_headers_height = self.get_treeview_headers_height(tree)
|
||||
self.force_stop_enable = False
|
||||
|
||||
def force_stop(self):
|
||||
self.emit("run-timer-stopped")
|
||||
self.force_stop_enable = True
|
||||
if self.timeout_id:
|
||||
if gobject.source_remove(self.timeout_id):
|
||||
self.timeout_id = None
|
||||
|
||||
def on_draw_pixbuf_cb(self, pixbuf, cr, x, y, img_width, img_height, do_refresh=True):
|
||||
if pixbuf:
|
||||
r = max(img_width/2, img_height/2)
|
||||
cr.translate(x + r, y + r)
|
||||
if do_refresh:
|
||||
cr.rotate(2 * math.pi * self.current_angle_pos)
|
||||
|
||||
cr.set_source_pixbuf(pixbuf, -img_width/2, -img_height/2)
|
||||
cr.paint()
|
||||
|
||||
def on_draw_fadeinout_cb(self, cr, color, x, y, width, height, do_fadeout=True):
|
||||
if do_fadeout:
|
||||
alpha = self.current_angle_pos * 0.8
|
||||
else:
|
||||
alpha = (1.0 - self.current_angle_pos) * 0.8
|
||||
|
||||
cr.set_source_rgba(color.red, color.green, color.blue, alpha)
|
||||
cr.rectangle(x, y, width, height)
|
||||
cr.fill()
|
||||
|
||||
def get_treeview_headers_height(self, tree):
|
||||
if tree and (tree.get_property("headers-visible") == True):
|
||||
height = tree.get_allocation().height - tree.get_bin_window().get_size()[1]
|
||||
return height
|
||||
|
||||
return 0
|
||||
|
||||
def make_image_on_progressing_cb(self, tree):
|
||||
self.current_angle_pos += self.step_angle
|
||||
if self.running_mode == self.MODE_CYCLE_RUNNING:
|
||||
if (self.current_angle_pos >= 1):
|
||||
self.current_angle_pos = 0
|
||||
else:
|
||||
if self.current_angle_pos > 1:
|
||||
self.force_stop()
|
||||
return False
|
||||
|
||||
if self.is_queue_draw_row_area:
|
||||
for path in self.running_cell_areas:
|
||||
rect = tree.get_cell_area(path, tree.get_column(0))
|
||||
row_x, _, row_width, _ = tree.get_visible_rect()
|
||||
tree.queue_draw_area(row_x, rect.y + self.tree_headers_height, row_width, rect.height)
|
||||
else:
|
||||
for rect in self.running_cell_areas:
|
||||
tree.queue_draw_area(rect.x, rect.y + self.tree_headers_height, rect.width, rect.height)
|
||||
|
||||
return (not self.force_stop_enable)
|
||||
|
||||
def append_running_cell_area(self, cell_area):
|
||||
if cell_area and (cell_area not in self.running_cell_areas):
|
||||
self.running_cell_areas.append(cell_area)
|
||||
|
||||
def remove_running_cell_area(self, cell_area):
|
||||
if cell_area in self.running_cell_areas:
|
||||
self.running_cell_areas.remove(cell_area)
|
||||
if not self.running_cell_areas:
|
||||
self.reset_run()
|
||||
|
||||
gobject.type_register(HobCellRendererController)
|
||||
|
||||
class HobCellRendererPixbuf(gtk.CellRendererPixbuf):
|
||||
def __init__(self):
|
||||
gtk.CellRendererPixbuf.__init__(self)
|
||||
self.control = HobCellRendererController()
|
||||
# add icon checker for make the gtk-icon transfer to hob-icon
|
||||
self.checker = HobIconChecker()
|
||||
self.set_property("stock-size", gtk.ICON_SIZE_DND)
|
||||
|
||||
def get_pixbuf_from_stock_icon(self, widget, stock_id="", size=gtk.ICON_SIZE_DIALOG):
|
||||
if widget and stock_id and gtk.icon_factory_lookup_default(stock_id):
|
||||
return widget.render_icon(stock_id, size)
|
||||
|
||||
return None
|
||||
|
||||
def set_icon_name_to_id(self, new_name):
|
||||
if new_name and type(new_name) == str:
|
||||
# check the name is need to transfer to hob icon or not
|
||||
name = self.checker.check_stock_icon(new_name)
|
||||
if name.startswith("hic") or name.startswith("gtk"):
|
||||
stock_id = name
|
||||
else:
|
||||
stock_id = 'gtk-' + name
|
||||
|
||||
return stock_id
|
||||
|
||||
''' render cell exactly, "icon-name" is priority
|
||||
if use the 'hic-task-refresh' will make the pix animation
|
||||
if 'pix' will change the pixbuf for it from the pixbuf or image.
|
||||
'''
|
||||
def do_render(self, window, tree, background_area,cell_area, expose_area, flags):
|
||||
if (not self.control) or (not tree):
|
||||
return
|
||||
|
||||
x, y, w, h = self.on_get_size(tree, cell_area)
|
||||
x += cell_area.x
|
||||
y += cell_area.y
|
||||
w -= 2 * self.get_property("xpad")
|
||||
h -= 2 * self.get_property("ypad")
|
||||
|
||||
stock_id = ""
|
||||
if self.props.icon_name:
|
||||
stock_id = self.set_icon_name_to_id(self.props.icon_name)
|
||||
elif self.props.stock_id:
|
||||
stock_id = self.props.stock_id
|
||||
elif self.props.pixbuf:
|
||||
pix = self.props.pixbuf
|
||||
else:
|
||||
return
|
||||
|
||||
if stock_id:
|
||||
pix = self.get_pixbuf_from_stock_icon(tree, stock_id, self.props.stock_size)
|
||||
if stock_id == 'hic-task-refresh':
|
||||
self.control.append_running_cell_area(cell_area)
|
||||
if self.control.is_active():
|
||||
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, True)
|
||||
else:
|
||||
self.control.start_run(200, 0, 0, 1000, 150, tree)
|
||||
else:
|
||||
self.control.remove_running_cell_area(cell_area)
|
||||
self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, False)
|
||||
|
||||
def on_get_size(self, widget, cell_area):
|
||||
if self.props.icon_name or self.props.pixbuf or self.props.stock_id:
|
||||
w, h = gtk.icon_size_lookup(self.props.stock_size)
|
||||
calc_width = self.get_property("xpad") * 2 + w
|
||||
calc_height = self.get_property("ypad") * 2 + h
|
||||
x_offset = 0
|
||||
y_offset = 0
|
||||
if cell_area and w > 0 and h > 0:
|
||||
x_offset = self.get_property("xalign") * (cell_area.width - calc_width - self.get_property("xpad"))
|
||||
y_offset = self.get_property("yalign") * (cell_area.height - calc_height - self.get_property("ypad"))
|
||||
|
||||
return x_offset, y_offset, w, h
|
||||
|
||||
return 0, 0, 0, 0
|
||||
|
||||
gobject.type_register(HobCellRendererPixbuf)
|
||||
|
||||
class HobCellRendererToggle(gtk.CellRendererToggle):
|
||||
def __init__(self):
|
||||
gtk.CellRendererToggle.__init__(self)
|
||||
self.ctrl = HobCellRendererController(is_draw_row=True)
|
||||
self.ctrl.running_mode = self.ctrl.MODE_ONE_SHORT
|
||||
self.cell_attr = {"fadeout": False, "number_of_children": 0}
|
||||
|
||||
def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
|
||||
if (not self.ctrl) or (not widget):
|
||||
return
|
||||
|
||||
if flags & gtk.CELL_RENDERER_SELECTED:
|
||||
state = gtk.STATE_SELECTED
|
||||
else:
|
||||
state = gtk.STATE_NORMAL
|
||||
|
||||
if self.ctrl.is_active():
|
||||
path = widget.get_path_at_pos(cell_area.x + cell_area.width/2, cell_area.y + cell_area.height/2)
|
||||
# sometimes the parameters of cell_area will be a negative number,such as pull up down the scroll bar
|
||||
# it's over the tree container range, so the path will be bad
|
||||
if not path: return
|
||||
path = path[0]
|
||||
if path in self.ctrl.running_cell_areas:
|
||||
cr = window.cairo_create()
|
||||
color = widget.get_style().base[state]
|
||||
|
||||
row_x, _, row_width, _ = widget.get_visible_rect()
|
||||
border_y = self.get_property("ypad")
|
||||
self.ctrl.on_draw_fadeinout_cb(cr, color, row_x, cell_area.y - border_y, row_width, \
|
||||
cell_area.height + border_y * 2, self.cell_attr["fadeout"])
|
||||
# draw number of a group
|
||||
if self.cell_attr["number_of_children"]:
|
||||
text = "%d pkg" % self.cell_attr["number_of_children"]
|
||||
pangolayout = widget.create_pango_layout(text)
|
||||
textw, texth = pangolayout.get_pixel_size()
|
||||
x = cell_area.x + (cell_area.width/2) - (textw/2)
|
||||
y = cell_area.y + (cell_area.height/2) - (texth/2)
|
||||
|
||||
widget.style.paint_layout(window, state, True, cell_area, widget, "checkbox", x, y, pangolayout)
|
||||
else:
|
||||
return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
|
||||
|
||||
'''delay: normally delay time is 1000ms
|
||||
cell_list: whilch cells need to be render
|
||||
'''
|
||||
def fadeout(self, tree, delay, cell_list=None):
|
||||
if (delay < 200) or (not tree):
|
||||
return
|
||||
self.cell_attr["fadeout"] = True
|
||||
self.ctrl.running_cell_areas = cell_list
|
||||
self.ctrl.start_run(200, 0, 0, delay, (delay * 200 / 1000), tree)
|
||||
|
||||
def connect_render_state_changed(self, func, usrdata=None):
|
||||
if not func:
|
||||
return
|
||||
if usrdata:
|
||||
self.ctrl.connect("run-timer-stopped", func, self, usrdata)
|
||||
else:
|
||||
self.ctrl.connect("run-timer-stopped", func, self)
|
||||
|
||||
gobject.type_register(HobCellRendererToggle)
|
||||
186
bitbake/lib/bb/ui/crumbs/persistenttooltip.py
Normal file
186
bitbake/lib/bb/ui/crumbs/persistenttooltip.py
Normal file
@@ -0,0 +1,186 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2012 Intel Corporation
|
||||
#
|
||||
# Authored by Joshua Lock <josh@linux.intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
try:
|
||||
import gconf
|
||||
except:
|
||||
pass
|
||||
|
||||
class PersistentTooltip(gtk.Window):
|
||||
"""
|
||||
A tooltip which persists once shown until the user dismisses it with the Esc
|
||||
key or by clicking the close button.
|
||||
|
||||
# FIXME: the PersistentTooltip should be disabled when the user clicks anywhere off
|
||||
# it. We can't do this with focus-out-event becuase modal ensures we have focus?
|
||||
|
||||
markup: some Pango text markup to display in the tooltip
|
||||
"""
|
||||
def __init__(self, markup, parent_win=None):
|
||||
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
|
||||
|
||||
# Inherit the system theme for a tooltip
|
||||
style = gtk.rc_get_style_by_paths(gtk.settings_get_default(),
|
||||
'gtk-tooltip', 'gtk-tooltip', gobject.TYPE_NONE)
|
||||
self.set_style(style)
|
||||
|
||||
# The placement of the close button on the tip should reflect how the
|
||||
# window manager of the users system places close buttons. Try to read
|
||||
# the metacity gconf key to determine whether the close button is on the
|
||||
# left or the right.
|
||||
# In the case that we can't determine the users configuration we default
|
||||
# to close buttons being on the right.
|
||||
__button_right = True
|
||||
try:
|
||||
client = gconf.client_get_default()
|
||||
order = client.get_string("/apps/metacity/general/button_layout")
|
||||
if order and order.endswith(":"):
|
||||
__button_right = False
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
# We need to ensure we're only shown once
|
||||
self.shown = False
|
||||
|
||||
# We don't want any WM decorations
|
||||
self.set_decorated(False)
|
||||
# We don't want to show in the taskbar or window switcher
|
||||
self.set_skip_pager_hint(True)
|
||||
self.set_skip_taskbar_hint(True)
|
||||
# We must be modal to ensure we grab focus when presented from a gtk.Dialog
|
||||
self.set_modal(True)
|
||||
|
||||
self.set_border_width(0)
|
||||
self.set_position(gtk.WIN_POS_MOUSE)
|
||||
self.set_opacity(0.95)
|
||||
|
||||
# Ensure a reasonable minimum size
|
||||
self.set_geometry_hints(self, 100, 50)
|
||||
|
||||
# Set this window as a transient window for parent(main window)
|
||||
if parent_win:
|
||||
self.set_transient_for(parent_win)
|
||||
self.set_destroy_with_parent(True)
|
||||
# Draw our label and close buttons
|
||||
hbox = gtk.HBox(False, 0)
|
||||
hbox.show()
|
||||
self.add(hbox)
|
||||
|
||||
img = gtk.Image()
|
||||
img.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON)
|
||||
|
||||
self.button = gtk.Button()
|
||||
self.button.set_image(img)
|
||||
self.button.connect("clicked", self._dismiss_cb)
|
||||
self.button.set_flags(gtk.CAN_DEFAULT)
|
||||
self.button.grab_focus()
|
||||
self.button.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
vbox.show()
|
||||
vbox.pack_start(self.button, False, False, 0)
|
||||
if __button_right:
|
||||
hbox.pack_end(vbox, True, True, 0)
|
||||
else:
|
||||
hbox.pack_start(vbox, True, True, 0)
|
||||
|
||||
self.set_default(self.button)
|
||||
|
||||
bin = gtk.HBox(True, 6)
|
||||
bin.set_border_width(6)
|
||||
bin.show()
|
||||
self.label = gtk.Label()
|
||||
self.label.set_line_wrap(True)
|
||||
# We want to match the colours of the normal tooltips, as dictated by
|
||||
# the users gtk+-2.0 theme, wherever possible - on some systems this
|
||||
# requires explicitly setting a fg_color for the label which matches the
|
||||
# tooltip_fg_color
|
||||
settings = gtk.settings_get_default()
|
||||
colours = settings.get_property('gtk-color-scheme').split('\n')
|
||||
# remove any empty lines, there's likely to be a trailing one after
|
||||
# calling split on a dictionary-like string
|
||||
colours = filter(None, colours)
|
||||
for col in colours:
|
||||
item, val = col.split(': ')
|
||||
if item == 'tooltip_fg_color':
|
||||
style = self.label.get_style()
|
||||
style.fg[gtk.STATE_NORMAL] = gtk.gdk.color_parse(val)
|
||||
self.label.set_style(style)
|
||||
break # we only care for the tooltip_fg_color
|
||||
|
||||
self.label.set_markup(markup)
|
||||
self.label.show()
|
||||
bin.add(self.label)
|
||||
hbox.pack_end(bin, True, True, 6)
|
||||
|
||||
# add the original URL display for user reference
|
||||
if 'a href' in markup:
|
||||
hbox.set_tooltip_text(self.get_markup_url(markup))
|
||||
hbox.show()
|
||||
|
||||
self.connect("key-press-event", self._catch_esc_cb)
|
||||
|
||||
"""
|
||||
Callback when the PersistentTooltip's close button is clicked.
|
||||
Hides the PersistentTooltip.
|
||||
"""
|
||||
def _dismiss_cb(self, button):
|
||||
self.hide()
|
||||
return True
|
||||
|
||||
"""
|
||||
Callback when the Esc key is detected. Hides the PersistentTooltip.
|
||||
"""
|
||||
def _catch_esc_cb(self, widget, event):
|
||||
keyname = gtk.gdk.keyval_name(event.keyval)
|
||||
if keyname == "Escape":
|
||||
self.hide()
|
||||
return True
|
||||
|
||||
"""
|
||||
Called to present the PersistentTooltip.
|
||||
Overrides the superclasses show() method to include state tracking.
|
||||
"""
|
||||
def show(self):
|
||||
if not self.shown:
|
||||
self.shown = True
|
||||
gtk.Window.show(self)
|
||||
|
||||
"""
|
||||
Called to hide the PersistentTooltip.
|
||||
Overrides the superclasses hide() method to include state tracking.
|
||||
"""
|
||||
def hide(self):
|
||||
self.shown = False
|
||||
gtk.Window.hide(self)
|
||||
|
||||
"""
|
||||
Called to get the hyperlink URL from markup text.
|
||||
"""
|
||||
def get_markup_url(self, markup):
|
||||
url = "http:"
|
||||
if markup and type(markup) == str:
|
||||
s = markup
|
||||
if 'http:' in s:
|
||||
import re
|
||||
url = re.search('(http:[^,\\ "]+)', s).group(0)
|
||||
|
||||
return url
|
||||
23
bitbake/lib/bb/ui/crumbs/progress.py
Normal file
23
bitbake/lib/bb/ui/crumbs/progress.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import gtk
|
||||
|
||||
class ProgressBar(gtk.Dialog):
|
||||
def __init__(self, parent):
|
||||
|
||||
gtk.Dialog.__init__(self, flags=(gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT))
|
||||
self.set_title("Parsing metadata, please wait...")
|
||||
self.set_default_size(500, 0)
|
||||
self.set_transient_for(parent)
|
||||
self.progress = gtk.ProgressBar()
|
||||
self.vbox.pack_start(self.progress)
|
||||
self.show_all()
|
||||
|
||||
def set_text(self, msg):
|
||||
self.progress.set_text(msg)
|
||||
|
||||
def update(self, x, y):
|
||||
self.progress.set_fraction(float(x)/float(y))
|
||||
self.progress.set_text("%2d %%" % (x*100/y))
|
||||
|
||||
def pulse(self):
|
||||
self.progress.set_text("Loading...")
|
||||
self.progress.pulse()
|
||||
59
bitbake/lib/bb/ui/crumbs/progressbar.py
Normal file
59
bitbake/lib/bb/ui/crumbs/progressbar.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011 Intel Corporation
|
||||
#
|
||||
# Authored by Shane Wang <shane.wang@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
from bb.ui.crumbs.hobcolor import HobColors
|
||||
|
||||
class HobProgressBar (gtk.ProgressBar):
|
||||
def __init__(self):
|
||||
gtk.ProgressBar.__init__(self)
|
||||
self.set_rcstyle(True)
|
||||
self.percentage = 0
|
||||
|
||||
def set_rcstyle(self, status):
|
||||
rcstyle = gtk.RcStyle()
|
||||
rcstyle.fg[2] = gtk.gdk.Color(HobColors.BLACK)
|
||||
if status == "stop":
|
||||
rcstyle.bg[3] = gtk.gdk.Color(HobColors.WARNING)
|
||||
elif status == "fail":
|
||||
rcstyle.bg[3] = gtk.gdk.Color(HobColors.ERROR)
|
||||
else:
|
||||
rcstyle.bg[3] = gtk.gdk.Color(HobColors.RUNNING)
|
||||
self.modify_style(rcstyle)
|
||||
|
||||
def set_title(self, text=None):
|
||||
if not text:
|
||||
text = ""
|
||||
text += " %.0f%%" % self.percentage
|
||||
self.set_text(text)
|
||||
|
||||
def set_stop_title(self, text=None):
|
||||
if not text:
|
||||
text = ""
|
||||
self.set_text(text)
|
||||
|
||||
def reset(self):
|
||||
self.set_fraction(0)
|
||||
self.set_text("")
|
||||
self.set_rcstyle(True)
|
||||
self.percentage = 0
|
||||
|
||||
def update(self, fraction):
|
||||
self.percentage = int(fraction * 100)
|
||||
self.set_fraction(fraction)
|
||||
606
bitbake/lib/bb/ui/crumbs/puccho.glade
Normal file
606
bitbake/lib/bb/ui/crumbs/puccho.glade
Normal file
@@ -0,0 +1,606 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
|
||||
<!--Generated with glade3 3.4.5 on Mon Nov 10 12:24:12 2008 -->
|
||||
<glade-interface>
|
||||
<widget class="GtkDialog" id="build_dialog">
|
||||
<property name="title" translatable="yes">Start a build</property>
|
||||
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="build_table">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">3</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="status_alignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="status_hbox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkImage" id="status_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="stock">gtk-dialog-error</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="status_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">If you see this text something is wrong...</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Build configuration</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="image_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="image_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Image:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="distribution_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="distribution_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Distribution:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="machine_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="machine_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Machine:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="refresh_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-refresh</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="location_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">32</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Location:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Repository</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment3">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<widget class="GtkHButtonBox" id="dialog-action_area1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<widget class="GtkDialog" id="dialog2">
|
||||
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">3</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Repositories</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Additional packages</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment6">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xscale">0</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Location: </property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment7">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xscale">0</property>
|
||||
<child>
|
||||
<widget class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-remove</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-edit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-add</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment5">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="label" translatable="yes">Search:</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment8">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<widget class="GtkHButtonBox" id="dialog-action_area2">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="label" translatable="yes">gtk-close</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="response_id">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<widget class="GtkWindow" id="main_window">
|
||||
<child>
|
||||
<widget class="GtkVBox" id="main_window_vbox">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkToolbar" id="main_toolbar">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkToolButton" id="main_toolbutton_build">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Build</property>
|
||||
<property name="stock_id">gtk-execute</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkVPaned" id="vpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="results_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="progress_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</glade-interface>
|
||||
551
bitbake/lib/bb/ui/crumbs/runningbuild.py
Normal file
551
bitbake/lib/bb/ui/crumbs/runningbuild.py
Normal file
@@ -0,0 +1,551 @@
|
||||
|
||||
#
|
||||
# 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 logging
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import pango
|
||||
from bb.ui.crumbs.hobcolor import HobColors
|
||||
from bb.ui.crumbs.hobwidget import HobWarpCellRendererText, HobCellRendererPixbuf
|
||||
|
||||
class RunningBuildModel (gtk.TreeStore):
|
||||
(COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = range(7)
|
||||
|
||||
def __init__ (self):
|
||||
gtk.TreeStore.__init__ (self,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_INT)
|
||||
|
||||
def failure_model_filter(self, model, it):
|
||||
color = model.get(it, self.COL_COLOR)[0]
|
||||
if not color:
|
||||
return False
|
||||
if color == HobColors.ERROR or color == HobColors.WARNING:
|
||||
return True
|
||||
return False
|
||||
|
||||
def failure_model(self):
|
||||
model = self.filter_new()
|
||||
model.set_visible_func(self.failure_model_filter)
|
||||
return model
|
||||
|
||||
def foreach_cell_func(self, model, path, iter, usr_data=None):
|
||||
if model.get_value(iter, self.COL_ICON) == "gtk-execute":
|
||||
model.set(iter, self.COL_ICON, "")
|
||||
|
||||
def close_task_refresh(self):
|
||||
self.foreach(self.foreach_cell_func, None)
|
||||
|
||||
class RunningBuild (gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'build-started' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'build-failed' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'build-complete' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'build-aborted' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'task-started' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_PYOBJECT,)),
|
||||
'log-error' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'log-warning' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'disk-full' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
()),
|
||||
'no-provider' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_PYOBJECT,)),
|
||||
'log' : (gobject.SIGNAL_RUN_LAST,
|
||||
gobject.TYPE_NONE,
|
||||
(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)),
|
||||
}
|
||||
pids_to_task = {}
|
||||
tasks_to_iter = {}
|
||||
|
||||
def __init__ (self, sequential=False):
|
||||
gobject.GObject.__init__ (self)
|
||||
self.model = RunningBuildModel()
|
||||
self.sequential = sequential
|
||||
self.buildaborted = False
|
||||
|
||||
def reset (self):
|
||||
self.pids_to_task.clear()
|
||||
self.tasks_to_iter.clear()
|
||||
self.model.clear()
|
||||
|
||||
def handle_event (self, event, pbar=None):
|
||||
# Handle an event from the event queue, this may result in updating
|
||||
# the model and thus the UI. Or it may be to tell us that the build
|
||||
# has finished successfully (or not, as the case may be.)
|
||||
|
||||
parent = None
|
||||
pid = 0
|
||||
package = None
|
||||
task = None
|
||||
|
||||
# If we have a pid attached to this message/event try and get the
|
||||
# (package, task) pair for it. If we get that then get the parent iter
|
||||
# for the message.
|
||||
if hasattr(event, 'pid'):
|
||||
pid = event.pid
|
||||
if hasattr(event, 'process'):
|
||||
pid = event.process
|
||||
|
||||
if pid and pid in self.pids_to_task:
|
||||
(package, task) = self.pids_to_task[pid]
|
||||
parent = self.tasks_to_iter[(package, task)]
|
||||
|
||||
if(isinstance(event, logging.LogRecord)):
|
||||
if event.taskpid == 0 or event.levelno > logging.INFO:
|
||||
self.emit("log", "handle", event)
|
||||
# FIXME: this is a hack! More info in Yocto #1433
|
||||
# http://bugzilla.pokylinux.org/show_bug.cgi?id=1433, temporarily
|
||||
# mask the error message as it's not informative for the user.
|
||||
if event.msg.startswith("Execution of event handler 'run_buildstats' failed"):
|
||||
return
|
||||
|
||||
if (event.levelno < logging.INFO or
|
||||
event.msg.startswith("Running task")):
|
||||
return # don't add these to the list
|
||||
|
||||
if event.levelno >= logging.ERROR:
|
||||
icon = "dialog-error"
|
||||
color = HobColors.ERROR
|
||||
self.emit("log-error")
|
||||
elif event.levelno >= logging.WARNING:
|
||||
icon = "dialog-warning"
|
||||
color = HobColors.WARNING
|
||||
self.emit("log-warning")
|
||||
else:
|
||||
icon = None
|
||||
color = HobColors.OK
|
||||
|
||||
# if we know which package we belong to, we'll append onto its list.
|
||||
# otherwise, we'll jump to the top of the master list
|
||||
if self.sequential or not parent:
|
||||
tree_add = self.model.append
|
||||
else:
|
||||
tree_add = self.model.prepend
|
||||
tree_add(parent,
|
||||
(None,
|
||||
package,
|
||||
task,
|
||||
event.getMessage(),
|
||||
icon,
|
||||
color,
|
||||
0))
|
||||
|
||||
# if there are warnings while processing a package
|
||||
# (parent), mark the task with warning color;
|
||||
# in case there are errors, the updates will be
|
||||
# handled on TaskFailed.
|
||||
if color == HobColors.WARNING and parent:
|
||||
self.model.set(parent, self.model.COL_COLOR, color)
|
||||
if task: #then we have a parent (package), and update it's color
|
||||
self.model.set(self.tasks_to_iter[(package, None)], self.model.COL_COLOR, color)
|
||||
|
||||
elif isinstance(event, bb.build.TaskStarted):
|
||||
(package, task) = (event._package, event._task)
|
||||
|
||||
# Save out this PID.
|
||||
self.pids_to_task[pid] = (package, task)
|
||||
|
||||
# Check if we already have this package in our model. If so then
|
||||
# that can be the parent for the task. Otherwise we create a new
|
||||
# top level for the package.
|
||||
if ((package, None) in self.tasks_to_iter):
|
||||
parent = self.tasks_to_iter[(package, None)]
|
||||
else:
|
||||
if self.sequential:
|
||||
add = self.model.append
|
||||
else:
|
||||
add = self.model.prepend
|
||||
parent = add(None, (None,
|
||||
package,
|
||||
None,
|
||||
"Package: %s" % (package),
|
||||
None,
|
||||
HobColors.OK,
|
||||
0))
|
||||
self.tasks_to_iter[(package, None)] = parent
|
||||
|
||||
# Because this parent package now has an active child mark it as
|
||||
# such.
|
||||
self.model.set(parent, self.model.COL_ICON, "gtk-execute")
|
||||
parent_color = self.model.get(parent, self.model.COL_COLOR)[0]
|
||||
if parent_color != HobColors.ERROR and parent_color != HobColors.WARNING:
|
||||
self.model.set(parent, self.model.COL_COLOR, HobColors.RUNNING)
|
||||
|
||||
# Add an entry in the model for this task
|
||||
i = self.model.append (parent, (None,
|
||||
package,
|
||||
task,
|
||||
"Task: %s" % (task),
|
||||
"gtk-execute",
|
||||
HobColors.RUNNING,
|
||||
0))
|
||||
|
||||
# update the parent's active task count
|
||||
num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] + 1
|
||||
self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
|
||||
|
||||
# Save out the iter so that we can find it when we have a message
|
||||
# that we need to attach to a task.
|
||||
self.tasks_to_iter[(package, task)] = i
|
||||
|
||||
elif isinstance(event, bb.build.TaskBase):
|
||||
self.emit("log", "info", event._message)
|
||||
current = self.tasks_to_iter[(package, task)]
|
||||
parent = self.tasks_to_iter[(package, None)]
|
||||
|
||||
# remove this task from the parent's active count
|
||||
num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] - 1
|
||||
self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
|
||||
|
||||
if isinstance(event, bb.build.TaskFailed):
|
||||
# Mark the task and parent as failed
|
||||
icon = "dialog-error"
|
||||
color = HobColors.ERROR
|
||||
|
||||
logfile = event.logfile
|
||||
if logfile and os.path.exists(logfile):
|
||||
with open(logfile) as f:
|
||||
logdata = f.read()
|
||||
self.model.append(current, ('pastebin', None, None, logdata, 'gtk-error', HobColors.OK, 0))
|
||||
|
||||
for i in (current, parent):
|
||||
self.model.set(i, self.model.COL_ICON, icon,
|
||||
self.model.COL_COLOR, color)
|
||||
else:
|
||||
# Mark the parent package and the task as inactive,
|
||||
# but make sure to preserve error, warnings and active
|
||||
# states
|
||||
parent_color = self.model.get(parent, self.model.COL_COLOR)[0]
|
||||
task_color = self.model.get(current, self.model.COL_COLOR)[0]
|
||||
|
||||
# Mark the task as inactive
|
||||
self.model.set(current, self.model.COL_ICON, None)
|
||||
if task_color != HobColors.ERROR:
|
||||
if task_color == HobColors.WARNING:
|
||||
self.model.set(current, self.model.COL_ICON, 'dialog-warning')
|
||||
else:
|
||||
self.model.set(current, self.model.COL_COLOR, HobColors.OK)
|
||||
|
||||
# Mark the parent as inactive
|
||||
if parent_color != HobColors.ERROR:
|
||||
if parent_color == HobColors.WARNING:
|
||||
self.model.set(parent, self.model.COL_ICON, "dialog-warning")
|
||||
else:
|
||||
self.model.set(parent, self.model.COL_ICON, None)
|
||||
if num_active == 0:
|
||||
self.model.set(parent, self.model.COL_COLOR, HobColors.OK)
|
||||
|
||||
# Clear the iters and the pids since when the task goes away the
|
||||
# pid will no longer be used for messages
|
||||
del self.tasks_to_iter[(package, task)]
|
||||
del self.pids_to_task[pid]
|
||||
|
||||
elif isinstance(event, bb.event.BuildStarted):
|
||||
|
||||
self.emit("build-started")
|
||||
self.model.prepend(None, (None,
|
||||
None,
|
||||
None,
|
||||
"Build Started (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
|
||||
None,
|
||||
HobColors.OK,
|
||||
0))
|
||||
if pbar:
|
||||
pbar.update(0, self.progress_total)
|
||||
pbar.set_title(bb.event.getName(event))
|
||||
|
||||
elif isinstance(event, bb.event.BuildCompleted):
|
||||
failures = int (event._failures)
|
||||
self.model.prepend(None, (None,
|
||||
None,
|
||||
None,
|
||||
"Build Completed (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
|
||||
None,
|
||||
HobColors.OK,
|
||||
0))
|
||||
|
||||
# Emit the appropriate signal depending on the number of failures
|
||||
if self.buildaborted:
|
||||
self.emit ("build-aborted")
|
||||
self.buildaborted = False
|
||||
elif (failures >= 1):
|
||||
self.emit ("build-failed")
|
||||
else:
|
||||
self.emit ("build-succeeded")
|
||||
# Emit a generic "build-complete" signal for things wishing to
|
||||
# handle when the build is finished
|
||||
self.emit("build-complete")
|
||||
# reset the all cell's icon indicator
|
||||
self.model.close_task_refresh()
|
||||
if pbar:
|
||||
pbar.set_text(event.msg)
|
||||
|
||||
elif isinstance(event, bb.event.DiskFull):
|
||||
self.buildaborted = True
|
||||
self.emit("disk-full")
|
||||
|
||||
elif isinstance(event, bb.command.CommandFailed):
|
||||
self.emit("log", "error", "Command execution failed: %s" % (event.error))
|
||||
if event.error.startswith("Exited with"):
|
||||
# If the command fails with an exit code we're done, emit the
|
||||
# generic signal for the UI to notify the user
|
||||
self.emit("build-complete")
|
||||
# reset the all cell's icon indicator
|
||||
self.model.close_task_refresh()
|
||||
|
||||
elif isinstance(event, bb.event.CacheLoadStarted) and pbar:
|
||||
pbar.set_title("Loading cache")
|
||||
self.progress_total = event.total
|
||||
pbar.update(0, self.progress_total)
|
||||
elif isinstance(event, bb.event.CacheLoadProgress) and pbar:
|
||||
pbar.update(event.current, self.progress_total)
|
||||
elif isinstance(event, bb.event.CacheLoadCompleted) and pbar:
|
||||
pbar.update(self.progress_total, self.progress_total)
|
||||
pbar.hide()
|
||||
elif isinstance(event, bb.event.ParseStarted) and pbar:
|
||||
if event.total == 0:
|
||||
return
|
||||
pbar.set_title("Processing recipes")
|
||||
self.progress_total = event.total
|
||||
pbar.update(0, self.progress_total)
|
||||
elif isinstance(event, bb.event.ParseProgress) and pbar:
|
||||
pbar.update(event.current, self.progress_total)
|
||||
elif isinstance(event, bb.event.ParseCompleted) and pbar:
|
||||
pbar.hide()
|
||||
#using runqueue events as many as possible to update the progress bar
|
||||
elif isinstance(event, bb.runqueue.runQueueTaskFailed):
|
||||
self.emit("log", "error", "Task %s (%s) failed with exit code '%s'" % (event.taskid, event.taskstring, event.exitcode))
|
||||
elif isinstance(event, bb.runqueue.sceneQueueTaskFailed):
|
||||
self.emit("log", "warn", "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead" \
|
||||
% (event.taskid, event.taskstring, event.exitcode))
|
||||
elif isinstance(event, (bb.runqueue.runQueueTaskStarted, bb.runqueue.sceneQueueTaskStarted)):
|
||||
if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
|
||||
self.emit("log", "info", "Running setscene task %d of %d (%s)" % \
|
||||
(event.stats.completed + event.stats.active + event.stats.failed + 1,
|
||||
event.stats.total, event.taskstring))
|
||||
else:
|
||||
if event.noexec:
|
||||
tasktype = 'noexec task'
|
||||
else:
|
||||
tasktype = 'task'
|
||||
self.emit("log", "info", "Running %s %s of %s (ID: %s, %s)" % \
|
||||
(tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1,
|
||||
event.stats.total, event.taskid, event.taskstring))
|
||||
message = {}
|
||||
message["eventname"] = bb.event.getName(event)
|
||||
num_of_completed = event.stats.completed + event.stats.failed
|
||||
message["current"] = num_of_completed
|
||||
message["total"] = event.stats.total
|
||||
message["title"] = ""
|
||||
message["task"] = event.taskstring
|
||||
self.emit("task-started", message)
|
||||
elif isinstance(event, bb.event.MultipleProviders):
|
||||
self.emit("log", "info", "multiple providers are available for %s%s (%s)" \
|
||||
% (event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)))
|
||||
self.emit("log", "info", "consider defining a PREFERRED_PROVIDER entry to match %s" % (event._item))
|
||||
elif isinstance(event, bb.event.NoProvider):
|
||||
msg = ""
|
||||
if event._runtime:
|
||||
r = "R"
|
||||
else:
|
||||
r = ""
|
||||
|
||||
extra = ''
|
||||
if not event._reasons:
|
||||
if event._close_matches:
|
||||
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
|
||||
|
||||
if event._dependees:
|
||||
msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s\n" % (r, event._item, ", ".join(event._dependees), r, extra)
|
||||
else:
|
||||
msg = "Nothing %sPROVIDES '%s'%s\n" % (r, event._item, extra)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
msg += ("%s\n" % reason)
|
||||
self.emit("no-provider", msg)
|
||||
self.emit("log", "error", msg)
|
||||
elif isinstance(event, bb.event.LogExecTTY):
|
||||
icon = "dialog-warning"
|
||||
color = HobColors.WARNING
|
||||
if self.sequential or not parent:
|
||||
tree_add = self.model.append
|
||||
else:
|
||||
tree_add = self.model.prepend
|
||||
tree_add(parent,
|
||||
(None,
|
||||
package,
|
||||
task,
|
||||
event.msg,
|
||||
icon,
|
||||
color,
|
||||
0))
|
||||
else:
|
||||
if not isinstance(event, (bb.event.BuildBase,
|
||||
bb.event.StampUpdate,
|
||||
bb.event.ConfigParsed,
|
||||
bb.event.RecipeParsed,
|
||||
bb.event.RecipePreFinalise,
|
||||
bb.runqueue.runQueueEvent,
|
||||
bb.runqueue.runQueueExitWait,
|
||||
bb.event.OperationStarted,
|
||||
bb.event.OperationCompleted,
|
||||
bb.event.OperationProgress)):
|
||||
self.emit("log", "error", "Unknown event: %s" % (event.error if hasattr(event, 'error') else 'error'))
|
||||
|
||||
return
|
||||
|
||||
|
||||
def do_pastebin(text):
|
||||
url = 'http://pastebin.com/api_public.php'
|
||||
params = {'paste_code': text, 'paste_format': 'text'}
|
||||
|
||||
req = urllib2.Request(url, urllib.urlencode(params))
|
||||
response = urllib2.urlopen(req)
|
||||
paste_url = response.read()
|
||||
|
||||
return paste_url
|
||||
|
||||
|
||||
class RunningBuildTreeView (gtk.TreeView):
|
||||
__gsignals__ = {
|
||||
"button_press_event" : "override"
|
||||
}
|
||||
def __init__ (self, readonly=False, hob=False):
|
||||
gtk.TreeView.__init__ (self)
|
||||
self.readonly = readonly
|
||||
|
||||
# The icon that indicates whether we're building or failed.
|
||||
# add 'hob' flag because there has not only hob to share this code
|
||||
if hob:
|
||||
renderer = HobCellRendererPixbuf ()
|
||||
else:
|
||||
renderer = gtk.CellRendererPixbuf()
|
||||
col = gtk.TreeViewColumn ("Status", renderer)
|
||||
col.add_attribute (renderer, "icon-name", 4)
|
||||
self.append_column (col)
|
||||
|
||||
# The message of the build.
|
||||
# add 'hob' flag because there has not only hob to share this code
|
||||
if hob:
|
||||
self.message_renderer = HobWarpCellRendererText (col_number=1)
|
||||
else:
|
||||
self.message_renderer = gtk.CellRendererText ()
|
||||
self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=3)
|
||||
self.message_column.add_attribute(self.message_renderer, 'background', 5)
|
||||
self.message_renderer.set_property('editable', (not self.readonly))
|
||||
self.append_column (self.message_column)
|
||||
|
||||
def do_button_press_event(self, event):
|
||||
gtk.TreeView.do_button_press_event(self, event)
|
||||
|
||||
if event.button == 3:
|
||||
selection = super(RunningBuildTreeView, self).get_selection()
|
||||
(model, it) = selection.get_selected()
|
||||
if it is not None:
|
||||
can_paste = model.get(it, model.COL_LOG)[0]
|
||||
if can_paste == 'pastebin':
|
||||
# build a simple menu with a pastebin option
|
||||
menu = gtk.Menu()
|
||||
menuitem = gtk.MenuItem("Copy")
|
||||
menu.append(menuitem)
|
||||
menuitem.connect("activate", self.clipboard_handler, (model, it))
|
||||
menuitem.show()
|
||||
menuitem = gtk.MenuItem("Send log to pastebin")
|
||||
menu.append(menuitem)
|
||||
menuitem.connect("activate", self.pastebin_handler, (model, it))
|
||||
menuitem.show()
|
||||
menu.show()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
def _add_to_clipboard(self, clipping):
|
||||
"""
|
||||
Add the contents of clipping to the system clipboard.
|
||||
"""
|
||||
clipboard = gtk.clipboard_get()
|
||||
clipboard.set_text(clipping)
|
||||
clipboard.store()
|
||||
|
||||
def pastebin_handler(self, widget, data):
|
||||
"""
|
||||
Send the log data to pastebin, then add the new paste url to the
|
||||
clipboard.
|
||||
"""
|
||||
(model, it) = data
|
||||
paste_url = do_pastebin(model.get(it, model.COL_MESSAGE)[0])
|
||||
|
||||
# @todo Provide visual feedback to the user that it is done and that
|
||||
# it worked.
|
||||
print paste_url
|
||||
|
||||
self._add_to_clipboard(paste_url)
|
||||
|
||||
def clipboard_handler(self, widget, data):
|
||||
"""
|
||||
"""
|
||||
(model, it) = data
|
||||
message = model.get(it, model.COL_MESSAGE)[0]
|
||||
|
||||
self._add_to_clipboard(message)
|
||||
|
||||
class BuildFailureTreeView(gtk.TreeView):
|
||||
|
||||
def __init__ (self):
|
||||
gtk.TreeView.__init__(self)
|
||||
self.set_rules_hint(False)
|
||||
self.set_headers_visible(False)
|
||||
self.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
||||
|
||||
# The icon that indicates whether we're building or failed.
|
||||
renderer = HobCellRendererPixbuf ()
|
||||
col = gtk.TreeViewColumn ("Status", renderer)
|
||||
col.add_attribute (renderer, "icon-name", RunningBuildModel.COL_ICON)
|
||||
self.append_column (col)
|
||||
|
||||
# The message of the build.
|
||||
self.message_renderer = HobWarpCellRendererText (col_number=1)
|
||||
self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=RunningBuildModel.COL_MESSAGE, background=RunningBuildModel.COL_COLOR)
|
||||
self.append_column (self.message_column)
|
||||
34
bitbake/lib/bb/ui/crumbs/utils.py
Normal file
34
bitbake/lib/bb/ui/crumbs/utils.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# BitBake UI Utils
|
||||
#
|
||||
# Copyright (C) 2012 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# This utility method looks for xterm or vte and return the
|
||||
# frist to exist, currently we are keeping this simple, but
|
||||
# we will likely move the oe.terminal implementation into
|
||||
# bitbake which will allow more flexibility.
|
||||
|
||||
import os
|
||||
import bb
|
||||
|
||||
def which_terminal():
|
||||
term = bb.utils.which(os.environ["PATH"], "xterm")
|
||||
if term:
|
||||
return term + " -e "
|
||||
term = bb.utils.which(os.environ["PATH"], "vte")
|
||||
if term:
|
||||
return term + " -c "
|
||||
return None
|
||||
333
bitbake/lib/bb/ui/depexp.py
Normal file
333
bitbake/lib/bb/ui/depexp.py
Normal file
@@ -0,0 +1,333 @@
|
||||
#
|
||||
# BitBake Graphical GTK based Dependency Explorer
|
||||
#
|
||||
# Copyright (C) 2007 Ross Burton
|
||||
# Copyright (C) 2007 - 2008 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import sys
|
||||
import gobject
|
||||
import gtk
|
||||
import Queue
|
||||
import threading
|
||||
import xmlrpclib
|
||||
import bb
|
||||
import bb.event
|
||||
from bb.ui.crumbs.progressbar import HobProgressBar
|
||||
|
||||
# Package Model
|
||||
(COL_PKG_NAME) = (0)
|
||||
|
||||
# Dependency Model
|
||||
(TYPE_DEP, TYPE_RDEP) = (0, 1)
|
||||
(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2)
|
||||
|
||||
|
||||
class PackageDepView(gtk.TreeView):
|
||||
def __init__(self, model, dep_type, label):
|
||||
gtk.TreeView.__init__(self)
|
||||
self.current = None
|
||||
self.dep_type = dep_type
|
||||
self.filter_model = model.filter_new()
|
||||
self.filter_model.set_visible_func(self._filter)
|
||||
self.set_model(self.filter_model)
|
||||
#self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
|
||||
self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE))
|
||||
|
||||
def _filter(self, model, iter):
|
||||
(this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT)
|
||||
if this_type != self.dep_type: return False
|
||||
return package == self.current
|
||||
|
||||
def set_current_package(self, package):
|
||||
self.current = package
|
||||
self.filter_model.refilter()
|
||||
|
||||
|
||||
class PackageReverseDepView(gtk.TreeView):
|
||||
def __init__(self, model, label):
|
||||
gtk.TreeView.__init__(self)
|
||||
self.current = None
|
||||
self.filter_model = model.filter_new()
|
||||
self.filter_model.set_visible_func(self._filter)
|
||||
self.set_model(self.filter_model)
|
||||
self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT))
|
||||
|
||||
def _filter(self, model, iter):
|
||||
package = model.get_value(iter, COL_DEP_PACKAGE)
|
||||
return package == self.current
|
||||
|
||||
def set_current_package(self, package):
|
||||
self.current = package
|
||||
self.filter_model.refilter()
|
||||
|
||||
|
||||
class DepExplorer(gtk.Window):
|
||||
def __init__(self):
|
||||
gtk.Window.__init__(self)
|
||||
self.set_title("Dependency Explorer")
|
||||
self.set_default_size(500, 500)
|
||||
self.connect("delete-event", gtk.main_quit)
|
||||
|
||||
# Create the data models
|
||||
self.pkg_model = gtk.ListStore(gobject.TYPE_STRING)
|
||||
self.pkg_model.set_sort_column_id(COL_PKG_NAME, gtk.SORT_ASCENDING)
|
||||
self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING)
|
||||
self.depends_model.set_sort_column_id(COL_DEP_PACKAGE, gtk.SORT_ASCENDING)
|
||||
|
||||
pane = gtk.HPaned()
|
||||
pane.set_position(250)
|
||||
self.add(pane)
|
||||
|
||||
# The master list of packages
|
||||
scrolled = gtk.ScrolledWindow()
|
||||
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolled.set_shadow_type(gtk.SHADOW_IN)
|
||||
|
||||
self.pkg_treeview = gtk.TreeView(self.pkg_model)
|
||||
self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed)
|
||||
column = gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)
|
||||
self.pkg_treeview.append_column(column)
|
||||
pane.add1(scrolled)
|
||||
scrolled.add(self.pkg_treeview)
|
||||
|
||||
box = gtk.VBox(homogeneous=True, spacing=4)
|
||||
|
||||
# Runtime Depends
|
||||
scrolled = gtk.ScrolledWindow()
|
||||
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolled.set_shadow_type(gtk.SHADOW_IN)
|
||||
self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends")
|
||||
self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
|
||||
scrolled.add(self.rdep_treeview)
|
||||
box.add(scrolled)
|
||||
|
||||
# Build Depends
|
||||
scrolled = gtk.ScrolledWindow()
|
||||
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolled.set_shadow_type(gtk.SHADOW_IN)
|
||||
self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends")
|
||||
self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
|
||||
scrolled.add(self.dep_treeview)
|
||||
box.add(scrolled)
|
||||
pane.add2(box)
|
||||
|
||||
# Reverse Depends
|
||||
scrolled = gtk.ScrolledWindow()
|
||||
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolled.set_shadow_type(gtk.SHADOW_IN)
|
||||
self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends")
|
||||
self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT)
|
||||
scrolled.add(self.revdep_treeview)
|
||||
box.add(scrolled)
|
||||
pane.add2(box)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def on_package_activated(self, treeview, path, column, data_col):
|
||||
model = treeview.get_model()
|
||||
package = model.get_value(model.get_iter(path), data_col)
|
||||
|
||||
pkg_path = []
|
||||
def finder(model, path, iter, needle):
|
||||
package = model.get_value(iter, COL_PKG_NAME)
|
||||
if package == needle:
|
||||
pkg_path.append(path)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
self.pkg_model.foreach(finder, package)
|
||||
if pkg_path:
|
||||
self.pkg_treeview.get_selection().select_path(pkg_path[0])
|
||||
self.pkg_treeview.scroll_to_cell(pkg_path[0])
|
||||
|
||||
def on_cursor_changed(self, selection):
|
||||
(model, it) = selection.get_selected()
|
||||
if it is None:
|
||||
current_package = None
|
||||
else:
|
||||
current_package = model.get_value(it, COL_PKG_NAME)
|
||||
self.rdep_treeview.set_current_package(current_package)
|
||||
self.dep_treeview.set_current_package(current_package)
|
||||
self.revdep_treeview.set_current_package(current_package)
|
||||
|
||||
|
||||
def parse(self, depgraph):
|
||||
for package in depgraph["pn"]:
|
||||
self.pkg_model.insert(0, (package,))
|
||||
|
||||
for package in depgraph["depends"]:
|
||||
for depend in depgraph["depends"][package]:
|
||||
self.depends_model.insert (0, (TYPE_DEP, package, depend))
|
||||
|
||||
for package in depgraph["rdepends-pn"]:
|
||||
for rdepend in depgraph["rdepends-pn"][package]:
|
||||
self.depends_model.insert (0, (TYPE_RDEP, package, rdepend))
|
||||
|
||||
|
||||
class gtkthread(threading.Thread):
|
||||
quit = threading.Event()
|
||||
def __init__(self, shutdown):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.shutdown = shutdown
|
||||
|
||||
def run(self):
|
||||
gobject.threads_init()
|
||||
gtk.gdk.threads_init()
|
||||
gtk.main()
|
||||
gtkthread.quit.set()
|
||||
|
||||
|
||||
def main(server, eventHandler, params):
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
print(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
if not cmdline or cmdline[0] != "generateDotGraph":
|
||||
print("This UI requires the -g option")
|
||||
return 1
|
||||
ret, error = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
return 1
|
||||
elif ret != True:
|
||||
print("Error running command '%s': returned %s" % (cmdline, ret))
|
||||
return 1
|
||||
except xmlrpclib.Fault as x:
|
||||
print("XMLRPC Fault getting commandline:\n %s" % x)
|
||||
return
|
||||
|
||||
try:
|
||||
gtk.init_check()
|
||||
except RuntimeError:
|
||||
sys.stderr.write("Please set DISPLAY variable before running this command \n")
|
||||
return
|
||||
|
||||
shutdown = 0
|
||||
|
||||
gtkgui = gtkthread(shutdown)
|
||||
gtkgui.start()
|
||||
|
||||
gtk.gdk.threads_enter()
|
||||
dep = DepExplorer()
|
||||
bardialog = gtk.Dialog(parent=dep,
|
||||
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
bardialog.set_default_size(400, 50)
|
||||
pbar = HobProgressBar()
|
||||
bardialog.vbox.pack_start(pbar)
|
||||
bardialog.show_all()
|
||||
bardialog.connect("delete-event", gtk.main_quit)
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
progress_total = 0
|
||||
while True:
|
||||
try:
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
if gtkthread.quit.isSet():
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
if error:
|
||||
print('Unable to cleanly stop: %s' % error)
|
||||
break
|
||||
|
||||
if event is None:
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.CacheLoadStarted):
|
||||
progress_total = event.total
|
||||
gtk.gdk.threads_enter()
|
||||
bardialog.set_title("Loading Cache")
|
||||
pbar.update(0)
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
if isinstance(event, bb.event.CacheLoadProgress):
|
||||
x = event.current
|
||||
gtk.gdk.threads_enter()
|
||||
pbar.update(x * 1.0 / progress_total)
|
||||
pbar.set_title('')
|
||||
gtk.gdk.threads_leave()
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.CacheLoadCompleted):
|
||||
bardialog.hide()
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.ParseStarted):
|
||||
progress_total = event.total
|
||||
if progress_total == 0:
|
||||
continue
|
||||
gtk.gdk.threads_enter()
|
||||
pbar.update(0)
|
||||
bardialog.set_title("Processing recipes")
|
||||
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
if isinstance(event, bb.event.ParseProgress):
|
||||
x = event.current
|
||||
gtk.gdk.threads_enter()
|
||||
pbar.update(x * 1.0 / progress_total)
|
||||
pbar.set_title('')
|
||||
gtk.gdk.threads_leave()
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.ParseCompleted):
|
||||
bardialog.hide()
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.DepTreeGenerated):
|
||||
gtk.gdk.threads_enter()
|
||||
dep.parse(event._depgraph)
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
if isinstance(event, bb.command.CommandCompleted):
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.command.CommandFailed):
|
||||
print("Command execution failed: %s" % event.error)
|
||||
return event.exitcode
|
||||
|
||||
if isinstance(event, bb.command.CommandExit):
|
||||
return event.exitcode
|
||||
|
||||
if isinstance(event, bb.cooker.CookerExit):
|
||||
break
|
||||
|
||||
continue
|
||||
except EnvironmentError as ioerror:
|
||||
# ignore interrupted io
|
||||
if ioerror.args[0] == 4:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
if shutdown == 2:
|
||||
print("\nThird Keyboard Interrupt, exit.\n")
|
||||
break
|
||||
if shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
if error:
|
||||
print('Unable to cleanly stop: %s' % error)
|
||||
if shutdown == 0:
|
||||
print("\nKeyboard Interrupt, closing down...\n")
|
||||
_, error = server.runCommand(["stateShutdown"])
|
||||
if error:
|
||||
print('Unable to cleanly shutdown: %s' % error)
|
||||
shutdown = shutdown + 1
|
||||
pass
|
||||
121
bitbake/lib/bb/ui/goggle.py
Normal file
121
bitbake/lib/bb/ui/goggle.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2008 Intel Corporation
|
||||
#
|
||||
# Authored by Rob Bradford <rob@linux.intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
import xmlrpclib
|
||||
from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
|
||||
from bb.ui.crumbs.progress import ProgressBar
|
||||
|
||||
import Queue
|
||||
|
||||
|
||||
def event_handle_idle_func (eventHandler, build, pbar):
|
||||
|
||||
# Consume as many messages as we can in the time available to us
|
||||
event = eventHandler.getEvent()
|
||||
while event:
|
||||
build.handle_event (event, pbar)
|
||||
event = eventHandler.getEvent()
|
||||
|
||||
return True
|
||||
|
||||
def scroll_tv_cb (model, path, iter, view):
|
||||
view.scroll_to_cell (path)
|
||||
|
||||
|
||||
# @todo hook these into the GUI so the user has feedback...
|
||||
def running_build_failed_cb (running_build):
|
||||
pass
|
||||
|
||||
|
||||
def running_build_succeeded_cb (running_build):
|
||||
pass
|
||||
|
||||
|
||||
class MainWindow (gtk.Window):
|
||||
def __init__ (self):
|
||||
gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
|
||||
|
||||
# Setup tree view and the scrolled window
|
||||
scrolled_window = gtk.ScrolledWindow ()
|
||||
self.add (scrolled_window)
|
||||
self.cur_build_tv = RunningBuildTreeView()
|
||||
self.connect("delete-event", gtk.main_quit)
|
||||
self.set_default_size(640, 480)
|
||||
scrolled_window.add (self.cur_build_tv)
|
||||
|
||||
|
||||
def main (server, eventHandler, params):
|
||||
gobject.threads_init()
|
||||
gtk.gdk.threads_init()
|
||||
|
||||
window = MainWindow ()
|
||||
window.show_all ()
|
||||
pbar = ProgressBar(window)
|
||||
pbar.connect("delete-event", gtk.main_quit)
|
||||
|
||||
# Create the object for the current build
|
||||
running_build = RunningBuild ()
|
||||
window.cur_build_tv.set_model (running_build.model)
|
||||
running_build.model.connect("row-inserted", scroll_tv_cb, window.cur_build_tv)
|
||||
running_build.connect ("build-succeeded", running_build_succeeded_cb)
|
||||
running_build.connect ("build-failed", running_build_failed_cb)
|
||||
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
return 1
|
||||
elif ret != True:
|
||||
print("Error running command '%s': returned %s" % (cmdline, ret))
|
||||
return 1
|
||||
except xmlrpclib.Fault as x:
|
||||
print("XMLRPC Fault getting commandline:\n %s" % x)
|
||||
return 1
|
||||
|
||||
# Use a timeout function for probing the event queue to find out if we
|
||||
# have a message waiting for us.
|
||||
gobject.timeout_add (100,
|
||||
event_handle_idle_func,
|
||||
eventHandler,
|
||||
running_build,
|
||||
pbar)
|
||||
|
||||
try:
|
||||
gtk.main()
|
||||
except EnvironmentError as ioerror:
|
||||
# ignore interrupted io
|
||||
if ioerror.args[0] == 4:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
server.runCommand(["stateForceShutdown"])
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user