Compare commits
645 Commits
1.5_M5.rc2
...
dylan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcfdc4d54 | ||
|
|
a04380fd0f | ||
|
|
6e08651e39 | ||
|
|
a92ee6a03f | ||
|
|
057a68d8ff | ||
|
|
b690589385 | ||
|
|
c0456385ce | ||
|
|
4b302b8fa0 | ||
|
|
7a9908432f | ||
|
|
b91889fb3e | ||
|
|
35ed0a06be | ||
|
|
c78877b07e | ||
|
|
02037d7daa | ||
|
|
804f6f71c5 | ||
|
|
bbdfb0ff1a | ||
|
|
6714a6c59a | ||
|
|
706dcccb5a | ||
|
|
033f5a5fa5 | ||
|
|
efdb05cf31 | ||
|
|
e5ac7abc95 | ||
|
|
d3bc30f75b | ||
|
|
889f731acc | ||
|
|
f3df1c7e77 | ||
|
|
32ed21cc09 | ||
|
|
42a5b0cbd1 | ||
|
|
5b12a3441b | ||
|
|
48d151851e | ||
|
|
bfbfbc057b | ||
|
|
13d42ba43e | ||
|
|
ddb3132224 | ||
|
|
be7a782dca | ||
|
|
f398d09d71 | ||
|
|
e55ac718a5 | ||
|
|
2f020ec5df | ||
|
|
99ab4edab0 | ||
|
|
b27dba2efd | ||
|
|
54c4e10a41 | ||
|
|
15063788eb | ||
|
|
1d4bae38d9 | ||
|
|
6ef0833186 | ||
|
|
fda1f07ddd | ||
|
|
465a860519 | ||
|
|
c397b69e68 | ||
|
|
02204c9711 | ||
|
|
e6db878d21 | ||
|
|
8cc2c2566b | ||
|
|
42663173ff | ||
|
|
b95c05db58 | ||
|
|
5152260a32 | ||
|
|
2a07edd04c | ||
|
|
97300d711d | ||
|
|
7ab82abcad | ||
|
|
7bd335fef6 | ||
|
|
447214ab95 | ||
|
|
9fb578a648 | ||
|
|
be50a58edd | ||
|
|
0c987df4a2 | ||
|
|
e2bb4a70a9 | ||
|
|
a7ec08e2b5 | ||
|
|
90547d53a1 | ||
|
|
59e8d60e25 | ||
|
|
2555e58e56 | ||
|
|
50ee75aaa2 | ||
|
|
7a5f2ae591 | ||
|
|
88b65f4d7a | ||
|
|
6dad99b1ab | ||
|
|
6a2b596725 | ||
|
|
31498bcf19 | ||
|
|
ac5c4dcf46 | ||
|
|
9cf2e005d1 | ||
|
|
da8a5ec920 | ||
|
|
fdbd1d8c86 | ||
|
|
eb3598d248 | ||
|
|
57e6d537e8 | ||
|
|
c4c6203ef5 | ||
|
|
53e65dc1dd | ||
|
|
d089194aa6 | ||
|
|
100a5ba8cb | ||
|
|
3ec2ad0d76 | ||
|
|
f665cee7a6 | ||
|
|
6e71a5f24b | ||
|
|
7ed98ddf7f | ||
|
|
88fe0cba5b | ||
|
|
1d31f44f8c | ||
|
|
90f366ec75 | ||
|
|
483102fb98 | ||
|
|
02b61bf09d | ||
|
|
8a70e3d87c | ||
|
|
2e5772020f | ||
|
|
6a1bec4f6e | ||
|
|
26a64656d0 | ||
|
|
6c39db8caa | ||
|
|
dbaa6f0e50 | ||
|
|
233c6f2c96 | ||
|
|
90a862ce42 | ||
|
|
3ef7b65897 | ||
|
|
ab3f627aaf | ||
|
|
9a56d0701a | ||
|
|
c168a351a6 | ||
|
|
7800f43d7e | ||
|
|
30548aae0a | ||
|
|
4e399f08d5 | ||
|
|
f9c6562774 | ||
|
|
5a1273af1e | ||
|
|
1563c61592 | ||
|
|
fcd93c0b41 | ||
|
|
7f1a354944 | ||
|
|
8bc5708945 | ||
|
|
8eacb41c1c | ||
|
|
a36c28943d | ||
|
|
1633edffc7 | ||
|
|
f5bfb120dd | ||
|
|
9f902cfea2 | ||
|
|
905642985d | ||
|
|
560fa9ad8d | ||
|
|
d734ab491a | ||
|
|
fc0b405e40 | ||
|
|
f035e027a9 | ||
|
|
925a200265 | ||
|
|
8176e457ba | ||
|
|
5043bc52ca | ||
|
|
2bf3117510 | ||
|
|
cf06c94a8a | ||
|
|
1ed8f9ef39 | ||
|
|
f83bc4b55c | ||
|
|
946e5f0bd1 | ||
|
|
4327c43470 | ||
|
|
33d6f0dfe2 | ||
|
|
477dbb3839 | ||
|
|
a02b660add | ||
|
|
042ca04cc6 | ||
|
|
d5ea232322 | ||
|
|
a86ddf11d1 | ||
|
|
030abb6024 | ||
|
|
721fbb5bf4 | ||
|
|
010390074c | ||
|
|
2de87a3c55 | ||
|
|
7f2c0fb94b | ||
|
|
3e8934b74c | ||
|
|
678adec516 | ||
|
|
89dafe9ea5 | ||
|
|
3070d43009 | ||
|
|
4ce2b1c95b | ||
|
|
c0151b1a45 | ||
|
|
08074dc486 | ||
|
|
7a3b4c5541 | ||
|
|
c238eb278c | ||
|
|
184bb4a00f | ||
|
|
c3b1cbb04e | ||
|
|
ecfe97741f | ||
|
|
c46300e08c | ||
|
|
053eca17db | ||
|
|
cf38dc717d | ||
|
|
9c70e442c7 | ||
|
|
ce2c85de29 | ||
|
|
e8a5266f30 | ||
|
|
824deabdab | ||
|
|
1f2aacf0d2 | ||
|
|
d45f9257a5 | ||
|
|
f3f21bafd2 | ||
|
|
5927edf4ea | ||
|
|
bc499fdd30 | ||
|
|
baffd936a5 | ||
|
|
bc5e808f01 | ||
|
|
7a1eba6304 | ||
|
|
9faeea2cfc | ||
|
|
ff4e1cd021 | ||
|
|
b4ba326910 | ||
|
|
2bd2fa985b | ||
|
|
43585009e8 | ||
|
|
9aa544f742 | ||
|
|
899e5cca99 | ||
|
|
eb1f9284ea | ||
|
|
46dcd80b36 | ||
|
|
be55c8e97c | ||
|
|
9612e1c5cc | ||
|
|
4751b5ba75 | ||
|
|
54f6c82829 | ||
|
|
4d47a1e644 | ||
|
|
db793180e0 | ||
|
|
10b63753f8 | ||
|
|
a221d7f2be | ||
|
|
fb4ccb075f | ||
|
|
dea3e69649 | ||
|
|
78d1a23bf3 | ||
|
|
1b86653740 | ||
|
|
d6d12689c0 | ||
|
|
eb5ec8899d | ||
|
|
64273e53f2 | ||
|
|
2d79d04885 | ||
|
|
0d5380fd3f | ||
|
|
e0e3471aa6 | ||
|
|
e1aa84839e | ||
|
|
d7f8415cb2 | ||
|
|
d9efe9c485 | ||
|
|
9d3ffeb5ae | ||
|
|
b0c40a9c1b | ||
|
|
f0b927882c | ||
|
|
d207bebb74 | ||
|
|
73cdce8936 | ||
|
|
271808b682 | ||
|
|
109d9980fe | ||
|
|
896b95ba75 | ||
|
|
9cdb9ef7bd | ||
|
|
b5fccff7ab | ||
|
|
8d9bdbb0d0 | ||
|
|
7da161aae6 | ||
|
|
7d7ed2e2b6 | ||
|
|
4c6f8db5ab | ||
|
|
6dc7ac5431 | ||
|
|
2ef0e6a35f | ||
|
|
3851ae0524 | ||
|
|
88c4df03a7 | ||
|
|
d00bac952f | ||
|
|
a27d06dbc4 | ||
|
|
2b9f62bf43 | ||
|
|
63319a2091 | ||
|
|
bb70038b85 | ||
|
|
88b12615a5 | ||
|
|
bb43931f3d | ||
|
|
b8de21c074 | ||
|
|
c1ea152c08 | ||
|
|
616cda690e | ||
|
|
9e1dca4134 | ||
|
|
230830e09e | ||
|
|
bc651f4b3a | ||
|
|
f00b5ea98a | ||
|
|
413a35d89f | ||
|
|
d3c465dc1e | ||
|
|
722c71119f | ||
|
|
2532e651f5 | ||
|
|
0ba66d7e53 | ||
|
|
eb50d973d0 | ||
|
|
59ab44fb3e | ||
|
|
e4a0a4c5e4 | ||
|
|
0fbbbd886e | ||
|
|
fb804f739e | ||
|
|
bb8e0a4d8b | ||
|
|
89d55eeb13 | ||
|
|
b2fddcb3b8 | ||
|
|
73f103bf9b | ||
|
|
0a697caca4 | ||
|
|
52c00a5e93 | ||
|
|
a21363af89 | ||
|
|
09d69c9d2a | ||
|
|
aaa011d60f | ||
|
|
fdeec07994 | ||
|
|
33fa8e2a78 | ||
|
|
3d79617742 | ||
|
|
7e78b41667 | ||
|
|
46253f7418 | ||
|
|
b0a5694e3e | ||
|
|
beda5013e4 | ||
|
|
4fe205a181 | ||
|
|
419ce91798 | ||
|
|
37e7d50bc3 | ||
|
|
0cf150a398 | ||
|
|
9643fbc28a | ||
|
|
6b9db4e482 | ||
|
|
96e832484b | ||
|
|
19d076df45 | ||
|
|
1e6b8988cd | ||
|
|
a1f3049a94 | ||
|
|
7945add420 | ||
|
|
337e25aac5 | ||
|
|
fc3e445a50 | ||
|
|
a9a18a57e2 | ||
|
|
33b9e4ff79 | ||
|
|
fe3f385b45 | ||
|
|
b679d37ca4 | ||
|
|
0836bc221e | ||
|
|
594ab9d3a8 | ||
|
|
3954bf45ff | ||
|
|
66b99274ef | ||
|
|
d78df944e7 | ||
|
|
ecdd2ec8f4 | ||
|
|
97521e95ae | ||
|
|
2dcd1f0604 | ||
|
|
a0f4706d23 | ||
|
|
ef038a793a | ||
|
|
b15bdd8420 | ||
|
|
79771a0ceb | ||
|
|
77130e78cc | ||
|
|
028fa09165 | ||
|
|
fe3a04f15d | ||
|
|
3faa5039c8 | ||
|
|
8721671e83 | ||
|
|
437d0c2122 | ||
|
|
8824d9ef7a | ||
|
|
3c138bc962 | ||
|
|
2e14de12b5 | ||
|
|
aa2a3f07e9 | ||
|
|
19bd981641 | ||
|
|
4bfbccf373 | ||
|
|
cf801ae2b8 | ||
|
|
397589cd08 | ||
|
|
d7cafcfbb2 | ||
|
|
afb5fbf012 | ||
|
|
ed6aeb4861 | ||
|
|
950f2e453a | ||
|
|
202658db64 | ||
|
|
259d0665ed | ||
|
|
1a7201545b | ||
|
|
28be9becfb | ||
|
|
a21415db4e | ||
|
|
0f9afc21e1 | ||
|
|
8b357d843a | ||
|
|
fae83fa753 | ||
|
|
3a58336bec | ||
|
|
910f07be6d | ||
|
|
6ad0460ca4 | ||
|
|
c1fc2ce554 | ||
|
|
902564c044 | ||
|
|
ba617d6b25 | ||
|
|
15edb1fb5e | ||
|
|
860db52937 | ||
|
|
a1f1c7fb1d | ||
|
|
6410b87876 | ||
|
|
1fee9dea86 | ||
|
|
18c3a2c016 | ||
|
|
535723da00 | ||
|
|
e8720cc0be | ||
|
|
d6e4ac6f5f | ||
|
|
3fecd58370 | ||
|
|
690b8a1811 | ||
|
|
5006fcfb8f | ||
|
|
4f47f899dd | ||
|
|
006ed65f51 | ||
|
|
c0c46125b6 | ||
|
|
2d31fa1a7f | ||
|
|
c3cb650418 | ||
|
|
0977a6868e | ||
|
|
789b2b7e0c | ||
|
|
dc529e1f18 | ||
|
|
7bf9d217ff | ||
|
|
acb5c00bad | ||
|
|
a9e6e56b37 | ||
|
|
5efe0fbad3 | ||
|
|
870d8a817d | ||
|
|
62477881e8 | ||
|
|
cbccd7974d | ||
|
|
701c3ce3c6 | ||
|
|
050c61732a | ||
|
|
7f2898c167 | ||
|
|
d3a004e581 | ||
|
|
87eb702437 | ||
|
|
a8ddc5a9c2 | ||
|
|
12c0a1810c | ||
|
|
fd09fcc6e2 | ||
|
|
f781dec7d1 | ||
|
|
1fe847cfa7 | ||
|
|
c91877ea53 | ||
|
|
3ed6e9c5a1 | ||
|
|
65b9c025eb | ||
|
|
ee82461e09 | ||
|
|
5eda9ea286 | ||
|
|
90fa872873 | ||
|
|
ca4ce80ed8 | ||
|
|
ace492a436 | ||
|
|
c519ceee6e | ||
|
|
0444420dd6 | ||
|
|
02b236c640 | ||
|
|
897d26a444 | ||
|
|
43159d7876 | ||
|
|
137ba42a95 | ||
|
|
6fd9274046 | ||
|
|
e3851d8832 | ||
|
|
13bcd84923 | ||
|
|
55323f53c0 | ||
|
|
47fb722475 | ||
|
|
6bb52a0dff | ||
|
|
e7be7a74b7 | ||
|
|
d9e4c1bbf0 | ||
|
|
da18336928 | ||
|
|
8d37bb99c9 | ||
|
|
f1f6024647 | ||
|
|
45c3e9a781 | ||
|
|
3bd8002fef | ||
|
|
d6b0170e62 | ||
|
|
918f22046f | ||
|
|
c420b88685 | ||
|
|
7f94a247c7 | ||
|
|
e4ea9e5825 | ||
|
|
d0552decd5 | ||
|
|
ceaf900ded | ||
|
|
a8305a5dd0 | ||
|
|
b640f954e9 | ||
|
|
87c117e640 | ||
|
|
5f7cb2bf21 | ||
|
|
9f8323530a | ||
|
|
218b3de495 | ||
|
|
57035d6fbb | ||
|
|
18fdade54f | ||
|
|
f36781eb13 | ||
|
|
1b4469d389 | ||
|
|
811a299a20 | ||
|
|
a46eebde48 | ||
|
|
dc65b0e26f | ||
|
|
709ea3e270 | ||
|
|
d8eb848db8 | ||
|
|
cdad14969c | ||
|
|
5a9caec004 | ||
|
|
5d39560acf | ||
|
|
1e70f78782 | ||
|
|
2dcd89db06 | ||
|
|
2ee0ce78ca | ||
|
|
ed4f3800b7 | ||
|
|
36bdc9b89e | ||
|
|
4f057b7553 | ||
|
|
6ba0c34730 | ||
|
|
329a621075 | ||
|
|
2118d02b86 | ||
|
|
2e5314d3a0 | ||
|
|
e75bba86e7 | ||
|
|
2b3c76f023 | ||
|
|
2f2bdaf5b4 | ||
|
|
ada742c6c2 | ||
|
|
61e8186ecb | ||
|
|
3921b523c6 | ||
|
|
ec49457acd | ||
|
|
d749559f05 | ||
|
|
2ef0f70ffa | ||
|
|
9c75af1c0a | ||
|
|
4f65354b56 | ||
|
|
ac58970475 | ||
|
|
5176d8aaaf | ||
|
|
6571634833 | ||
|
|
f357289c0a | ||
|
|
f03055d1ea | ||
|
|
23b2b55685 | ||
|
|
f160173b62 | ||
|
|
3b0739f718 | ||
|
|
38f50ac9df | ||
|
|
b17cae7814 | ||
|
|
0c22370f4f | ||
|
|
5f93bece47 | ||
|
|
05c106ea1c | ||
|
|
ff48374c36 | ||
|
|
3227d0761a | ||
|
|
df61d3d8f4 | ||
|
|
4ffde57e6f | ||
|
|
3d71d8f47e | ||
|
|
32b2b0be10 | ||
|
|
814152cfe8 | ||
|
|
6228b16c2d | ||
|
|
47a9f9577a | ||
|
|
aaf4d33e74 | ||
|
|
a902e3f0c1 | ||
|
|
719c8298a8 | ||
|
|
591a3de2a1 | ||
|
|
45a7699a08 | ||
|
|
d65b5e2583 | ||
|
|
c6851c5db3 | ||
|
|
a4795014b1 | ||
|
|
53273c6db0 | ||
|
|
bc874ba89c | ||
|
|
ff8b1388d9 | ||
|
|
085cda85a4 | ||
|
|
862d33547a | ||
|
|
2ee24d06b1 | ||
|
|
f4539525f5 | ||
|
|
47e01e3e25 | ||
|
|
6afab4b089 | ||
|
|
e58eb7515e | ||
|
|
6fe01c3b43 | ||
|
|
f849ee4448 | ||
|
|
cff0c77a00 | ||
|
|
b9495c15aa | ||
|
|
d0f6c29f42 | ||
|
|
f5cd276edc | ||
|
|
57b9f19b78 | ||
|
|
4b21f6706f | ||
|
|
897384018d | ||
|
|
a6d1a1740a | ||
|
|
7d13494e70 | ||
|
|
4dd4dc674d | ||
|
|
a25ecf9735 | ||
|
|
c6485be38b | ||
|
|
c54159d95c | ||
|
|
6b3ff86b9b | ||
|
|
fe122663a9 | ||
|
|
a7aec39257 | ||
|
|
c1914d7a06 | ||
|
|
6453276414 | ||
|
|
61c6a8a93e | ||
|
|
ef13a1da19 | ||
|
|
4f15598c61 | ||
|
|
bf1bdc67e5 | ||
|
|
c743881592 | ||
|
|
6f02de25fb | ||
|
|
ddbe024765 | ||
|
|
4f2b84081a | ||
|
|
17eff0796c | ||
|
|
bec8e830fd | ||
|
|
19b40a4e08 | ||
|
|
4a1620fcbf | ||
|
|
ec67f99a08 | ||
|
|
9fab06175a | ||
|
|
20269c371e | ||
|
|
3e178a69c2 | ||
|
|
ce3d421806 | ||
|
|
cb1941c180 | ||
|
|
ef73f099ff | ||
|
|
104aa2a01f | ||
|
|
95f11cd6c7 | ||
|
|
80c7216a91 | ||
|
|
b429128c36 | ||
|
|
36aa65b968 | ||
|
|
e12e186464 | ||
|
|
48f6553680 | ||
|
|
c037017f71 | ||
|
|
78ee7ae14c | ||
|
|
7e8a5a5b00 | ||
|
|
9c5c9649a6 | ||
|
|
5b175082d0 | ||
|
|
4ca52d6c3c | ||
|
|
e396769d8a | ||
|
|
357bce7a15 | ||
|
|
d80e663e20 | ||
|
|
1519fcda32 | ||
|
|
a679fadba2 | ||
|
|
85dc4f53d8 | ||
|
|
fefe4bf006 | ||
|
|
9ce8c049af | ||
|
|
8a118b847d | ||
|
|
c5b52dbf9b | ||
|
|
7fb1be276b | ||
|
|
f5dbfbbebe | ||
|
|
94c19bdf26 | ||
|
|
3c1e74b843 | ||
|
|
6ae39ca7e3 | ||
|
|
3bb962f1bc | ||
|
|
b0fe31a29d | ||
|
|
4c73baf4a3 | ||
|
|
8bec6f0a71 | ||
|
|
35ec2a0ac1 | ||
|
|
c4961a9333 | ||
|
|
523f23d1de | ||
|
|
7731eb43d4 | ||
|
|
62a07765f8 | ||
|
|
69cf5e4501 | ||
|
|
49e15824ef | ||
|
|
8de47d42cc | ||
|
|
d61df63a60 | ||
|
|
86243228c7 | ||
|
|
790768a337 | ||
|
|
b84371680e | ||
|
|
dc449a2ef5 | ||
|
|
eb4d0afa68 | ||
|
|
6938af111e | ||
|
|
c6a0d8642f | ||
|
|
c9e3261d13 | ||
|
|
9cc9a9ba06 | ||
|
|
1a3f395852 | ||
|
|
c7bca49baf | ||
|
|
e1b96f05ca | ||
|
|
92a758a9ee | ||
|
|
d9fd9b89fa | ||
|
|
fea74c43d1 | ||
|
|
a43b4ecd10 | ||
|
|
87bae4b17a | ||
|
|
35ffb696c6 | ||
|
|
a04c136719 | ||
|
|
daed00059c | ||
|
|
ee61cfbd84 | ||
|
|
d98cb97342 | ||
|
|
53d011fd7d | ||
|
|
3efe1723d7 | ||
|
|
d50cadc817 | ||
|
|
7c0e6faa82 | ||
|
|
a636c0f538 | ||
|
|
07879e5865 | ||
|
|
7561640dd2 | ||
|
|
4d9e79fe39 | ||
|
|
a2804b60bb | ||
|
|
6cb25fe4ac | ||
|
|
d3498d2fb4 | ||
|
|
b77e4fb6c6 | ||
|
|
ddf3dac5fe | ||
|
|
c52f7a640c | ||
|
|
3fdb080821 | ||
|
|
3b2713684e | ||
|
|
48fe02a104 | ||
|
|
c14122abe2 | ||
|
|
83cc3abf34 | ||
|
|
ce960f4200 | ||
|
|
8a917985df | ||
|
|
d0873800fd | ||
|
|
dab237bc6a | ||
|
|
c9442c8eea | ||
|
|
54de6b4390 | ||
|
|
f2d5900a18 | ||
|
|
d27584da67 | ||
|
|
1696043662 | ||
|
|
1336753f02 | ||
|
|
39be3e0902 | ||
|
|
5825581a9c | ||
|
|
1f083ee863 | ||
|
|
b8b3559690 | ||
|
|
eea3a1d0cc | ||
|
|
37c1ac944c | ||
|
|
1ca2e833d9 | ||
|
|
a033ef6b5f | ||
|
|
563525d621 | ||
|
|
07721a9ca5 | ||
|
|
f4ec6ca109 | ||
|
|
56eff8f76b | ||
|
|
20907b4cd8 | ||
|
|
c8e07b41da | ||
|
|
39e586db8e | ||
|
|
bd987922e6 | ||
|
|
0bf8b70c18 | ||
|
|
efac313dd8 | ||
|
|
38f2044de8 | ||
|
|
ddde2b5cca | ||
|
|
dcbd0fef40 | ||
|
|
21629e825e | ||
|
|
bc08b90fea | ||
|
|
af433229de | ||
|
|
fca52503d1 | ||
|
|
78e8bf18f8 | ||
|
|
120faaf7be | ||
|
|
c973b36249 | ||
|
|
b5d55cfe03 | ||
|
|
53623cb381 | ||
|
|
4045c3bd53 | ||
|
|
d3762f29b3 | ||
|
|
98c1f5b1ea | ||
|
|
98b7d1d6a2 | ||
|
|
194aec50c6 | ||
|
|
1de5bda888 | ||
|
|
4a20f6b23e | ||
|
|
5d3d1019eb | ||
|
|
a54046cfbf | ||
|
|
adb63ca023 | ||
|
|
b2a88072c8 | ||
|
|
01c84014a4 | ||
|
|
6619534183 | ||
|
|
e73060cf32 | ||
|
|
56a12e3f90 | ||
|
|
7dc51a7b09 | ||
|
|
4cb8950b29 | ||
|
|
013157a38a | ||
|
|
86f91a1ca2 | ||
|
|
349544d8a2 |
@@ -59,7 +59,7 @@ Consumer Devices
|
||||
|
||||
The following consumer devices are supported by the meta-yocto-bsp layer:
|
||||
|
||||
* Intel x86 based PCs and devices (genericx86)
|
||||
* Intel Atom based PCs and devices (atom-pc)
|
||||
|
||||
For more information see the device's section below. The appropriate MACHINE
|
||||
variable value corresponding to the device is given in brackets.
|
||||
@@ -70,10 +70,10 @@ variable value corresponding to the device is given in brackets.
|
||||
===============================
|
||||
|
||||
|
||||
Intel x86 based PCs and devices (genericx86)
|
||||
Intel Atom based PCs and devices (atom-pc)
|
||||
==========================================
|
||||
|
||||
The genericx86 MACHINE is tested on the following platforms:
|
||||
The atom-pc MACHINE is tested on the following platforms:
|
||||
|
||||
o Asus EeePC 901
|
||||
o Acer Aspire One
|
||||
@@ -93,7 +93,7 @@ device as the following commands are run as root and are not reversable.
|
||||
USB Device:
|
||||
1. Build a live image. This image type consists of a simple filesystem
|
||||
without a partition table, which is suitable for USB keys, and with the
|
||||
default setup for the genericx86 machine, this image type is built
|
||||
default setup for the atom-pc machine, this image type is built
|
||||
automatically for any image you build. For example:
|
||||
|
||||
$ bitbake core-image-minimal
|
||||
@@ -101,7 +101,7 @@ USB Device:
|
||||
2. Use the "dd" utility to write the image to the raw block device. For
|
||||
example:
|
||||
|
||||
# dd if=core-image-minimal-genericx86.hddimg of=/dev/sdb
|
||||
# dd if=core-image-minimal-atom-pc.hddimg of=/dev/sdb
|
||||
|
||||
If the device fails to boot with "Boot error" displayed, or apparently
|
||||
stops just after the SYSLINUX version banner, it is likely the BIOS cannot
|
||||
@@ -142,7 +142,7 @@ USB Device:
|
||||
|
||||
# mkdir /tmp/image
|
||||
# mkdir /tmp/usbkey
|
||||
# mount -o loop core-image-minimal-genericx86.hddimg /tmp/image
|
||||
# mount -o loop core-image-minimal-atom-pc.hddimg /tmp/image
|
||||
# mount /dev/sdb4 /tmp/usbkey
|
||||
# cp -rf /tmp/image/* /tmp/usbkey
|
||||
|
||||
|
||||
@@ -39,15 +39,10 @@ import bb.msg
|
||||
from bb import cooker
|
||||
from bb import ui
|
||||
from bb import server
|
||||
from bb import cookerdata
|
||||
|
||||
__version__ = "1.19.1"
|
||||
__version__ = "1.18.0"
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
# Python multiprocessing requires /dev/shm
|
||||
if not os.access('/dev/shm', os.W_OK | os.X_OK):
|
||||
sys.exit("FATAL: /dev/shm does not exist or is not writable")
|
||||
|
||||
# Unbuffer stdout to avoid log truncation in the event
|
||||
# of an unorderly exit as well as to provide timely
|
||||
# updates to log files for use with tail
|
||||
@@ -57,6 +52,16 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
class BBConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self, options):
|
||||
for key, val in options.__dict__.items():
|
||||
setattr(self, key, val)
|
||||
self.pkgs_to_build = []
|
||||
|
||||
|
||||
def get_ui(config):
|
||||
if not config.ui:
|
||||
@@ -70,23 +75,11 @@ def get_ui(config):
|
||||
# suggest a fixed set this allows you to have flexibility in which
|
||||
# ones are available.
|
||||
module = __import__("bb.ui", fromlist = [interface])
|
||||
return getattr(module, interface)
|
||||
return getattr(module, interface).main
|
||||
except AttributeError:
|
||||
sys.exit("FATAL: Invalid user interface '%s' specified.\n"
|
||||
"Valid interfaces: depexp, goggle, ncurses, hob, knotty [default]." % interface)
|
||||
|
||||
def gather_extra_cache_data():
|
||||
extra = []
|
||||
interfaces = ['depexp', 'goggle', 'ncurses', 'hob', 'knotty']
|
||||
for i in interfaces:
|
||||
try:
|
||||
ui = __import__("bb.ui." + i, fromlist = [i])
|
||||
if hasattr(ui, "extraCaches"):
|
||||
extra = extra + ui.extraCaches
|
||||
del ui
|
||||
except:
|
||||
pass
|
||||
return extra
|
||||
|
||||
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
|
||||
warnlog = logging.getLogger("BitBake.Warnings")
|
||||
@@ -107,218 +100,159 @@ warnings.filterwarnings("ignore", category=ImportWarning)
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning, module="<string>$")
|
||||
warnings.filterwarnings("ignore", message="With-statements now directly support multiple context managers")
|
||||
|
||||
class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||
|
||||
def parseCommandLine(self):
|
||||
parser = optparse.OptionParser(
|
||||
version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),
|
||||
usage = """%prog [options] [package ...]
|
||||
|
||||
Executes the specified task (default is 'build') for a given set of BitBake files.
|
||||
It expects that BBFILES is defined, which is a space separated list of files to
|
||||
be executed. BBFILES does support wildcards.
|
||||
Default BBFILES are the .bb files in the current directory.""")
|
||||
|
||||
parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.",
|
||||
action = "store", dest = "buildfile", default = None)
|
||||
|
||||
parser.add_option("-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",
|
||||
action = "store_false", dest = "abort", default = True)
|
||||
|
||||
parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.",
|
||||
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||
|
||||
parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status",
|
||||
action = "store_true", dest = "force", default = False)
|
||||
|
||||
parser.add_option("-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks",
|
||||
action = "store", dest = "cmd")
|
||||
|
||||
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified cmd such as 'compile' and run the default task for the specified target(s)",
|
||||
action = "store", dest = "invalidate_stamp")
|
||||
|
||||
parser.add_option("-r", "--read", help = "read the specified file before bitbake.conf",
|
||||
action = "append", dest = "prefile", default = [])
|
||||
|
||||
parser.add_option("-R", "--postread", help = "read the specified file after bitbake.conf",
|
||||
action = "append", dest = "postfile", default = [])
|
||||
|
||||
parser.add_option("-v", "--verbose", help = "output more chit-chat to the terminal",
|
||||
action = "store_true", dest = "verbose", default = False)
|
||||
|
||||
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||
action = "count", dest="debug", default = 0)
|
||||
|
||||
parser.add_option("-n", "--dry-run", help = "don't execute, just go through the motions",
|
||||
action = "store_true", dest = "dry_run", default = False)
|
||||
|
||||
parser.add_option("-S", "--dump-signatures", help = "don't execute, just dump out the signature construction information",
|
||||
action = "store_true", dest = "dump_signatures", default = False)
|
||||
|
||||
parser.add_option("-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
|
||||
action = "store_true", dest = "parse_only", default = False)
|
||||
|
||||
parser.add_option("-s", "--show-versions", help = "show current and preferred versions of all recipes",
|
||||
action = "store_true", dest = "show_versions", default = False)
|
||||
|
||||
parser.add_option("-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
|
||||
action = "store_true", dest = "show_environment", default = False)
|
||||
|
||||
parser.add_option("-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list",
|
||||
action = "store_true", dest = "dot_graph", default = False)
|
||||
|
||||
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
|
||||
action = "append", dest = "extra_assume_provided", default = [])
|
||||
|
||||
parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
|
||||
action = "append", dest = "debug_domains", default = [])
|
||||
|
||||
parser.add_option("-P", "--profile", help = "profile the command and print a report",
|
||||
action = "store_true", dest = "profile", default = False)
|
||||
|
||||
parser.add_option("-u", "--ui", help = "userinterface to use",
|
||||
action = "store", dest = "ui")
|
||||
|
||||
parser.add_option("-t", "--servertype", help = "Choose which server to use, process or xmlrpc",
|
||||
action = "store", dest = "servertype")
|
||||
|
||||
parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not",
|
||||
action = "store_true", dest = "revisions_changed", default = False)
|
||||
|
||||
parser.add_option("", "--server-only", help = "Run bitbake without UI, the frontend can connect with bitbake server itself",
|
||||
action = "store_true", dest = "server_only", default = False)
|
||||
|
||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
|
||||
action = "store", dest = "bind", default = False)
|
||||
|
||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
|
||||
action = "store_true", dest = "nosetscene", default = False)
|
||||
|
||||
parser.add_option("", "--remote-server", help = "Connect to the specified server",
|
||||
action = "store", dest = "remote_server", default = False)
|
||||
|
||||
parser.add_option("-m", "--kill-server", help = "Terminate the remote server",
|
||||
action = "store_true", dest = "kill_server", default = False)
|
||||
|
||||
parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client",
|
||||
action = "store_true", dest = "observe_only", default = False)
|
||||
|
||||
options, targets = parser.parse_args(sys.argv)
|
||||
|
||||
# some environmental variables set also configuration options
|
||||
if "BBSERVER" in os.environ:
|
||||
options.servertype = "xmlrpc"
|
||||
options.remote_server = os.environ["BBSERVER"]
|
||||
|
||||
return options, targets[1:]
|
||||
|
||||
|
||||
def start_server(servermodule, configParams, configuration):
|
||||
server = servermodule.BitBakeServer()
|
||||
if configParams.bind:
|
||||
(host, port) = configParams.bind.split(':')
|
||||
server.initServer((host, int(port)))
|
||||
else:
|
||||
server.initServer()
|
||||
|
||||
try:
|
||||
configuration.setServerRegIdleCallback(server.getServerIdleCB())
|
||||
|
||||
cooker = bb.cooker.BBCooker(configuration)
|
||||
|
||||
server.addcooker(cooker)
|
||||
server.saveConnectionDetails()
|
||||
except Exception as e:
|
||||
exc_info = sys.exc_info()
|
||||
while True:
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
try:
|
||||
event = server.event_queue.get(block=False)
|
||||
except (queue.Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
raise exc_info[1], None, exc_info[2]
|
||||
server.detach()
|
||||
return server
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(
|
||||
version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),
|
||||
usage = """%prog [options] [package ...]
|
||||
|
||||
configParams = BitBakeConfigParameters()
|
||||
configuration = cookerdata.CookerConfiguration()
|
||||
configuration.setConfigParameters(configParams)
|
||||
Executes the specified task (default is 'build') for a given set of BitBake files.
|
||||
It expects that BBFILES is defined, which is a space separated list of files to
|
||||
be executed. BBFILES does support wildcards.
|
||||
Default BBFILES are the .bb files in the current directory.""")
|
||||
|
||||
ui_module = get_ui(configParams)
|
||||
parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.",
|
||||
action = "store", dest = "buildfile", default = None)
|
||||
|
||||
# Server type can be xmlrpc or process currently, if nothing is specified,
|
||||
parser.add_option("-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",
|
||||
action = "store_false", dest = "abort", default = True)
|
||||
|
||||
parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.",
|
||||
action = "store_true", dest = "tryaltconfigs", default = False)
|
||||
|
||||
parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status",
|
||||
action = "store_true", dest = "force", default = False)
|
||||
|
||||
parser.add_option("-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks",
|
||||
action = "store", dest = "cmd")
|
||||
|
||||
parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified cmd such as 'compile' and run the default task for the specified target(s)",
|
||||
action = "store", dest = "invalidate_stamp")
|
||||
|
||||
parser.add_option("-r", "--read", help = "read the specified file before bitbake.conf",
|
||||
action = "append", dest = "prefile", default = [])
|
||||
|
||||
parser.add_option("-R", "--postread", help = "read the specified file after bitbake.conf",
|
||||
action = "append", dest = "postfile", default = [])
|
||||
|
||||
parser.add_option("-v", "--verbose", help = "output more chit-chat to the terminal",
|
||||
action = "store_true", dest = "verbose", default = False)
|
||||
|
||||
parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
|
||||
action = "count", dest="debug", default = 0)
|
||||
|
||||
parser.add_option("-n", "--dry-run", help = "don't execute, just go through the motions",
|
||||
action = "store_true", dest = "dry_run", default = False)
|
||||
|
||||
parser.add_option("-S", "--dump-signatures", help = "don't execute, just dump out the signature construction information",
|
||||
action = "store_true", dest = "dump_signatures", default = False)
|
||||
|
||||
parser.add_option("-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
|
||||
action = "store_true", dest = "parse_only", default = False)
|
||||
|
||||
parser.add_option("-s", "--show-versions", help = "show current and preferred versions of all recipes",
|
||||
action = "store_true", dest = "show_versions", default = False)
|
||||
|
||||
parser.add_option("-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
|
||||
action = "store_true", dest = "show_environment", default = False)
|
||||
|
||||
parser.add_option("-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list",
|
||||
action = "store_true", dest = "dot_graph", default = False)
|
||||
|
||||
parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
|
||||
action = "append", dest = "extra_assume_provided", default = [])
|
||||
|
||||
parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
|
||||
action = "append", dest = "debug_domains", default = [])
|
||||
|
||||
parser.add_option("-P", "--profile", help = "profile the command and print a report",
|
||||
action = "store_true", dest = "profile", default = False)
|
||||
|
||||
parser.add_option("-u", "--ui", help = "userinterface to use",
|
||||
action = "store", dest = "ui")
|
||||
|
||||
parser.add_option("-t", "--servertype", help = "Choose which server to use, none, process or xmlrpc",
|
||||
action = "store", dest = "servertype")
|
||||
|
||||
parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not",
|
||||
action = "store_true", dest = "revisions_changed", default = False)
|
||||
|
||||
parser.add_option("", "--server-only", help = "Run bitbake without UI, the frontend can connect with bitbake server itself",
|
||||
action = "store_true", dest = "server_only", default = False)
|
||||
|
||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
|
||||
action = "store", dest = "bind", default = False)
|
||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
|
||||
action = "store_true", dest = "nosetscene", default = False)
|
||||
options, args = parser.parse_args(sys.argv)
|
||||
|
||||
configuration = BBConfiguration(options)
|
||||
configuration.pkgs_to_build.extend(args[1:])
|
||||
|
||||
ui_main = get_ui(configuration)
|
||||
|
||||
# Server type can be xmlrpc, process or none currently, if nothing is specified,
|
||||
# the default server is process
|
||||
if configParams.servertype:
|
||||
server_type = configParams.servertype
|
||||
if configuration.servertype:
|
||||
server_type = configuration.servertype
|
||||
else:
|
||||
server_type = 'process'
|
||||
|
||||
try:
|
||||
module = __import__("bb.server", fromlist = [server_type])
|
||||
servermodule = getattr(module, server_type)
|
||||
server = getattr(module, server_type)
|
||||
except AttributeError:
|
||||
sys.exit("FATAL: Invalid server type '%s' specified.\n"
|
||||
"Valid interfaces: xmlrpc, process [default]." % servertype)
|
||||
"Valid interfaces: xmlrpc, process [default], none." % servertype)
|
||||
|
||||
if configParams.server_only:
|
||||
if configParams.servertype != "xmlrpc":
|
||||
if configuration.server_only:
|
||||
if configuration.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '--server-only' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
if not configParams.bind:
|
||||
if not configuration.bind:
|
||||
sys.exit("FATAL: The '--server-only' option requires a name/address to bind to with the -B option.\n")
|
||||
if configParams.remote_server:
|
||||
sys.exit("FATAL: The '--server-only' option conflicts with the '--remote-server' option. %s\n" %
|
||||
("Please check your BBSERVER environment" if "BBSERVER" in os.environ else "" ))
|
||||
|
||||
if configParams.bind and configParams.servertype != "xmlrpc":
|
||||
if configuration.bind and configuration.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
|
||||
if configParams.remote_server and configParams.servertype != "xmlrpc":
|
||||
sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||
|
||||
if configParams.observe_only and (not configParams.remote_server or configParams.bind):
|
||||
sys.exit("FATAL: '--observe-only' can only be used by UI clients connecting to a server.\n")
|
||||
|
||||
if "BBDEBUG" in os.environ:
|
||||
level = int(os.environ["BBDEBUG"])
|
||||
if level > configuration.debug:
|
||||
configuration.debug = level
|
||||
|
||||
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
|
||||
bb.msg.init_msgconfig(configuration.verbose, configuration.debug,
|
||||
configuration.debug_domains)
|
||||
|
||||
# Ensure logging messages get sent to the UI as events
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
# Before we start modifying the environment we should take a pristine
|
||||
# copy for possible later use
|
||||
initialenv = os.environ.copy()
|
||||
# Clear away any spurious environment variables while we stoke up the cooker
|
||||
cleanedvars = bb.utils.clean_environment()
|
||||
|
||||
# Collect all the caches we need
|
||||
if configParams.server_only:
|
||||
configuration.extra_caches = gather_extra_cache_data()
|
||||
server = server.BitBakeServer()
|
||||
if configuration.bind:
|
||||
server.initServer((configuration.bind, 0))
|
||||
else:
|
||||
configuration.extra_caches = getattr(ui_module, "extraCaches", [])
|
||||
server.initServer()
|
||||
|
||||
if not configParams.remote_server:
|
||||
# we start a server with a given configuration
|
||||
server = start_server(servermodule, configParams, configuration)
|
||||
else:
|
||||
# we start a stub server that is actually a XMLRPClient to
|
||||
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only)
|
||||
server.saveConnectionDetails(configParams.remote_server)
|
||||
idle = server.getServerIdleCB()
|
||||
|
||||
cooker = bb.cooker.BBCooker(configuration, idle, initialenv)
|
||||
cooker.parseCommandLine()
|
||||
|
||||
server.addcooker(cooker)
|
||||
server.saveConnectionDetails()
|
||||
server.detach()
|
||||
|
||||
# Should no longer need to ever reference cooker
|
||||
del cooker
|
||||
|
||||
logger.removeHandler(handler)
|
||||
|
||||
if not configParams.server_only:
|
||||
if not configuration.server_only:
|
||||
# Setup a connection to the server (cooker)
|
||||
server_connection = server.establishConnection()
|
||||
|
||||
@@ -327,12 +261,12 @@ def main():
|
||||
os.environ[k] = cleanedvars[k]
|
||||
|
||||
try:
|
||||
return ui_module.main(server_connection.connection, server_connection.events, configParams)
|
||||
return server.launchUI(ui_main, server_connection.connection, server_connection.events)
|
||||
finally:
|
||||
bb.event.ui_queue = []
|
||||
server_connection.terminate()
|
||||
else:
|
||||
print("server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
|
||||
print("server address: %s, server port: %s" % (server.serverinfo.host, server.serverinfo.port))
|
||||
|
||||
return 1
|
||||
|
||||
@@ -342,6 +276,6 @@ if __name__ == "__main__":
|
||||
except Exception:
|
||||
ret = 1
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
traceback.print_exc(5)
|
||||
sys.exit(ret)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class Commands(cmd.Cmd):
|
||||
for layerdir in self.bblayers:
|
||||
layername = self.get_layer_name(layerdir)
|
||||
layerpri = 0
|
||||
for layer, _, regex, pri in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layer, _, regex, pri in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(os.path.join(layerdir, 'test')):
|
||||
layerpri = pri
|
||||
break
|
||||
@@ -225,15 +225,15 @@ Options:
|
||||
|
||||
|
||||
def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_multi_provider_only):
|
||||
pkg_pn = self.bbhandler.cooker.recipecache.pkg_pn
|
||||
(latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.config_data, self.bbhandler.cooker.recipecache, pkg_pn)
|
||||
allproviders = bb.providers.allProviders(self.bbhandler.cooker.recipecache)
|
||||
pkg_pn = self.bbhandler.cooker.status.pkg_pn
|
||||
(latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.cooker.configuration.data, self.bbhandler.cooker.status, pkg_pn)
|
||||
allproviders = bb.providers.allProviders(self.bbhandler.cooker.status)
|
||||
|
||||
# Ensure we list skipped recipes
|
||||
# We are largely guessing about PN, PV and the preferred version here,
|
||||
# but we have no choice since skipped recipes are not fully parsed
|
||||
skiplist = self.bbhandler.cooker.skiplist.keys()
|
||||
skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.collection.calc_bbfile_priority(fileitem) )
|
||||
skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.calc_bbfile_priority(fileitem) )
|
||||
skiplist.reverse()
|
||||
for fn in skiplist:
|
||||
recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
|
||||
@@ -371,8 +371,8 @@ build results (as the layer priority order has effectively changed).
|
||||
appended_recipes = []
|
||||
for layer in layers:
|
||||
overlayed = []
|
||||
for f in self.bbhandler.cooker.collection.overlayed.iterkeys():
|
||||
for of in self.bbhandler.cooker.collection.overlayed[f]:
|
||||
for f in self.bbhandler.cooker.overlayed.iterkeys():
|
||||
for of in self.bbhandler.cooker.overlayed[f]:
|
||||
if of.startswith(layer):
|
||||
overlayed.append(of)
|
||||
|
||||
@@ -396,8 +396,8 @@ build results (as the layer priority order has effectively changed).
|
||||
logger.warn('Overwriting file %s', fdest)
|
||||
bb.utils.copyfile(f1full, fdest)
|
||||
if ext == '.bb':
|
||||
if f1 in self.bbhandler.cooker.collection.appendlist:
|
||||
appends = self.bbhandler.cooker.collection.appendlist[f1]
|
||||
if f1 in self.bbhandler.cooker.appendlist:
|
||||
appends = self.bbhandler.cooker.appendlist[f1]
|
||||
if appends:
|
||||
logger.plain(' Applying appends to %s' % fdest )
|
||||
for appendname in appends:
|
||||
@@ -406,9 +406,9 @@ build results (as the layer priority order has effectively changed).
|
||||
appended_recipes.append(f1)
|
||||
|
||||
# Take care of when some layers are excluded and yet we have included bbappends for those recipes
|
||||
for recipename in self.bbhandler.cooker.collection.appendlist.iterkeys():
|
||||
for recipename in self.bbhandler.cooker.appendlist.iterkeys():
|
||||
if recipename not in appended_recipes:
|
||||
appends = self.bbhandler.cooker.collection.appendlist[recipename]
|
||||
appends = self.bbhandler.cooker.appendlist[recipename]
|
||||
first_append = None
|
||||
for appendname in appends:
|
||||
layer = layer_path_match(appendname)
|
||||
@@ -426,7 +426,7 @@ build results (as the layer priority order has effectively changed).
|
||||
# have come from)
|
||||
first_regex = None
|
||||
layerdir = layers[0]
|
||||
for layername, pattern, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layername, pattern, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(os.path.join(layerdir, 'test')):
|
||||
first_regex = regex
|
||||
break
|
||||
@@ -456,7 +456,7 @@ build results (as the layer priority order has effectively changed).
|
||||
logger.warning("File %s does not match the flattened layer's BBFILES setting, you may need to edit conf/layer.conf or move the file elsewhere" % f1full)
|
||||
|
||||
def get_file_layer(self, filename):
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(filename):
|
||||
for layerdir in self.bblayers:
|
||||
if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
|
||||
@@ -464,7 +464,7 @@ build results (as the layer priority order has effectively changed).
|
||||
return "?"
|
||||
|
||||
def get_file_layerdir(self, filename):
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
|
||||
for layer, _, regex, _ in self.bbhandler.cooker.status.bbfile_config_priorities:
|
||||
if regex.match(filename):
|
||||
for layerdir in self.bblayers:
|
||||
if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
|
||||
@@ -498,7 +498,7 @@ usage: show-appends
|
||||
Recipes are listed with the bbappends that apply to them as subitems.
|
||||
"""
|
||||
self.bbhandler.prepare()
|
||||
if not self.bbhandler.cooker.collection.appendlist:
|
||||
if not self.bbhandler.cooker.appendlist:
|
||||
logger.plain('No append files found')
|
||||
return
|
||||
|
||||
@@ -515,7 +515,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
|
||||
filenames = self.bbhandler.cooker_data.pkg_pn[pn]
|
||||
|
||||
best = bb.providers.findBestProvider(pn,
|
||||
self.bbhandler.config_data,
|
||||
self.bbhandler.cooker.configuration.data,
|
||||
self.bbhandler.cooker_data,
|
||||
self.bbhandler.cooker_data.pkg_pn)
|
||||
best_filename = os.path.basename(best[3])
|
||||
@@ -550,7 +550,7 @@ Recipes are listed with the bbappends that apply to them as subitems.
|
||||
continue
|
||||
|
||||
basename = os.path.basename(filename)
|
||||
appends = self.bbhandler.cooker.collection.appendlist.get(basename)
|
||||
appends = self.bbhandler.cooker.appendlist.get(basename)
|
||||
if appends:
|
||||
appended.append((basename, list(appends)))
|
||||
else:
|
||||
@@ -598,7 +598,7 @@ The .bbappend file can impact the dependency.
|
||||
for pn in deps:
|
||||
if pn in self.bbhandler.cooker_data.pkg_pn:
|
||||
best = bb.providers.findBestProvider(pn,
|
||||
self.bbhandler.config_data,
|
||||
self.bbhandler.cooker.configuration.data,
|
||||
self.bbhandler.cooker_data,
|
||||
self.bbhandler.cooker_data.pkg_pn)
|
||||
self.check_cross_depends("DEPENDS", layername, f, best[3], show_filenames)
|
||||
@@ -616,7 +616,7 @@ The .bbappend file can impact the dependency.
|
||||
all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep)
|
||||
if all_p:
|
||||
best = bb.providers.filterProvidersRunTime(all_p, rdep,
|
||||
self.bbhandler.config_data,
|
||||
self.bbhandler.cooker.configuration.data,
|
||||
self.bbhandler.cooker_data)[0][0]
|
||||
self.check_cross_depends("RDEPENDS", layername, f, best, show_filenames)
|
||||
|
||||
|
||||
119
bitbake/bin/bitbake-runtask
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
from bb import fetch2
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||
|
||||
class BBConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self, **options):
|
||||
self.data = {}
|
||||
self.file = []
|
||||
self.cmd = None
|
||||
self.dump_signatures = True
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.parse_only = True
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
try:
|
||||
return super(BBConfiguration, self).__getattribute__(attribute)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
_warnings_showwarning = warnings.showwarning
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Display python warning messages using bb.msg"""
|
||||
if file is not None:
|
||||
if _warnings_showwarning is not None:
|
||||
_warnings_showwarning(message, category, filename, lineno, file, line)
|
||||
else:
|
||||
s = warnings.formatwarning(message, category, filename, lineno)
|
||||
s = s.split("\n")[0]
|
||||
bb.msg.warn(None, s)
|
||||
|
||||
warnings.showwarning = _showwarning
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
import bb.event
|
||||
import bb.cooker
|
||||
|
||||
buildfile = sys.argv[1]
|
||||
taskname = sys.argv[2]
|
||||
if len(sys.argv) >= 4:
|
||||
dryrun = sys.argv[3]
|
||||
else:
|
||||
dryrun = False
|
||||
if len(sys.argv) >= 5:
|
||||
hashfile = sys.argv[4]
|
||||
p = pickle.Unpickler(file(hashfile, "rb"))
|
||||
hashdata = p.load()
|
||||
else:
|
||||
hashdata = None
|
||||
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
#An example to make debug log messages show up
|
||||
#bb.msg.init_msgconfig(True, 3, [])
|
||||
|
||||
console = logging.StreamHandler(sys.stdout)
|
||||
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
|
||||
def worker_fire(event, d):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
console.handle(event)
|
||||
bb.event.worker_fire = worker_fire
|
||||
bb.event.worker_pid = os.getpid()
|
||||
|
||||
initialenv = os.environ.copy()
|
||||
config = BBConfiguration()
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
pass
|
||||
|
||||
cooker = bb.cooker.BBCooker(config, register_idle_function, initialenv)
|
||||
config_data = cooker.configuration.data
|
||||
cooker.status = config_data
|
||||
cooker.handleCollections(config_data.getVar("BBFILE_COLLECTIONS", 1))
|
||||
|
||||
fn, cls = bb.cache.Cache.virtualfn2realfn(buildfile)
|
||||
buildfile = cooker.matchFile(fn)
|
||||
fn = bb.cache.Cache.realfn2virtual(buildfile, cls)
|
||||
|
||||
cooker.buildSetVars()
|
||||
|
||||
# Load data into the cache for fn and parse the loaded cache data
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, cooker.get_file_appends(fn), cooker.configuration.data)
|
||||
|
||||
if taskname.endswith("_setscene"):
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
if hashdata:
|
||||
bb.parse.siggen.set_taskdata(hashdata["hashes"], hashdata["deps"])
|
||||
for h in hashdata["hashes"]:
|
||||
the_data.setVar("BBHASH_%s" % h, hashdata["hashes"][h])
|
||||
for h in hashdata["deps"]:
|
||||
the_data.setVar("BBHASHDEPS_%s" % h, hashdata["deps"][h])
|
||||
|
||||
ret = 0
|
||||
if dryrun != "True":
|
||||
ret = bb.build.exec_task(fn, taskname, the_data)
|
||||
sys.exit(ret)
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
|
||||
from bb import fetch2
|
||||
import logging
|
||||
import bb
|
||||
import select
|
||||
import errno
|
||||
import signal
|
||||
|
||||
# Users shouldn't be running this code directly
|
||||
if len(sys.argv) != 2 or sys.argv[1] != "decafbad":
|
||||
print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
|
||||
sys.exit(1)
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
|
||||
|
||||
|
||||
worker_pipe = sys.stdout.fileno()
|
||||
bb.utils.nonblockingfd(worker_pipe)
|
||||
|
||||
handler = bb.event.LogHandler()
|
||||
logger.addHandler(handler)
|
||||
|
||||
if 0:
|
||||
# Code to write out a log file of all events passing through the worker
|
||||
logfilename = "/tmp/workerlogfile"
|
||||
format_str = "%(levelname)s: %(message)s"
|
||||
conlogformat = bb.msg.BBLogFormatter(format_str)
|
||||
consolelog = logging.FileHandler(logfilename)
|
||||
bb.msg.addDefaultlogFilter(consolelog)
|
||||
consolelog.setFormatter(conlogformat)
|
||||
logger.addHandler(consolelog)
|
||||
|
||||
worker_queue = ""
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_fire_prepickled(data)
|
||||
|
||||
def worker_fire_prepickled(event):
|
||||
global worker_queue
|
||||
|
||||
worker_queue = worker_queue + event
|
||||
worker_flush()
|
||||
|
||||
def worker_flush():
|
||||
global worker_queue, worker_pipe
|
||||
|
||||
if not worker_queue:
|
||||
return
|
||||
|
||||
try:
|
||||
written = os.write(worker_pipe, worker_queue)
|
||||
worker_queue = worker_queue[written:]
|
||||
except (IOError, OSError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
|
||||
def worker_child_fire(event, d):
|
||||
global worker_pipe
|
||||
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_pipe.write(data)
|
||||
|
||||
bb.event.worker_fire = worker_fire
|
||||
|
||||
lf = None
|
||||
#lf = open("/tmp/workercommandlog", "w+")
|
||||
def workerlog_write(msg):
|
||||
if lf:
|
||||
lf.write(msg)
|
||||
lf.flush()
|
||||
|
||||
def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, quieterrors=False):
|
||||
# We need to setup the environment BEFORE the fork, since
|
||||
# a fork() or exec*() activates PSEUDO...
|
||||
|
||||
envbackup = {}
|
||||
fakeenv = {}
|
||||
umask = None
|
||||
|
||||
taskdep = workerdata["taskdeps"][fn]
|
||||
if 'umask' in taskdep and taskname in taskdep['umask']:
|
||||
# umask might come in as a number or text string..
|
||||
try:
|
||||
umask = int(taskdep['umask'][taskname],8)
|
||||
except TypeError:
|
||||
umask = taskdep['umask'][taskname]
|
||||
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
|
||||
envvars = (workerdata["fakerootenv"][fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
fakedirs = (workerdata["fakerootdirs"][fn] or "").split()
|
||||
for p in fakedirs:
|
||||
bb.utils.mkdirhier(p)
|
||||
logger.debug(2, 'Running %s:%s under fakeroot, fakedirs: %s' %
|
||||
(fn, taskname, ', '.join(fakedirs)))
|
||||
else:
|
||||
envvars = (workerdata["fakerootnoenv"][fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
try:
|
||||
pipein, pipeout = os.pipe()
|
||||
pipein = os.fdopen(pipein, 'rb', 4096)
|
||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||
pid = os.fork()
|
||||
except OSError as e:
|
||||
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
|
||||
if pid == 0:
|
||||
global worker_pipe
|
||||
pipein.close()
|
||||
|
||||
# Save out the PID so that the event can include it the
|
||||
# events
|
||||
bb.event.worker_pid = os.getpid()
|
||||
bb.event.worker_fire = worker_child_fire
|
||||
worker_pipe = pipeout
|
||||
|
||||
# Make the child the process group leader
|
||||
os.setpgid(0, 0)
|
||||
# No stdin
|
||||
newsi = os.open(os.devnull, os.O_RDWR)
|
||||
os.dup2(newsi, sys.stdin.fileno())
|
||||
|
||||
if umask:
|
||||
os.umask(umask)
|
||||
|
||||
data.setVar("BB_WORKERCONTEXT", "1")
|
||||
data.setVar("BUILDNAME", workerdata["buildname"])
|
||||
data.setVar("DATE", workerdata["date"])
|
||||
data.setVar("TIME", workerdata["time"])
|
||||
bb.parse.siggen.set_taskdata(workerdata["hashes"], workerdata["hash_deps"], workerdata["sigchecksums"])
|
||||
ret = 0
|
||||
try:
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, appends, data)
|
||||
the_data.setVar('BB_TASKHASH', workerdata["runq_hash"][task])
|
||||
for h in workerdata["hashes"]:
|
||||
the_data.setVar("BBHASH_%s" % h, workerdata["hashes"][h])
|
||||
for h in workerdata["hash_deps"]:
|
||||
the_data.setVar("BBHASHDEPS_%s" % h, workerdata["hash_deps"][h])
|
||||
|
||||
# exported_vars() returns a generator which *cannot* be passed to os.environ.update()
|
||||
# successfully. We also need to unset anything from the environment which shouldn't be there
|
||||
exports = bb.data.exported_vars(the_data)
|
||||
bb.utils.empty_environment()
|
||||
for e, v in exports:
|
||||
os.environ[e] = v
|
||||
for e in fakeenv:
|
||||
os.environ[e] = fakeenv[e]
|
||||
the_data.setVar(e, fakeenv[e])
|
||||
the_data.setVarFlag(e, 'export', "1")
|
||||
|
||||
if quieterrors:
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
except Exception as exc:
|
||||
if not quieterrors:
|
||||
logger.critical(str(exc))
|
||||
os._exit(1)
|
||||
try:
|
||||
if not cfg.dry_run:
|
||||
ret = bb.build.exec_task(fn, taskname, the_data, cfg.profile)
|
||||
os._exit(ret)
|
||||
except:
|
||||
os._exit(1)
|
||||
else:
|
||||
for key, value in envbackup.iteritems():
|
||||
if value is None:
|
||||
del os.environ[key]
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
return pid, pipein, pipeout
|
||||
|
||||
class runQueueWorkerPipe():
|
||||
"""
|
||||
Abstraction for a pipe between a worker thread and the worker server
|
||||
"""
|
||||
def __init__(self, pipein, pipeout):
|
||||
self.input = pipein
|
||||
if pipeout:
|
||||
pipeout.close()
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
|
||||
def read(self):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read(102400)
|
||||
except (OSError, IOError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
|
||||
end = len(self.queue)
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1:
|
||||
worker_fire_prepickled(self.queue[:index+8])
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find("</event>")
|
||||
return (end > start)
|
||||
|
||||
def close(self):
|
||||
while self.read():
|
||||
continue
|
||||
if len(self.queue) > 0:
|
||||
print("Warning, worker child left partial message: %s" % self.queue)
|
||||
self.input.close()
|
||||
|
||||
normalexit = False
|
||||
|
||||
class BitbakeWorker(object):
|
||||
def __init__(self, din):
|
||||
self.input = din
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
self.cookercfg = None
|
||||
self.databuilder = None
|
||||
self.data = None
|
||||
self.build_pids = {}
|
||||
self.build_pipes = {}
|
||||
|
||||
def serve(self):
|
||||
while True:
|
||||
(ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
|
||||
if self.input in ready or len(self.queue):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read()
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
end = len(self.queue)
|
||||
self.handle_item("cookerconfig", self.handle_cookercfg)
|
||||
self.handle_item("workerdata", self.handle_workerdata)
|
||||
self.handle_item("runtask", self.handle_runtask)
|
||||
self.handle_item("finishnow", self.handle_finishnow)
|
||||
self.handle_item("ping", self.handle_ping)
|
||||
self.handle_item("quit", self.handle_quit)
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
if len(self.build_pids):
|
||||
self.process_waitpid()
|
||||
worker_flush()
|
||||
|
||||
|
||||
def handle_item(self, item, func):
|
||||
if self.queue.startswith("<" + item + ">"):
|
||||
index = self.queue.find("</" + item + ">")
|
||||
while index != -1:
|
||||
func(self.queue[(len(item) + 2):index])
|
||||
self.queue = self.queue[(index + len(item) + 3):]
|
||||
index = self.queue.find("</" + item + ">")
|
||||
|
||||
def handle_cookercfg(self, data):
|
||||
self.cookercfg = pickle.loads(data)
|
||||
self.databuilder = bb.cookerdata.CookerDataBuilder(self.cookercfg, worker=True)
|
||||
self.databuilder.parseBaseConfiguration()
|
||||
self.data = self.databuilder.data
|
||||
|
||||
def handle_workerdata(self, data):
|
||||
self.workerdata = pickle.loads(data)
|
||||
bb.msg.loggerDefaultDebugLevel = self.workerdata["logdefaultdebug"]
|
||||
bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
|
||||
bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
|
||||
bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
|
||||
self.data.setVar("PRSERV_HOST", self.workerdata["prhost"])
|
||||
|
||||
def handle_ping(self, _):
|
||||
workerlog_write("Handling ping\n")
|
||||
|
||||
logger.warn("Pong from bitbake-worker!")
|
||||
|
||||
def handle_quit(self, data):
|
||||
workerlog_write("Handling quit\n")
|
||||
|
||||
global normalexit
|
||||
normalexit = True
|
||||
sys.exit(0)
|
||||
|
||||
def handle_runtask(self, data):
|
||||
fn, task, taskname, quieterrors, appends = pickle.loads(data)
|
||||
workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
|
||||
|
||||
pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.workerdata, fn, task, taskname, appends, quieterrors)
|
||||
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
|
||||
|
||||
def process_waitpid(self):
|
||||
"""
|
||||
Return none is there are no processes awaiting result collection, otherwise
|
||||
collect the process exit codes and close the information pipe.
|
||||
"""
|
||||
try:
|
||||
pid, status = os.waitpid(-1, os.WNOHANG)
|
||||
if pid == 0 or os.WIFSTOPPED(status):
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
|
||||
|
||||
if os.WIFEXITED(status):
|
||||
status = os.WEXITSTATUS(status)
|
||||
elif os.WIFSIGNALED(status):
|
||||
# Per shell conventions for $?, when a process exits due to
|
||||
# a signal, we return an exit code of 128 + SIGNUM
|
||||
status = 128 + os.WTERMSIG(status)
|
||||
|
||||
task = self.build_pids[pid]
|
||||
del self.build_pids[pid]
|
||||
|
||||
self.build_pipes[pid].close()
|
||||
del self.build_pipes[pid]
|
||||
|
||||
worker_fire_prepickled("<exitcode>" + pickle.dumps((task, status)) + "</exitcode>")
|
||||
|
||||
def handle_finishnow(self, _):
|
||||
if self.build_pids:
|
||||
logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
|
||||
for k, v in self.build_pids.iteritems():
|
||||
try:
|
||||
os.kill(-k, signal.SIGTERM)
|
||||
os.waitpid(-1, 0)
|
||||
except:
|
||||
pass
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
try:
|
||||
worker = BitbakeWorker(sys.stdin)
|
||||
worker.serve()
|
||||
except BaseException as e:
|
||||
if not normalexit:
|
||||
import traceback
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
sys.stderr.write(str(e))
|
||||
while len(worker_queue):
|
||||
worker_flush()
|
||||
workerlog_write("exitting")
|
||||
sys.exit(0)
|
||||
|
||||
@@ -14,7 +14,7 @@ endif
|
||||
au BufNewFile,BufRead *.{bb,bbappend,bbclass} set filetype=bitbake
|
||||
|
||||
" .inc
|
||||
au BufNewFile,BufRead *.inc set filetype=bitbake
|
||||
au BufNewFile,BufRead *.inc set filetype=bitbake
|
||||
|
||||
" .conf
|
||||
au BufNewFile,BufRead *.conf
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
set sts=4 sw=4 et
|
||||
set cms=#%s
|
||||
|
||||
@@ -15,7 +15,7 @@ if &compatible || v:version < 600
|
||||
endif
|
||||
|
||||
fun! <SID>GetUserName()
|
||||
let l:user_name = system("git config --get user.name")
|
||||
let l:user_name = system("git-config --get user.name")
|
||||
if v:shell_error
|
||||
return "Unknow User"
|
||||
else
|
||||
@@ -23,7 +23,7 @@ fun! <SID>GetUserName()
|
||||
endfun
|
||||
|
||||
fun! <SID>GetUserEmail()
|
||||
let l:user_email = system("git config --get user.email")
|
||||
let l:user_email = system("git-config --get user.email")
|
||||
if v:shell_error
|
||||
return "unknow@user.org"
|
||||
else
|
||||
|
||||
@@ -44,8 +44,8 @@ syn match bbArrayBrackets "[\[\]]" contained
|
||||
|
||||
" BitBake strings
|
||||
syn match bbContinue "\\$"
|
||||
syn region bbString matchgroup=bbQuote start=+"+ skip=+\\$+ end=+"+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+'+ skip=+\\$+ end=+'+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+"+ skip=+\\$+ excludenl end=+"+ contained keepend contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
syn region bbString matchgroup=bbQuote start=+'+ skip=+\\$+ excludenl end=+'+ contained keepend contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell
|
||||
|
||||
" Vars definition
|
||||
syn match bbExport "^export" nextgroup=bbIdentifier skipwhite
|
||||
@@ -55,11 +55,11 @@ syn match bbVarDeref "${[a-zA-Z0-9\-_\.\/\+]\+}" contained
|
||||
syn match bbVarEq "\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)" contained nextgroup=bbVarValue
|
||||
syn match bbVarDef "^\(export\s*\)\?\([a-zA-Z0-9\-_\.\/\+]\+\(_[${}a-zA-Z0-9\-_\.\/\+]\+\)\?\)\s*\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)\@=" contains=bbExportFlag,bbIdentifier,bbVarDeref nextgroup=bbVarEq
|
||||
syn match bbVarValue ".*$" contained contains=bbString,bbVarDeref,bbVarPyValue
|
||||
syn region bbVarPyValue start=+${@+ skip=+\\$+ end=+}+ contained contains=@python
|
||||
syn region bbVarPyValue start=+${@+ skip=+\\$+ excludenl end=+}+ contained contains=@python
|
||||
|
||||
" Vars metadata flags
|
||||
syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag
|
||||
syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(=\|+=\|=+\|?=\)\@=" contained contains=bbIdentifier nextgroup=bbVarEq
|
||||
syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(=\)\@=" keepend excludenl contained contains=bbIdentifier nextgroup=bbVarEq
|
||||
|
||||
" Includes and requires
|
||||
syn keyword bbInclude inherit include require contained
|
||||
@@ -83,16 +83,13 @@ if exists("b:current_syntax")
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
syn keyword bbShFakeRootFlag fakeroot contained
|
||||
syn match bbShFuncDef "^\(fakeroot\s*\)\?\([0-9A-Za-z_${}-]\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbFunction,bbVarDeref,bbDelimiter nextgroup=bbShFuncRegion skipwhite
|
||||
syn region bbShFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@shell
|
||||
|
||||
" Python value inside shell functions
|
||||
syn region shDeref start=+${@+ skip=+\\$+ excludenl end=+}+ contained contains=@python
|
||||
syn match bbShFuncDef "^\(fakeroot\s*\)\?\([0-9A-Za-z_-]\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbFunction,bbDelimiter nextgroup=bbShFuncRegion skipwhite
|
||||
syn region bbShFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@shell
|
||||
|
||||
" BitBake python metadata
|
||||
syn keyword bbPyFlag python contained
|
||||
syn match bbPyFuncDef "^\(python\s\+\)\([0-9A-Za-z_${}-]\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPyFlag,bbFunction,bbVarDeref,bbDelimiter nextgroup=bbPyFuncRegion skipwhite
|
||||
syn region bbPyFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@python
|
||||
syn match bbPyFuncDef "^\(python\s\+\)\([0-9A-Za-z_-]\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbPyFlag,bbFunction,bbDelimiter nextgroup=bbPyFuncRegion skipwhite
|
||||
syn region bbPyFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" keepend contained contains=@python
|
||||
|
||||
" BitBake 'def'd python functions
|
||||
syn keyword bbPyDef def contained
|
||||
|
||||
@@ -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.19.1"
|
||||
__version__ = "1.18.0"
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2, 7, 3):
|
||||
raise RuntimeError("Sorry, python 2.7.3 or later is required for this version of bitbake")
|
||||
if sys.version_info < (2, 6, 0):
|
||||
raise RuntimeError("Sorry, python 2.6.0 or later is required for this version of bitbake")
|
||||
|
||||
|
||||
class BBHandledException(Exception):
|
||||
|
||||
@@ -34,7 +34,7 @@ import bb
|
||||
import bb.msg
|
||||
import bb.process
|
||||
from contextlib import nested
|
||||
from bb import event, utils
|
||||
from bb import data, event, utils
|
||||
|
||||
bblogger = logging.getLogger('BitBake')
|
||||
logger = logging.getLogger('BitBake.Build')
|
||||
@@ -60,7 +60,7 @@ class FuncFailed(Exception):
|
||||
|
||||
def __str__(self):
|
||||
if self.logfile and os.path.exists(self.logfile):
|
||||
msg = ("%s (log file is located at %s)" %
|
||||
msg = ("%s (see %s for further information)" %
|
||||
(self.msg, self.logfile))
|
||||
else:
|
||||
msg = self.msg
|
||||
@@ -69,10 +69,9 @@ class FuncFailed(Exception):
|
||||
class TaskBase(event.Event):
|
||||
"""Base class for task events"""
|
||||
|
||||
def __init__(self, t, logfile, d):
|
||||
def __init__(self, t, d ):
|
||||
self._task = t
|
||||
self._package = d.getVar("PF", True)
|
||||
self.logfile = logfile
|
||||
event.Event.__init__(self)
|
||||
self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
|
||||
|
||||
@@ -97,11 +96,16 @@ class TaskFailed(TaskBase):
|
||||
"""Task execution failed"""
|
||||
|
||||
def __init__(self, task, logfile, metadata, errprinted = False):
|
||||
self.logfile = logfile
|
||||
self.errprinted = errprinted
|
||||
super(TaskFailed, self).__init__(task, logfile, metadata)
|
||||
super(TaskFailed, self).__init__(task, metadata)
|
||||
|
||||
class TaskFailedSilent(TaskBase):
|
||||
"""Task execution failed (silently)"""
|
||||
def __init__(self, task, logfile, metadata):
|
||||
self.logfile = logfile
|
||||
super(TaskFailedSilent, self).__init__(task, metadata)
|
||||
|
||||
def getDisplayName(self):
|
||||
# Don't need to tell the user it was silent
|
||||
return "Failed"
|
||||
@@ -109,7 +113,7 @@ class TaskFailedSilent(TaskBase):
|
||||
class TaskInvalid(TaskBase):
|
||||
|
||||
def __init__(self, task, metadata):
|
||||
super(TaskInvalid, self).__init__(task, None, metadata)
|
||||
super(TaskInvalid, self).__init__(task, metadata)
|
||||
self._message = "No such task '%s'" % task
|
||||
|
||||
|
||||
@@ -138,68 +142,54 @@ class LogTee(object):
|
||||
def exec_func(func, d, dirs = None):
|
||||
"""Execute an BB 'function'"""
|
||||
|
||||
body = d.getVar(func)
|
||||
body = data.getVar(func, d)
|
||||
if not body:
|
||||
if body is None:
|
||||
logger.warn("Function %s doesn't exist", func)
|
||||
return
|
||||
|
||||
flags = d.getVarFlags(func)
|
||||
flags = data.getVarFlags(func, d)
|
||||
cleandirs = flags.get('cleandirs')
|
||||
if cleandirs:
|
||||
for cdir in d.expand(cleandirs).split():
|
||||
for cdir in data.expand(cleandirs, d).split():
|
||||
bb.utils.remove(cdir, True)
|
||||
|
||||
if dirs is None:
|
||||
dirs = flags.get('dirs')
|
||||
if dirs:
|
||||
dirs = d.expand(dirs).split()
|
||||
dirs = data.expand(dirs, d).split()
|
||||
|
||||
if dirs:
|
||||
for adir in dirs:
|
||||
bb.utils.mkdirhier(adir)
|
||||
adir = dirs[-1]
|
||||
else:
|
||||
adir = d.getVar('B', True)
|
||||
adir = data.getVar('B', d, 1)
|
||||
bb.utils.mkdirhier(adir)
|
||||
|
||||
ispython = flags.get('python')
|
||||
|
||||
lockflag = flags.get('lockfiles')
|
||||
if lockflag:
|
||||
lockfiles = [d.expand(f) for f in lockflag.split()]
|
||||
lockfiles = [data.expand(f, d) for f in lockflag.split()]
|
||||
else:
|
||||
lockfiles = None
|
||||
|
||||
tempdir = d.getVar('T', True)
|
||||
tempdir = data.getVar('T', d, 1)
|
||||
|
||||
# or func allows items to be executed outside of the normal
|
||||
# task set, such as buildhistory
|
||||
task = d.getVar('BB_RUNTASK', True) or func
|
||||
task = data.getVar('BB_RUNTASK', d, 1) or func
|
||||
if task == func:
|
||||
taskfunc = task
|
||||
else:
|
||||
taskfunc = "%s.%s" % (task, func)
|
||||
|
||||
runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
|
||||
runfmt = data.getVar('BB_RUNFMT', d, 1) or "run.{func}.{pid}"
|
||||
runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
|
||||
runfile = os.path.join(tempdir, runfn)
|
||||
bb.utils.mkdirhier(os.path.dirname(runfile))
|
||||
|
||||
# Setup the courtesy link to the runfn, only for tasks
|
||||
# we create the link 'just' before the run script is created
|
||||
# if we create it after, and if the run script fails, then the
|
||||
# link won't be created as an exception would be fired.
|
||||
if task == func:
|
||||
runlink = os.path.join(tempdir, 'run.{0}'.format(task))
|
||||
if runlink:
|
||||
bb.utils.remove(runlink)
|
||||
|
||||
try:
|
||||
os.symlink(runfn, runlink)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
with bb.utils.fileslocked(lockfiles):
|
||||
if ispython:
|
||||
exec_func_python(func, d, runfile, cwd=adir)
|
||||
@@ -260,37 +250,14 @@ def exec_func_shell(func, d, runfile, cwd=None):
|
||||
d.delVarFlag('PWD', 'export')
|
||||
|
||||
with open(runfile, 'w') as script:
|
||||
script.write('''#!/bin/sh\n
|
||||
# Emit a useful diagnostic if something fails:
|
||||
bb_exit_handler() {
|
||||
ret=$?
|
||||
case $ret in
|
||||
0) ;;
|
||||
*) case $BASH_VERSION in
|
||||
"") echo "WARNING: exit code $ret from a shell command.";;
|
||||
*) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from
|
||||
\"$BASH_COMMAND\"";;
|
||||
esac
|
||||
exit $ret
|
||||
esac
|
||||
}
|
||||
trap 'bb_exit_handler' 0
|
||||
set -e
|
||||
''')
|
||||
|
||||
bb.data.emit_func(func, script, d)
|
||||
script.write('#!/bin/sh -e\n')
|
||||
data.emit_func(func, script, d)
|
||||
|
||||
if bb.msg.loggerVerboseLogs:
|
||||
script.write("set -x\n")
|
||||
if cwd:
|
||||
script.write("cd %s\n" % cwd)
|
||||
script.write("%s\n" % func)
|
||||
script.write('''
|
||||
# cleanup
|
||||
ret=$?
|
||||
trap '' 0
|
||||
exit $?
|
||||
''')
|
||||
|
||||
os.chmod(runfile, 0775)
|
||||
|
||||
@@ -308,8 +275,7 @@ exit $?
|
||||
bb.debug(2, "Executing shell function %s" % func)
|
||||
|
||||
try:
|
||||
with open(os.devnull, 'r+') as stdin:
|
||||
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile)
|
||||
bb.process.run(cmd, shell=False, stdin=NULL, log=logfile)
|
||||
except bb.process.CmdError:
|
||||
logfn = d.getVar('BB_LOGFILE', True)
|
||||
raise FuncFailed(func, logfn)
|
||||
@@ -317,13 +283,13 @@ exit $?
|
||||
bb.debug(2, "Shell function %s finished" % func)
|
||||
|
||||
def _task_data(fn, task, d):
|
||||
localdata = bb.data.createCopy(d)
|
||||
localdata = data.createCopy(d)
|
||||
localdata.setVar('BB_FILENAME', fn)
|
||||
localdata.setVar('BB_CURRENTTASK', task[3:])
|
||||
localdata.setVar('OVERRIDES', 'task-%s:%s' %
|
||||
(task[3:], d.getVar('OVERRIDES', False)))
|
||||
localdata.finalize()
|
||||
bb.data.expandKeys(localdata)
|
||||
data.expandKeys(localdata)
|
||||
return localdata
|
||||
|
||||
def _exec_task(fn, task, d, quieterr):
|
||||
@@ -332,7 +298,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
Execution of a task involves a bit more setup than executing a function,
|
||||
running it with its own local metadata, and with some useful variables set.
|
||||
"""
|
||||
if not d.getVarFlag(task, 'task'):
|
||||
if not data.getVarFlag(task, 'task', d):
|
||||
event.fire(TaskInvalid(task, d), d)
|
||||
logger.error("No such task: %s" % task)
|
||||
return 1
|
||||
@@ -353,11 +319,12 @@ def _exec_task(fn, task, d, quieterr):
|
||||
# Document the order of the tasks...
|
||||
logorder = os.path.join(tempdir, 'log.task_order')
|
||||
try:
|
||||
with open(logorder, 'a') as logorderfile:
|
||||
logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
|
||||
logorderfile = file(logorder, 'a')
|
||||
except OSError:
|
||||
logger.exception("Opening log file '%s'", logorder)
|
||||
pass
|
||||
logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
|
||||
logorderfile.close()
|
||||
|
||||
# Setup the courtesy link to the logfn
|
||||
loglink = os.path.join(tempdir, 'log.{0}'.format(task))
|
||||
@@ -381,10 +348,10 @@ def _exec_task(fn, task, d, quieterr):
|
||||
self.triggered = True
|
||||
|
||||
# Handle logfiles
|
||||
si = open('/dev/null', 'r')
|
||||
si = file('/dev/null', 'r')
|
||||
try:
|
||||
bb.utils.mkdirhier(os.path.dirname(logfn))
|
||||
logfile = open(logfn, 'w')
|
||||
logfile = file(logfn, 'w')
|
||||
except OSError:
|
||||
logger.exception("Opening log file '%s'", logfn)
|
||||
pass
|
||||
@@ -412,7 +379,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
localdata.setVar('BB_LOGFILE', logfn)
|
||||
localdata.setVar('BB_RUNTASK', task)
|
||||
|
||||
event.fire(TaskStarted(task, logfn, localdata), localdata)
|
||||
event.fire(TaskStarted(task, localdata), localdata)
|
||||
try:
|
||||
for func in (prefuncs or '').split():
|
||||
exec_func(func, localdata)
|
||||
@@ -449,7 +416,7 @@ def _exec_task(fn, task, d, quieterr):
|
||||
logger.debug(2, "Zero size logfn %s, removing", logfn)
|
||||
bb.utils.remove(logfn)
|
||||
bb.utils.remove(loglink)
|
||||
event.fire(TaskSucceeded(task, logfn, localdata), localdata)
|
||||
event.fire(TaskSucceeded(task, localdata), localdata)
|
||||
|
||||
if not localdata.getVarFlag(task, 'nostamp') and not localdata.getVarFlag(task, 'selfstamp'):
|
||||
make_stamp(task, localdata)
|
||||
@@ -463,7 +430,7 @@ def exec_task(fn, task, d, profile = False):
|
||||
quieterr = True
|
||||
|
||||
if profile:
|
||||
profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
|
||||
profname = "profile-%s.log" % (os.path.basename(fn) + "-" + task)
|
||||
try:
|
||||
import cProfile as profile
|
||||
except:
|
||||
@@ -566,7 +533,8 @@ def make_stamp(task, d, file_name = None):
|
||||
# change on broken NFS filesystems
|
||||
if stamp:
|
||||
bb.utils.remove(stamp)
|
||||
open(stamp, "w").close()
|
||||
f = open(stamp, "w")
|
||||
f.close()
|
||||
|
||||
# If we're in task context, write out a signature file for each task
|
||||
# as it completes
|
||||
@@ -608,7 +576,7 @@ def stampfile(taskname, d, file_name = None):
|
||||
return stamp_internal(taskname, d, file_name)
|
||||
|
||||
def add_tasks(tasklist, d):
|
||||
task_deps = d.getVar('_task_deps')
|
||||
task_deps = data.getVar('_task_deps', d)
|
||||
if not task_deps:
|
||||
task_deps = {}
|
||||
if not 'tasks' in task_deps:
|
||||
@@ -617,25 +585,24 @@ def add_tasks(tasklist, d):
|
||||
task_deps['parents'] = {}
|
||||
|
||||
for task in tasklist:
|
||||
task = d.expand(task)
|
||||
d.setVarFlag(task, 'task', 1)
|
||||
task = data.expand(task, d)
|
||||
data.setVarFlag(task, 'task', 1, d)
|
||||
|
||||
if not task in task_deps['tasks']:
|
||||
task_deps['tasks'].append(task)
|
||||
|
||||
flags = d.getVarFlags(task)
|
||||
flags = data.getVarFlags(task, d)
|
||||
def getTask(name):
|
||||
if not name in task_deps:
|
||||
task_deps[name] = {}
|
||||
if name in flags:
|
||||
deptask = d.expand(flags[name])
|
||||
deptask = data.expand(flags[name], d)
|
||||
task_deps[name][task] = deptask
|
||||
getTask('depends')
|
||||
getTask('rdepends')
|
||||
getTask('deptask')
|
||||
getTask('rdeptask')
|
||||
getTask('recrdeptask')
|
||||
getTask('recideptask')
|
||||
getTask('nostamp')
|
||||
getTask('fakeroot')
|
||||
getTask('noexec')
|
||||
@@ -643,15 +610,15 @@ def add_tasks(tasklist, d):
|
||||
task_deps['parents'][task] = []
|
||||
if 'deps' in flags:
|
||||
for dep in flags['deps']:
|
||||
dep = d.expand(dep)
|
||||
dep = data.expand(dep, d)
|
||||
task_deps['parents'][task].append(dep)
|
||||
|
||||
# don't assume holding a reference
|
||||
d.setVar('_task_deps', task_deps)
|
||||
data.setVar('_task_deps', task_deps, d)
|
||||
|
||||
def remove_task(task, kill, d):
|
||||
"""Remove an BB 'task'.
|
||||
|
||||
If kill is 1, also remove tasks that depend on this task."""
|
||||
|
||||
d.delVarFlag(task, 'task')
|
||||
data.delVarFlag(task, 'task', d)
|
||||
|
||||
@@ -43,7 +43,7 @@ except ImportError:
|
||||
logger.info("Importing cPickle failed. "
|
||||
"Falling back to a very slow implementation.")
|
||||
|
||||
__cache_version__ = "146"
|
||||
__cache_version__ = "145"
|
||||
|
||||
def getCacheFile(path, filename, data_hash):
|
||||
return os.path.join(path, filename + "." + data_hash)
|
||||
@@ -524,15 +524,6 @@ class Cache(object):
|
||||
self.remove(fn)
|
||||
return False
|
||||
|
||||
if hasattr(info_array[0], 'file_checksums'):
|
||||
for _, fl in info_array[0].file_checksums.items():
|
||||
for f in fl.split():
|
||||
if not os.path.exists(f):
|
||||
logger.debug(2, "Cache: %s's file checksum list file %s was removed",
|
||||
fn, f)
|
||||
self.remove(fn)
|
||||
return False
|
||||
|
||||
if appends != info_array[0].appends:
|
||||
logger.debug(2, "Cache: appends for %s changed", fn)
|
||||
logger.debug(2, "%s to %s" % (str(appends), str(info_array[0].appends)))
|
||||
@@ -724,6 +715,7 @@ class CacheData(object):
|
||||
for info in info_array:
|
||||
info.add_cacheData(self, fn)
|
||||
|
||||
|
||||
class MultiProcessCache(object):
|
||||
"""
|
||||
BitBake multi-process cache implementation
|
||||
@@ -745,18 +737,12 @@ class MultiProcessCache(object):
|
||||
self.cachefile = os.path.join(cachedir, self.__class__.cache_file_name)
|
||||
logger.debug(1, "Using cache in '%s'", self.cachefile)
|
||||
|
||||
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
||||
|
||||
try:
|
||||
with open(self.cachefile, "rb") as f:
|
||||
p = pickle.Unpickler(f)
|
||||
data, version = p.load()
|
||||
p = pickle.Unpickler(file(self.cachefile, "rb"))
|
||||
data, version = p.load()
|
||||
except:
|
||||
bb.utils.unlockfile(glf)
|
||||
return
|
||||
|
||||
bb.utils.unlockfile(glf)
|
||||
|
||||
if version != self.__class__.CACHE_VERSION:
|
||||
return
|
||||
|
||||
@@ -793,9 +779,8 @@ class MultiProcessCache(object):
|
||||
i = i + 1
|
||||
continue
|
||||
|
||||
with open(self.cachefile + "-" + str(i), "wb") as f:
|
||||
p = pickle.Pickler(f, -1)
|
||||
p.dump([self.cachedata_extras, self.__class__.CACHE_VERSION])
|
||||
p = pickle.Pickler(file(self.cachefile + "-" + str(i), "wb"), -1)
|
||||
p.dump([self.cachedata_extras, self.__class__.CACHE_VERSION])
|
||||
|
||||
bb.utils.unlockfile(lf)
|
||||
bb.utils.unlockfile(glf)
|
||||
@@ -813,9 +798,8 @@ class MultiProcessCache(object):
|
||||
glf = bb.utils.lockfile(self.cachefile + ".lock")
|
||||
|
||||
try:
|
||||
with open(self.cachefile, "rb") as f:
|
||||
p = pickle.Unpickler(f)
|
||||
data, version = p.load()
|
||||
p = pickle.Unpickler(file(self.cachefile, "rb"))
|
||||
data, version = p.load()
|
||||
except (IOError, EOFError):
|
||||
data, version = None, None
|
||||
|
||||
@@ -825,9 +809,8 @@ class MultiProcessCache(object):
|
||||
for f in [y for y in os.listdir(os.path.dirname(self.cachefile)) if y.startswith(os.path.basename(self.cachefile) + '-')]:
|
||||
f = os.path.join(os.path.dirname(self.cachefile), f)
|
||||
try:
|
||||
with open(f, "rb") as fd:
|
||||
p = pickle.Unpickler(fd)
|
||||
extradata, version = p.load()
|
||||
p = pickle.Unpickler(file(f, "rb"))
|
||||
extradata, version = p.load()
|
||||
except (IOError, EOFError):
|
||||
extradata, version = self.create_cachedata(), None
|
||||
|
||||
@@ -839,9 +822,8 @@ class MultiProcessCache(object):
|
||||
|
||||
self.compress_keys(data)
|
||||
|
||||
with open(self.cachefile, "wb") as f:
|
||||
p = pickle.Pickler(f, -1)
|
||||
p.dump([data, self.__class__.CACHE_VERSION])
|
||||
p = pickle.Pickler(file(self.cachefile, "wb"), -1)
|
||||
p.dump([data, self.__class__.CACHE_VERSION])
|
||||
|
||||
bb.utils.unlockfile(glf)
|
||||
|
||||
|
||||
@@ -59,14 +59,11 @@ class Command:
|
||||
# FIXME Add lock for this
|
||||
self.currentAsyncCommand = None
|
||||
|
||||
def runCommand(self, commandline, ro_only = False):
|
||||
def runCommand(self, commandline):
|
||||
command = commandline.pop(0)
|
||||
if hasattr(CommandsSync, command):
|
||||
# Can run synchronous commands straight away
|
||||
command_method = getattr(self.cmds_sync, command)
|
||||
if ro_only:
|
||||
if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'):
|
||||
return None, "Not able to execute not readonly commands in readonly mode"
|
||||
try:
|
||||
result = command_method(self, commandline)
|
||||
except CommandError as exc:
|
||||
@@ -81,7 +78,7 @@ class Command:
|
||||
if command not in CommandsAsync.__dict__:
|
||||
return None, "No such command"
|
||||
self.currentAsyncCommand = (command, commandline)
|
||||
self.cooker.configuration.server_register_idlecallback(self.cooker.runCommands, self.cooker)
|
||||
self.cooker.server_registration_cb(self.cooker.runCommands, self.cooker)
|
||||
return True, None
|
||||
|
||||
def runAsyncCommand(self):
|
||||
@@ -90,7 +87,8 @@ class Command:
|
||||
(command, options) = self.currentAsyncCommand
|
||||
commandmethod = getattr(CommandsAsync, command)
|
||||
needcache = getattr( commandmethod, "needcache" )
|
||||
if needcache and self.cooker.state != bb.cooker.state.running:
|
||||
if (needcache and self.cooker.state in
|
||||
(bb.cooker.state.initial, bb.cooker.state.parsing)):
|
||||
self.cooker.updateCache()
|
||||
return True
|
||||
else:
|
||||
@@ -117,14 +115,14 @@ class Command:
|
||||
return False
|
||||
|
||||
def finishAsyncCommand(self, msg=None, code=None):
|
||||
if msg or msg == "":
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.event_data)
|
||||
if msg:
|
||||
bb.event.fire(CommandFailed(msg), self.cooker.configuration.event_data)
|
||||
elif code:
|
||||
bb.event.fire(CommandExit(code), self.cooker.event_data)
|
||||
bb.event.fire(CommandExit(code), self.cooker.configuration.event_data)
|
||||
else:
|
||||
bb.event.fire(CommandCompleted(), self.cooker.event_data)
|
||||
bb.event.fire(CommandCompleted(), self.cooker.configuration.event_data)
|
||||
self.currentAsyncCommand = None
|
||||
self.cooker.finishcommand()
|
||||
|
||||
|
||||
class CommandsSync:
|
||||
"""
|
||||
@@ -137,41 +135,44 @@ class CommandsSync:
|
||||
"""
|
||||
Trigger cooker 'shutdown' mode
|
||||
"""
|
||||
command.cooker.shutdown(False)
|
||||
command.cooker.shutdown()
|
||||
|
||||
def stateForceShutdown(self, command, params):
|
||||
def stateStop(self, command, params):
|
||||
"""
|
||||
Stop the cooker
|
||||
"""
|
||||
command.cooker.shutdown(True)
|
||||
command.cooker.stop()
|
||||
|
||||
def getCmdLineAction(self, command, params):
|
||||
"""
|
||||
Get any command parsed from the commandline
|
||||
"""
|
||||
cmd_action = command.cooker.commandlineAction
|
||||
if cmd_action is None:
|
||||
return None
|
||||
elif 'msg' in cmd_action and cmd_action['msg']:
|
||||
raise CommandError(cmd_action['msg'])
|
||||
else:
|
||||
return cmd_action['action']
|
||||
|
||||
def getVariable(self, command, params):
|
||||
"""
|
||||
Read the value of a variable from data
|
||||
Read the value of a variable from configuration.data
|
||||
"""
|
||||
varname = params[0]
|
||||
expand = True
|
||||
if len(params) > 1:
|
||||
expand = (params[1] == "True")
|
||||
expand = params[1]
|
||||
|
||||
return command.cooker.data.getVar(varname, expand)
|
||||
getVariable.readonly = True
|
||||
return command.cooker.configuration.data.getVar(varname, expand)
|
||||
|
||||
def setVariable(self, command, params):
|
||||
"""
|
||||
Set the value of variable in data
|
||||
Set the value of variable in configuration.data
|
||||
"""
|
||||
varname = params[0]
|
||||
value = str(params[1])
|
||||
command.cooker.data.setVar(varname, value)
|
||||
|
||||
def setConfig(self, command, params):
|
||||
"""
|
||||
Set the value of variable in configuration
|
||||
"""
|
||||
varname = params[0]
|
||||
value = str(params[1])
|
||||
setattr(command.cooker.configuration, varname, value)
|
||||
command.cooker.configuration.data.setVar(varname, value)
|
||||
|
||||
def enableDataTracking(self, command, params):
|
||||
"""
|
||||
@@ -203,7 +204,6 @@ class CommandsSync:
|
||||
Get the CPU count on the bitbake server
|
||||
"""
|
||||
return bb.utils.cpu_count()
|
||||
getCpuCount.readonly = True
|
||||
|
||||
def matchFile(self, command, params):
|
||||
fMatch = params[0]
|
||||
@@ -213,38 +213,13 @@ class CommandsSync:
|
||||
image = params[0]
|
||||
base_image = params[1]
|
||||
package_queue = params[2]
|
||||
timestamp = params[3]
|
||||
description = params[4]
|
||||
return command.cooker.generateNewImage(image, base_image,
|
||||
package_queue, timestamp, description)
|
||||
|
||||
def ensureDir(self, command, params):
|
||||
directory = params[0]
|
||||
bb.utils.mkdirhier(directory)
|
||||
return command.cooker.generateNewImage(image, base_image, package_queue)
|
||||
|
||||
def setVarFile(self, command, params):
|
||||
"""
|
||||
Save a variable in a file; used for saving in a configuration file
|
||||
"""
|
||||
var = params[0]
|
||||
val = params[1]
|
||||
default_file = params[2]
|
||||
op = params[3]
|
||||
command.cooker.modifyConfigurationVar(var, val, default_file, op)
|
||||
|
||||
def createConfigFile(self, command, params):
|
||||
"""
|
||||
Create an extra configuration file
|
||||
"""
|
||||
name = params[0]
|
||||
command.cooker.createConfigFile(name)
|
||||
|
||||
def setEventMask(self, command, params):
|
||||
handlerNum = params[0]
|
||||
llevel = params[1]
|
||||
debug_domains = params[2]
|
||||
mask = params[3]
|
||||
return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
|
||||
command.cooker.saveConfigurationVar(var, val, default_file)
|
||||
|
||||
class CommandsAsync:
|
||||
"""
|
||||
@@ -392,11 +367,19 @@ class CommandsAsync:
|
||||
command.finishAsyncCommand()
|
||||
parseFiles.needcache = True
|
||||
|
||||
def reparseFiles(self, command, params):
|
||||
"""
|
||||
Reparse .bb files
|
||||
"""
|
||||
command.cooker.reparseFiles()
|
||||
command.finishAsyncCommand()
|
||||
reparseFiles.needcache = True
|
||||
|
||||
def compareRevisions(self, command, params):
|
||||
"""
|
||||
Parse the .bb files
|
||||
"""
|
||||
if bb.fetch.fetcher_compare_revisions(command.cooker.data):
|
||||
if bb.fetch.fetcher_compare_revisions(command.cooker.configuration.data):
|
||||
command.finishAsyncCommand(code=1)
|
||||
else:
|
||||
command.finishAsyncCommand()
|
||||
@@ -406,11 +389,9 @@ class CommandsAsync:
|
||||
"""
|
||||
Parse the configuration files
|
||||
"""
|
||||
prefiles = params[0].split()
|
||||
postfiles = params[1].split()
|
||||
command.cooker.configuration.prefile = prefiles
|
||||
command.cooker.configuration.postfile = postfiles
|
||||
command.cooker.loadConfigurationData()
|
||||
prefiles = params[0]
|
||||
postfiles = params[1]
|
||||
command.cooker.parseConfigurationFiles(prefiles, postfiles)
|
||||
command.finishAsyncCommand()
|
||||
parseConfigurationFiles.needcache = False
|
||||
|
||||
@@ -419,7 +400,7 @@ class CommandsAsync:
|
||||
Trigger a certain event
|
||||
"""
|
||||
event = params[0]
|
||||
bb.event.fire(eval(event), command.cooker.data)
|
||||
bb.event.fire(eval(event), command.cooker.configuration.data)
|
||||
command.currentAsyncCommand = None
|
||||
triggerEvent.needcache = False
|
||||
|
||||
|
||||
@@ -1,6 +1,928 @@
|
||||
"""Code pulled from future python versions, here for compatibility"""
|
||||
|
||||
from collections import MutableMapping, KeysView, ValuesView, ItemsView, OrderedDict
|
||||
from functools import total_ordering
|
||||
from collections import MutableMapping, KeysView, ValuesView, ItemsView
|
||||
try:
|
||||
from thread import get_ident as _get_ident
|
||||
except ImportError:
|
||||
from dummy_thread import get_ident as _get_ident
|
||||
|
||||
def total_ordering(cls):
|
||||
"""Class decorator that fills in missing ordering methods"""
|
||||
convert = {
|
||||
'__lt__': [('__gt__', lambda self, other: other < self),
|
||||
('__le__', lambda self, other: not other < self),
|
||||
('__ge__', lambda self, other: not self < other)],
|
||||
'__le__': [('__ge__', lambda self, other: other <= self),
|
||||
('__lt__', lambda self, other: not other <= self),
|
||||
('__gt__', lambda self, other: not self <= other)],
|
||||
'__gt__': [('__lt__', lambda self, other: other > self),
|
||||
('__ge__', lambda self, other: not other > self),
|
||||
('__le__', lambda self, other: not self > other)],
|
||||
'__ge__': [('__le__', lambda self, other: other >= self),
|
||||
('__gt__', lambda self, other: not other >= self),
|
||||
('__lt__', lambda self, other: not self >= other)]
|
||||
}
|
||||
roots = set(dir(cls)) & set(convert)
|
||||
if not roots:
|
||||
raise ValueError('must define at least one ordering operation: < > <= >=')
|
||||
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
|
||||
for opname, opfunc in convert[root]:
|
||||
if opname not in roots:
|
||||
opfunc.__name__ = opname
|
||||
opfunc.__doc__ = getattr(int, opname).__doc__
|
||||
setattr(cls, opname, opfunc)
|
||||
return cls
|
||||
|
||||
class OrderedDict(dict):
|
||||
'Dictionary that remembers insertion order'
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as regular dictionaries.
|
||||
|
||||
# The internal self.__map dict maps keys to links in a doubly linked list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. The signature is the same as
|
||||
regular dictionaries, but keyword arguments are not recommended because
|
||||
their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
|
||||
'od.__setitem__(i, y) <==> od[i]=y'
|
||||
# Setting a new item creates a new link at the end of the linked list,
|
||||
# and the inherited dictionary is updated with the new key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[PREV]
|
||||
last[NEXT] = root[PREV] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__):
|
||||
'od.__delitem__(y) <==> del od[y]'
|
||||
# Deleting an existing item uses self.__map to find the link which gets
|
||||
# removed by updating the links in the predecessor and successor nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[NEXT] = link_next
|
||||
link_next[PREV] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
'od.__iter__() <==> iter(od)'
|
||||
# Traverse the linked list in order.
|
||||
NEXT, KEY = 1, 2
|
||||
root = self.__root
|
||||
curr = root[NEXT]
|
||||
while curr is not root:
|
||||
yield curr[KEY]
|
||||
curr = curr[NEXT]
|
||||
|
||||
def __reversed__(self):
|
||||
'od.__reversed__() <==> reversed(od)'
|
||||
# Traverse the linked list in reverse order.
|
||||
PREV, KEY = 0, 2
|
||||
root = self.__root
|
||||
curr = root[PREV]
|
||||
while curr is not root:
|
||||
yield curr[KEY]
|
||||
curr = curr[PREV]
|
||||
|
||||
def clear(self):
|
||||
'od.clear() -> None. Remove all items from od.'
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
dict.clear(self)
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
'od.keys() -> list of keys in od'
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
'od.values() -> list of values in od'
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
'od.items() -> list of (key, value) pairs in od'
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
'od.iterkeys() -> an iterator over the keys in od'
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
'od.itervalues -> an iterator over the values in od'
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
'od.iteritems -> an iterator over the (key, value) pairs in od'
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
update = MutableMapping.update
|
||||
|
||||
__update = update # let subclasses override update without breaking __init__
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding
|
||||
value. If key is not found, d is returned if given, otherwise KeyError
|
||||
is raised.
|
||||
|
||||
'''
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
key = next(reversed(self) if last else iter(self))
|
||||
value = self.pop(key)
|
||||
return key, value
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
'od.__repr__() <==> repr(od)'
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
'Return state information for pickling'
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
'od.copy() -> a shallow copy of od'
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
|
||||
If not specified, the value defaults to None.
|
||||
|
||||
'''
|
||||
self = cls()
|
||||
for key in iterable:
|
||||
self[key] = value
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
|
||||
while comparison to a regular mapping is order-insensitive.
|
||||
|
||||
'''
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self)==len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
'od.__ne__(y) <==> od!=y'
|
||||
return not self == other
|
||||
|
||||
# -- the following methods support python 3.x style dictionary views --
|
||||
|
||||
def viewkeys(self):
|
||||
"od.viewkeys() -> a set-like object providing a view on od's keys"
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self):
|
||||
"od.viewvalues() -> an object providing a view on od's values"
|
||||
return ValuesView(self)
|
||||
|
||||
def viewitems(self):
|
||||
"od.viewitems() -> a set-like object providing a view on od's items"
|
||||
return ItemsView(self)
|
||||
|
||||
# Multiprocessing pool code imported from python 2.7.3. Previous versions of
|
||||
# python have issues in this code which hang pool usage
|
||||
|
||||
#
|
||||
# Module providing the `Pool` class for managing a process pool
|
||||
#
|
||||
# multiprocessing/pool.py
|
||||
#
|
||||
# Copyright (c) 2006-2008, R Oudkerk
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of author nor the names of any contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
import threading
|
||||
import Queue
|
||||
import itertools
|
||||
import collections
|
||||
import time
|
||||
|
||||
import multiprocessing
|
||||
from multiprocessing import Process, cpu_count, TimeoutError, pool
|
||||
from multiprocessing.util import Finalize, debug
|
||||
|
||||
#
|
||||
# Constants representing the state of a pool
|
||||
#
|
||||
|
||||
RUN = 0
|
||||
CLOSE = 1
|
||||
TERMINATE = 2
|
||||
|
||||
#
|
||||
# Miscellaneous
|
||||
#
|
||||
|
||||
def mapstar(args):
|
||||
return map(*args)
|
||||
|
||||
class MaybeEncodingError(Exception):
|
||||
"""Wraps possible unpickleable errors, so they can be
|
||||
safely sent through the socket."""
|
||||
|
||||
def __init__(self, exc, value):
|
||||
self.exc = repr(exc)
|
||||
self.value = repr(value)
|
||||
super(MaybeEncodingError, self).__init__(self.exc, self.value)
|
||||
|
||||
def __str__(self):
|
||||
return "Error sending result: '%s'. Reason: '%s'" % (self.value,
|
||||
self.exc)
|
||||
|
||||
def __repr__(self):
|
||||
return "<MaybeEncodingError: %s>" % str(self)
|
||||
|
||||
def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
|
||||
assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
|
||||
put = outqueue.put
|
||||
get = inqueue.get
|
||||
if hasattr(inqueue, '_writer'):
|
||||
inqueue._writer.close()
|
||||
outqueue._reader.close()
|
||||
|
||||
if initializer is not None:
|
||||
initializer(*initargs)
|
||||
|
||||
completed = 0
|
||||
while maxtasks is None or (maxtasks and completed < maxtasks):
|
||||
try:
|
||||
task = get()
|
||||
except (EOFError, IOError):
|
||||
debug('worker got EOFError or IOError -- exiting')
|
||||
break
|
||||
|
||||
if task is None:
|
||||
debug('worker got sentinel -- exiting')
|
||||
break
|
||||
|
||||
job, i, func, args, kwds = task
|
||||
try:
|
||||
result = (True, func(*args, **kwds))
|
||||
except Exception, e:
|
||||
result = (False, e)
|
||||
try:
|
||||
put((job, i, result))
|
||||
except Exception as e:
|
||||
wrapped = MaybeEncodingError(e, result[1])
|
||||
debug("Possible encoding error while sending result: %s" % (
|
||||
wrapped))
|
||||
put((job, i, (False, wrapped)))
|
||||
completed += 1
|
||||
debug('worker exiting after %d tasks' % completed)
|
||||
|
||||
|
||||
class Pool(object):
|
||||
'''
|
||||
Class which supports an async version of the `apply()` builtin
|
||||
'''
|
||||
Process = Process
|
||||
|
||||
def __init__(self, processes=None, initializer=None, initargs=(),
|
||||
maxtasksperchild=None):
|
||||
self._setup_queues()
|
||||
self._taskqueue = Queue.Queue()
|
||||
self._cache = {}
|
||||
self._state = RUN
|
||||
self._maxtasksperchild = maxtasksperchild
|
||||
self._initializer = initializer
|
||||
self._initargs = initargs
|
||||
|
||||
if processes is None:
|
||||
try:
|
||||
processes = cpu_count()
|
||||
except NotImplementedError:
|
||||
processes = 1
|
||||
if processes < 1:
|
||||
raise ValueError("Number of processes must be at least 1")
|
||||
|
||||
if initializer is not None and not hasattr(initializer, '__call__'):
|
||||
raise TypeError('initializer must be a callable')
|
||||
|
||||
self._processes = processes
|
||||
self._pool = []
|
||||
self._repopulate_pool()
|
||||
|
||||
self._worker_handler = threading.Thread(
|
||||
target=Pool._handle_workers,
|
||||
args=(self, )
|
||||
)
|
||||
self._worker_handler.daemon = True
|
||||
self._worker_handler._state = RUN
|
||||
self._worker_handler.start()
|
||||
|
||||
|
||||
self._task_handler = threading.Thread(
|
||||
target=Pool._handle_tasks,
|
||||
args=(self._taskqueue, self._quick_put, self._outqueue, self._pool)
|
||||
)
|
||||
self._task_handler.daemon = True
|
||||
self._task_handler._state = RUN
|
||||
self._task_handler.start()
|
||||
|
||||
self._result_handler = threading.Thread(
|
||||
target=Pool._handle_results,
|
||||
args=(self._outqueue, self._quick_get, self._cache)
|
||||
)
|
||||
self._result_handler.daemon = True
|
||||
self._result_handler._state = RUN
|
||||
self._result_handler.start()
|
||||
|
||||
self._terminate = Finalize(
|
||||
self, self._terminate_pool,
|
||||
args=(self._taskqueue, self._inqueue, self._outqueue, self._pool,
|
||||
self._worker_handler, self._task_handler,
|
||||
self._result_handler, self._cache),
|
||||
exitpriority=15
|
||||
)
|
||||
|
||||
def _join_exited_workers(self):
|
||||
"""Cleanup after any worker processes which have exited due to reaching
|
||||
their specified lifetime. Returns True if any workers were cleaned up.
|
||||
"""
|
||||
cleaned = False
|
||||
for i in reversed(range(len(self._pool))):
|
||||
worker = self._pool[i]
|
||||
if worker.exitcode is not None:
|
||||
# worker exited
|
||||
debug('cleaning up worker %d' % i)
|
||||
worker.join()
|
||||
cleaned = True
|
||||
del self._pool[i]
|
||||
return cleaned
|
||||
|
||||
def _repopulate_pool(self):
|
||||
"""Bring the number of pool processes up to the specified number,
|
||||
for use after reaping workers which have exited.
|
||||
"""
|
||||
for i in range(self._processes - len(self._pool)):
|
||||
w = self.Process(target=worker,
|
||||
args=(self._inqueue, self._outqueue,
|
||||
self._initializer,
|
||||
self._initargs, self._maxtasksperchild)
|
||||
)
|
||||
self._pool.append(w)
|
||||
w.name = w.name.replace('Process', 'PoolWorker')
|
||||
w.daemon = True
|
||||
w.start()
|
||||
debug('added worker')
|
||||
|
||||
def _maintain_pool(self):
|
||||
"""Clean up any exited workers and start replacements for them.
|
||||
"""
|
||||
if self._join_exited_workers():
|
||||
self._repopulate_pool()
|
||||
|
||||
def _setup_queues(self):
|
||||
from multiprocessing.queues import SimpleQueue
|
||||
self._inqueue = SimpleQueue()
|
||||
self._outqueue = SimpleQueue()
|
||||
self._quick_put = self._inqueue._writer.send
|
||||
self._quick_get = self._outqueue._reader.recv
|
||||
|
||||
def apply(self, func, args=(), kwds={}):
|
||||
'''
|
||||
Equivalent of `apply()` builtin
|
||||
'''
|
||||
assert self._state == RUN
|
||||
return self.apply_async(func, args, kwds).get()
|
||||
|
||||
def map(self, func, iterable, chunksize=None):
|
||||
'''
|
||||
Equivalent of `map()` builtin
|
||||
'''
|
||||
assert self._state == RUN
|
||||
return self.map_async(func, iterable, chunksize).get()
|
||||
|
||||
def imap(self, func, iterable, chunksize=1):
|
||||
'''
|
||||
Equivalent of `itertools.imap()` -- can be MUCH slower than `Pool.map()`
|
||||
'''
|
||||
assert self._state == RUN
|
||||
if chunksize == 1:
|
||||
result = IMapIterator(self._cache)
|
||||
self._taskqueue.put((((result._job, i, func, (x,), {})
|
||||
for i, x in enumerate(iterable)), result._set_length))
|
||||
return result
|
||||
else:
|
||||
assert chunksize > 1
|
||||
task_batches = Pool._get_tasks(func, iterable, chunksize)
|
||||
result = IMapIterator(self._cache)
|
||||
self._taskqueue.put((((result._job, i, mapstar, (x,), {})
|
||||
for i, x in enumerate(task_batches)), result._set_length))
|
||||
return (item for chunk in result for item in chunk)
|
||||
|
||||
def imap_unordered(self, func, iterable, chunksize=1):
|
||||
'''
|
||||
Like `imap()` method but ordering of results is arbitrary
|
||||
'''
|
||||
assert self._state == RUN
|
||||
if chunksize == 1:
|
||||
result = IMapUnorderedIterator(self._cache)
|
||||
self._taskqueue.put((((result._job, i, func, (x,), {})
|
||||
for i, x in enumerate(iterable)), result._set_length))
|
||||
return result
|
||||
else:
|
||||
assert chunksize > 1
|
||||
task_batches = Pool._get_tasks(func, iterable, chunksize)
|
||||
result = IMapUnorderedIterator(self._cache)
|
||||
self._taskqueue.put((((result._job, i, mapstar, (x,), {})
|
||||
for i, x in enumerate(task_batches)), result._set_length))
|
||||
return (item for chunk in result for item in chunk)
|
||||
|
||||
def apply_async(self, func, args=(), kwds={}, callback=None):
|
||||
'''
|
||||
Asynchronous equivalent of `apply()` builtin
|
||||
'''
|
||||
assert self._state == RUN
|
||||
result = ApplyResult(self._cache, callback)
|
||||
self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
|
||||
return result
|
||||
|
||||
def map_async(self, func, iterable, chunksize=None, callback=None):
|
||||
'''
|
||||
Asynchronous equivalent of `map()` builtin
|
||||
'''
|
||||
assert self._state == RUN
|
||||
if not hasattr(iterable, '__len__'):
|
||||
iterable = list(iterable)
|
||||
|
||||
if chunksize is None:
|
||||
chunksize, extra = divmod(len(iterable), len(self._pool) * 4)
|
||||
if extra:
|
||||
chunksize += 1
|
||||
if len(iterable) == 0:
|
||||
chunksize = 0
|
||||
|
||||
task_batches = Pool._get_tasks(func, iterable, chunksize)
|
||||
result = MapResult(self._cache, chunksize, len(iterable), callback)
|
||||
self._taskqueue.put((((result._job, i, mapstar, (x,), {})
|
||||
for i, x in enumerate(task_batches)), None))
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _handle_workers(pool):
|
||||
thread = threading.current_thread()
|
||||
|
||||
# Keep maintaining workers until the cache gets drained, unless the pool
|
||||
# is terminated.
|
||||
while thread._state == RUN or (pool._cache and thread._state != TERMINATE):
|
||||
pool._maintain_pool()
|
||||
time.sleep(0.1)
|
||||
# send sentinel to stop workers
|
||||
pool._taskqueue.put(None)
|
||||
debug('worker handler exiting')
|
||||
|
||||
@staticmethod
|
||||
def _handle_tasks(taskqueue, put, outqueue, pool):
|
||||
thread = threading.current_thread()
|
||||
|
||||
for taskseq, set_length in iter(taskqueue.get, None):
|
||||
i = -1
|
||||
for i, task in enumerate(taskseq):
|
||||
if thread._state:
|
||||
debug('task handler found thread._state != RUN')
|
||||
break
|
||||
try:
|
||||
put(task)
|
||||
except IOError:
|
||||
debug('could not put task on queue')
|
||||
break
|
||||
else:
|
||||
if set_length:
|
||||
debug('doing set_length()')
|
||||
set_length(i+1)
|
||||
continue
|
||||
break
|
||||
else:
|
||||
debug('task handler got sentinel')
|
||||
|
||||
|
||||
try:
|
||||
# tell result handler to finish when cache is empty
|
||||
debug('task handler sending sentinel to result handler')
|
||||
outqueue.put(None)
|
||||
|
||||
# tell workers there is no more work
|
||||
debug('task handler sending sentinel to workers')
|
||||
for p in pool:
|
||||
put(None)
|
||||
except IOError:
|
||||
debug('task handler got IOError when sending sentinels')
|
||||
|
||||
debug('task handler exiting')
|
||||
|
||||
@staticmethod
|
||||
def _handle_results(outqueue, get, cache):
|
||||
thread = threading.current_thread()
|
||||
|
||||
while 1:
|
||||
try:
|
||||
task = get()
|
||||
except (IOError, EOFError):
|
||||
debug('result handler got EOFError/IOError -- exiting')
|
||||
return
|
||||
|
||||
if thread._state:
|
||||
assert thread._state == TERMINATE
|
||||
debug('result handler found thread._state=TERMINATE')
|
||||
break
|
||||
|
||||
if task is None:
|
||||
debug('result handler got sentinel')
|
||||
break
|
||||
|
||||
job, i, obj = task
|
||||
try:
|
||||
cache[job]._set(i, obj)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
while cache and thread._state != TERMINATE:
|
||||
try:
|
||||
task = get()
|
||||
except (IOError, EOFError):
|
||||
debug('result handler got EOFError/IOError -- exiting')
|
||||
return
|
||||
|
||||
if task is None:
|
||||
debug('result handler ignoring extra sentinel')
|
||||
continue
|
||||
job, i, obj = task
|
||||
try:
|
||||
cache[job]._set(i, obj)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if hasattr(outqueue, '_reader'):
|
||||
debug('ensuring that outqueue is not full')
|
||||
# If we don't make room available in outqueue then
|
||||
# attempts to add the sentinel (None) to outqueue may
|
||||
# block. There is guaranteed to be no more than 2 sentinels.
|
||||
try:
|
||||
for i in range(10):
|
||||
if not outqueue._reader.poll():
|
||||
break
|
||||
get()
|
||||
except (IOError, EOFError):
|
||||
pass
|
||||
|
||||
debug('result handler exiting: len(cache)=%s, thread._state=%s',
|
||||
len(cache), thread._state)
|
||||
|
||||
@staticmethod
|
||||
def _get_tasks(func, it, size):
|
||||
it = iter(it)
|
||||
while 1:
|
||||
x = tuple(itertools.islice(it, size))
|
||||
if not x:
|
||||
return
|
||||
yield (func, x)
|
||||
|
||||
def __reduce__(self):
|
||||
raise NotImplementedError(
|
||||
'pool objects cannot be passed between processes or pickled'
|
||||
)
|
||||
|
||||
def close(self):
|
||||
debug('closing pool')
|
||||
if self._state == RUN:
|
||||
self._state = CLOSE
|
||||
self._worker_handler._state = CLOSE
|
||||
|
||||
def terminate(self):
|
||||
debug('terminating pool')
|
||||
self._state = TERMINATE
|
||||
self._worker_handler._state = TERMINATE
|
||||
self._terminate()
|
||||
|
||||
def join(self):
|
||||
debug('joining pool')
|
||||
assert self._state in (CLOSE, TERMINATE)
|
||||
self._worker_handler.join()
|
||||
self._task_handler.join()
|
||||
self._result_handler.join()
|
||||
for p in self._pool:
|
||||
p.join()
|
||||
|
||||
@staticmethod
|
||||
def _help_stuff_finish(inqueue, task_handler, size):
|
||||
# task_handler may be blocked trying to put items on inqueue
|
||||
debug('removing tasks from inqueue until task handler finished')
|
||||
inqueue._rlock.acquire()
|
||||
while task_handler.is_alive() and inqueue._reader.poll():
|
||||
inqueue._reader.recv()
|
||||
time.sleep(0)
|
||||
|
||||
@classmethod
|
||||
def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool,
|
||||
worker_handler, task_handler, result_handler, cache):
|
||||
# this is guaranteed to only be called once
|
||||
debug('finalizing pool')
|
||||
|
||||
worker_handler._state = TERMINATE
|
||||
task_handler._state = TERMINATE
|
||||
|
||||
debug('helping task handler/workers to finish')
|
||||
cls._help_stuff_finish(inqueue, task_handler, len(pool))
|
||||
|
||||
assert result_handler.is_alive() or len(cache) == 0
|
||||
|
||||
result_handler._state = TERMINATE
|
||||
outqueue.put(None) # sentinel
|
||||
|
||||
# We must wait for the worker handler to exit before terminating
|
||||
# workers because we don't want workers to be restarted behind our back.
|
||||
debug('joining worker handler')
|
||||
if threading.current_thread() is not worker_handler:
|
||||
worker_handler.join(1e100)
|
||||
|
||||
# Terminate workers which haven't already finished.
|
||||
if pool and hasattr(pool[0], 'terminate'):
|
||||
debug('terminating workers')
|
||||
for p in pool:
|
||||
if p.exitcode is None:
|
||||
p.terminate()
|
||||
|
||||
debug('joining task handler')
|
||||
if threading.current_thread() is not task_handler:
|
||||
task_handler.join(1e100)
|
||||
|
||||
debug('joining result handler')
|
||||
if threading.current_thread() is not result_handler:
|
||||
result_handler.join(1e100)
|
||||
|
||||
if pool and hasattr(pool[0], 'terminate'):
|
||||
debug('joining pool workers')
|
||||
for p in pool:
|
||||
if p.is_alive():
|
||||
# worker has not yet exited
|
||||
debug('cleaning up worker %d' % p.pid)
|
||||
p.join()
|
||||
|
||||
class ApplyResult(object):
|
||||
|
||||
def __init__(self, cache, callback):
|
||||
self._cond = threading.Condition(threading.Lock())
|
||||
self._job = multiprocessing.pool.job_counter.next()
|
||||
self._cache = cache
|
||||
self._ready = False
|
||||
self._callback = callback
|
||||
cache[self._job] = self
|
||||
|
||||
def ready(self):
|
||||
return self._ready
|
||||
|
||||
def successful(self):
|
||||
assert self._ready
|
||||
return self._success
|
||||
|
||||
def wait(self, timeout=None):
|
||||
self._cond.acquire()
|
||||
try:
|
||||
if not self._ready:
|
||||
self._cond.wait(timeout)
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
def get(self, timeout=None):
|
||||
self.wait(timeout)
|
||||
if not self._ready:
|
||||
raise TimeoutError
|
||||
if self._success:
|
||||
return self._value
|
||||
else:
|
||||
raise self._value
|
||||
|
||||
def _set(self, i, obj):
|
||||
self._success, self._value = obj
|
||||
if self._callback and self._success:
|
||||
self._callback(self._value)
|
||||
self._cond.acquire()
|
||||
try:
|
||||
self._ready = True
|
||||
self._cond.notify()
|
||||
finally:
|
||||
self._cond.release()
|
||||
del self._cache[self._job]
|
||||
|
||||
#
|
||||
# Class whose instances are returned by `Pool.map_async()`
|
||||
#
|
||||
|
||||
class MapResult(ApplyResult):
|
||||
|
||||
def __init__(self, cache, chunksize, length, callback):
|
||||
ApplyResult.__init__(self, cache, callback)
|
||||
self._success = True
|
||||
self._value = [None] * length
|
||||
self._chunksize = chunksize
|
||||
if chunksize <= 0:
|
||||
self._number_left = 0
|
||||
self._ready = True
|
||||
del cache[self._job]
|
||||
else:
|
||||
self._number_left = length//chunksize + bool(length % chunksize)
|
||||
|
||||
def _set(self, i, success_result):
|
||||
success, result = success_result
|
||||
if success:
|
||||
self._value[i*self._chunksize:(i+1)*self._chunksize] = result
|
||||
self._number_left -= 1
|
||||
if self._number_left == 0:
|
||||
if self._callback:
|
||||
self._callback(self._value)
|
||||
del self._cache[self._job]
|
||||
self._cond.acquire()
|
||||
try:
|
||||
self._ready = True
|
||||
self._cond.notify()
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
else:
|
||||
self._success = False
|
||||
self._value = result
|
||||
del self._cache[self._job]
|
||||
self._cond.acquire()
|
||||
try:
|
||||
self._ready = True
|
||||
self._cond.notify()
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
#
|
||||
# Class whose instances are returned by `Pool.imap()`
|
||||
#
|
||||
|
||||
class IMapIterator(object):
|
||||
|
||||
def __init__(self, cache):
|
||||
self._cond = threading.Condition(threading.Lock())
|
||||
self._job = multiprocessing.pool.job_counter.next()
|
||||
self._cache = cache
|
||||
self._items = collections.deque()
|
||||
self._index = 0
|
||||
self._length = None
|
||||
self._unsorted = {}
|
||||
cache[self._job] = self
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self, timeout=None):
|
||||
self._cond.acquire()
|
||||
try:
|
||||
try:
|
||||
item = self._items.popleft()
|
||||
except IndexError:
|
||||
if self._index == self._length:
|
||||
raise StopIteration
|
||||
self._cond.wait(timeout)
|
||||
try:
|
||||
item = self._items.popleft()
|
||||
except IndexError:
|
||||
if self._index == self._length:
|
||||
raise StopIteration
|
||||
raise TimeoutError
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
success, value = item
|
||||
if success:
|
||||
return value
|
||||
raise value
|
||||
|
||||
__next__ = next # XXX
|
||||
|
||||
def _set(self, i, obj):
|
||||
self._cond.acquire()
|
||||
try:
|
||||
if self._index == i:
|
||||
self._items.append(obj)
|
||||
self._index += 1
|
||||
while self._index in self._unsorted:
|
||||
obj = self._unsorted.pop(self._index)
|
||||
self._items.append(obj)
|
||||
self._index += 1
|
||||
self._cond.notify()
|
||||
else:
|
||||
self._unsorted[i] = obj
|
||||
|
||||
if self._index == self._length:
|
||||
del self._cache[self._job]
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
def _set_length(self, length):
|
||||
self._cond.acquire()
|
||||
try:
|
||||
self._length = length
|
||||
if self._index == self._length:
|
||||
self._cond.notify()
|
||||
del self._cache[self._job]
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
#
|
||||
# Class whose instances are returned by `Pool.imap_unordered()`
|
||||
#
|
||||
|
||||
class IMapUnorderedIterator(IMapIterator):
|
||||
|
||||
def _set(self, i, obj):
|
||||
self._cond.acquire()
|
||||
try:
|
||||
self._items.append(obj)
|
||||
self._index += 1
|
||||
self._cond.notify()
|
||||
if self._index == self._length:
|
||||
del self._cache[self._job]
|
||||
finally:
|
||||
self._cond.release()
|
||||
|
||||
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# ex:ts=4:sw=4:sts=4:et
|
||||
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# Copyright (C) 2003, 2004 Chris Larson
|
||||
# Copyright (C) 2003, 2004 Phil Blundell
|
||||
# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2005 Holger Hans Peter Freyther
|
||||
# Copyright (C) 2005 ROAD GmbH
|
||||
# Copyright (C) 2006 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os, sys
|
||||
from functools import wraps
|
||||
import logging
|
||||
import bb
|
||||
from bb import data
|
||||
import bb.parse
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
parselog = logging.getLogger("BitBake.Parsing")
|
||||
|
||||
class ConfigParameters(object):
|
||||
def __init__(self):
|
||||
self.options, targets = self.parseCommandLine()
|
||||
self.environment = self.parseEnvironment()
|
||||
|
||||
self.options.pkgs_to_build = targets or []
|
||||
|
||||
self.options.tracking = False
|
||||
if hasattr(self.options, "show_environment") and self.options.show_environment:
|
||||
self.options.tracking = True
|
||||
|
||||
for key, val in self.options.__dict__.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
def parseCommandLine(self):
|
||||
raise Exception("Caller must implement commandline option parsing")
|
||||
|
||||
def parseEnvironment(self):
|
||||
return os.environ.copy()
|
||||
|
||||
def updateFromServer(self, server):
|
||||
if not self.options.cmd:
|
||||
defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
|
||||
if error:
|
||||
raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
|
||||
self.options.cmd = defaulttask or "build"
|
||||
_, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
|
||||
if error:
|
||||
raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
|
||||
|
||||
if not self.options.pkgs_to_build:
|
||||
bbpkgs, error = server.runCommand(["getVariable", "BBPKGS"])
|
||||
if error:
|
||||
raise Exception("Unable to get the value of BBPKGS from the server: %s" % error)
|
||||
if bbpkgs:
|
||||
self.options.pkgs_to_build.extend(bbpkgs.split())
|
||||
|
||||
def parseActions(self):
|
||||
# Parse any commandline into actions
|
||||
action = {'action':None, 'msg':None}
|
||||
if self.options.show_environment:
|
||||
if 'world' in self.options.pkgs_to_build:
|
||||
action['msg'] = "'world' is not a valid target for --environment."
|
||||
elif 'universe' in self.options.pkgs_to_build:
|
||||
action['msg'] = "'universe' is not a valid target for --environment."
|
||||
elif len(self.options.pkgs_to_build) > 1:
|
||||
action['msg'] = "Only one target can be used with the --environment option."
|
||||
elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
|
||||
action['msg'] = "No target should be used with the --environment and --buildfile options."
|
||||
elif len(self.options.pkgs_to_build) > 0:
|
||||
action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
|
||||
else:
|
||||
action['action'] = ["showEnvironment", self.options.buildfile]
|
||||
elif self.options.buildfile is not None:
|
||||
action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
|
||||
elif self.options.revisions_changed:
|
||||
action['action'] = ["compareRevisions"]
|
||||
elif self.options.show_versions:
|
||||
action['action'] = ["showVersions"]
|
||||
elif self.options.parse_only:
|
||||
action['action'] = ["parseFiles"]
|
||||
elif self.options.dot_graph:
|
||||
if self.options.pkgs_to_build:
|
||||
action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
|
||||
else:
|
||||
action['msg'] = "Please specify a package name for dependency graph generation."
|
||||
else:
|
||||
if self.options.pkgs_to_build:
|
||||
action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
|
||||
else:
|
||||
#action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
|
||||
action = None
|
||||
self.options.initialaction = action
|
||||
return action
|
||||
|
||||
class CookerConfiguration(object):
|
||||
"""
|
||||
Manages build options and configurations for one run
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.debug_domains = []
|
||||
self.extra_assume_provided = []
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.debug = 0
|
||||
self.cmd = None
|
||||
self.abort = True
|
||||
self.force = False
|
||||
self.profile = False
|
||||
self.nosetscene = False
|
||||
self.invalidate_stamp = False
|
||||
self.dump_signatures = False
|
||||
self.dry_run = False
|
||||
self.tracking = False
|
||||
self.extra_caches = []
|
||||
|
||||
self.env = {}
|
||||
|
||||
def setConfigParameters(self, parameters):
|
||||
for key in self.__dict__.keys():
|
||||
if key in parameters.options.__dict__:
|
||||
setattr(self, key, parameters.options.__dict__[key])
|
||||
self.env = parameters.environment.copy()
|
||||
self.tracking = parameters.tracking
|
||||
|
||||
def setServerRegIdleCallback(self, srcb):
|
||||
self.server_register_idlecallback = srcb
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
for key in self.__dict__.keys():
|
||||
if key == "server_register_idlecallback":
|
||||
state[key] = None
|
||||
else:
|
||||
state[key] = getattr(self, key)
|
||||
return state
|
||||
|
||||
def __setstate__(self,state):
|
||||
for k in state:
|
||||
setattr(self, k, state[k])
|
||||
|
||||
|
||||
def catch_parse_error(func):
|
||||
"""Exception handling bits for our parsing"""
|
||||
@wraps(func)
|
||||
def wrapped(fn, *args):
|
||||
try:
|
||||
return func(fn, *args)
|
||||
except (IOError, bb.parse.ParseError, bb.data_smart.ExpansionError) as exc:
|
||||
import traceback
|
||||
parselog.critical( traceback.format_exc())
|
||||
parselog.critical("Unable to parse %s: %s" % (fn, exc))
|
||||
sys.exit(1)
|
||||
return wrapped
|
||||
|
||||
@catch_parse_error
|
||||
def parse_config_file(fn, data, include=True):
|
||||
return bb.parse.handle(fn, data, include)
|
||||
|
||||
@catch_parse_error
|
||||
def _inherit(bbclass, data):
|
||||
bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
|
||||
return data
|
||||
|
||||
def findConfigFile(configfile, data):
|
||||
search = []
|
||||
bbpath = data.getVar("BBPATH", True)
|
||||
if bbpath:
|
||||
for i in bbpath.split(":"):
|
||||
search.append(os.path.join(i, "conf", configfile))
|
||||
path = os.getcwd()
|
||||
while path != "/":
|
||||
search.append(os.path.join(path, "conf", configfile))
|
||||
path, _ = os.path.split(path)
|
||||
|
||||
for i in search:
|
||||
if os.path.exists(i):
|
||||
return i
|
||||
|
||||
return None
|
||||
|
||||
class CookerDataBuilder(object):
|
||||
|
||||
def __init__(self, cookercfg, worker = False):
|
||||
|
||||
self.prefiles = cookercfg.prefile
|
||||
self.postfiles = cookercfg.postfile
|
||||
self.tracking = cookercfg.tracking
|
||||
|
||||
bb.utils.set_context(bb.utils.clean_context())
|
||||
bb.event.set_class_handlers(bb.event.clean_class_handlers())
|
||||
self.data = bb.data.init()
|
||||
if self.tracking:
|
||||
self.data.enableTracking()
|
||||
|
||||
# Keep a datastore of the initial environment variables and their
|
||||
# values from when BitBake was launched to enable child processes
|
||||
# to use environment variables which have been cleaned from the
|
||||
# BitBake processes env
|
||||
self.savedenv = bb.data.init()
|
||||
for k in cookercfg.env:
|
||||
self.savedenv.setVar(k, cookercfg.env[k])
|
||||
|
||||
filtered_keys = bb.utils.approved_variables()
|
||||
bb.data.inheritFromOS(self.data, self.savedenv, filtered_keys)
|
||||
self.data.setVar("BB_ORIGENV", self.savedenv)
|
||||
|
||||
if worker:
|
||||
self.data.setVar("BB_WORKERCONTEXT", "1")
|
||||
|
||||
def parseBaseConfiguration(self):
|
||||
try:
|
||||
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
except SyntaxError:
|
||||
sys.exit(1)
|
||||
except Exception:
|
||||
logger.exception("Error parsing configuration files")
|
||||
sys.exit(1)
|
||||
|
||||
def _findLayerConf(self, data):
|
||||
return findConfigFile("bblayers.conf", data)
|
||||
|
||||
def parseConfigurationFiles(self, prefiles, postfiles):
|
||||
data = self.data
|
||||
bb.parse.init_parser(data)
|
||||
|
||||
# Parse files for loading *before* bitbake.conf and any includes
|
||||
for f in prefiles:
|
||||
data = parse_config_file(f, data)
|
||||
|
||||
layerconf = self._findLayerConf(data)
|
||||
if layerconf:
|
||||
parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
|
||||
# By definition bblayers.conf is in conf/ of TOPDIR.
|
||||
# We may have been called with cwd somewhere else so reset TOPDIR
|
||||
data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
|
||||
data = parse_config_file(layerconf, data)
|
||||
|
||||
layers = (data.getVar('BBLAYERS', True) or "").split()
|
||||
|
||||
data = bb.data.createCopy(data)
|
||||
for layer in layers:
|
||||
parselog.debug(2, "Adding layer %s", layer)
|
||||
data.setVar('LAYERDIR', layer)
|
||||
data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
|
||||
data.expandVarref('LAYERDIR')
|
||||
|
||||
data.delVar('LAYERDIR')
|
||||
|
||||
if not data.getVar("BBPATH", True):
|
||||
msg = "The BBPATH variable is not set"
|
||||
if not layerconf:
|
||||
msg += (" and bitbake did not find a conf/bblayers.conf file in"
|
||||
" the expected location.\nMaybe you accidentally"
|
||||
" invoked bitbake from the wrong directory?")
|
||||
raise SystemExit(msg)
|
||||
|
||||
data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
|
||||
|
||||
# Parse files for loading *after* bitbake.conf and any includes
|
||||
for p in postfiles:
|
||||
data = parse_config_file(p, data)
|
||||
|
||||
# Handle any INHERITs and inherit the base class
|
||||
bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
|
||||
for bbclass in bbclasses:
|
||||
data = _inherit(bbclass, data)
|
||||
|
||||
# Nomally we only register event handlers at the end of parsing .bb files
|
||||
# We register any handlers we've found so far here...
|
||||
for var in data.getVar('__BBHANDLERS') or []:
|
||||
bb.event.register(var, data.getVar(var), (data.getVarFlag(var, "eventmask", True) or "").split())
|
||||
|
||||
if data.getVar("BB_WORKERCONTEXT", False) is None:
|
||||
bb.fetch.fetcher_init(data)
|
||||
bb.codeparser.parser_cache_init(data)
|
||||
bb.event.fire(bb.event.ConfigParsed(), data)
|
||||
|
||||
if data.getVar("BB_INVALIDCONF") is True:
|
||||
data.setVar("BB_INVALIDCONF", False)
|
||||
self.parseConfigurationFiles(self.prefiles, self.postfiles)
|
||||
return
|
||||
|
||||
bb.parse.init_parser(data)
|
||||
data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
|
||||
self.data = data
|
||||
self.data_hash = data.get_hash()
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def init():
|
||||
def init_db(parent = None):
|
||||
"""Return a new object representing the Bitbake data,
|
||||
optionally based on an existing object"""
|
||||
if parent is not None:
|
||||
if parent:
|
||||
return parent.createCopy()
|
||||
else:
|
||||
return _dict_type()
|
||||
@@ -97,10 +97,6 @@ def delVar(var, d):
|
||||
"""Removes a variable from the data set"""
|
||||
d.delVar(var)
|
||||
|
||||
def appendVar(var, value, d):
|
||||
"""Append additional value to a variable"""
|
||||
d.appendVar(var, value)
|
||||
|
||||
def setVarFlag(var, flag, flagvalue, d):
|
||||
"""Set a flag for a given variable to a given value"""
|
||||
d.setVarFlag(var, flag, flagvalue)
|
||||
@@ -148,7 +144,7 @@ def expandKeys(alterdata, readdata = None):
|
||||
readdata = alterdata
|
||||
|
||||
todolist = {}
|
||||
for key in alterdata:
|
||||
for key in keys(alterdata):
|
||||
if not '${' in key:
|
||||
continue
|
||||
|
||||
@@ -278,31 +274,26 @@ def emit_func(func, o=sys.__stdout__, d = init()):
|
||||
if d.getVarFlag(dep, "func"):
|
||||
emit_var(dep, o, d, False) and o.write('\n')
|
||||
newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
|
||||
newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
|
||||
newdeps -= seen
|
||||
|
||||
def update_data(d):
|
||||
"""Performs final steps upon the datastore, including application of overrides"""
|
||||
d.finalize(parent = True)
|
||||
|
||||
def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
def build_dependencies(key, keys, shelldeps, vardepvals, d):
|
||||
deps = set()
|
||||
vardeps = d.getVarFlag(key, "vardeps", True)
|
||||
try:
|
||||
if key[-1] == ']':
|
||||
vf = key[:-1].split('[')
|
||||
value = d.getVarFlag(vf[0], vf[1], False)
|
||||
parser = d.expandWithRefs(value, key)
|
||||
deps |= parser.references
|
||||
deps = deps | (keys & parser.execs)
|
||||
return deps, value
|
||||
varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude"]) or {}
|
||||
vardeps = varflags.get("vardeps")
|
||||
value = d.getVar(key, False)
|
||||
else:
|
||||
value = d.getVar(key, False)
|
||||
|
||||
if "vardepvalue" in varflags:
|
||||
value = varflags.get("vardepvalue")
|
||||
elif varflags.get("func"):
|
||||
if varflags.get("python"):
|
||||
if key in vardepvals:
|
||||
value = d.getVarFlag(key, "vardepvalue", True)
|
||||
elif d.getVarFlag(key, "func"):
|
||||
if d.getVarFlag(key, "python"):
|
||||
parsedvar = d.expandWithRefs(value, key)
|
||||
parser = bb.codeparser.PythonParser(key, logger)
|
||||
if parsedvar.value and "\t" in parsedvar.value:
|
||||
@@ -324,16 +315,19 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
deps = deps | (keys & parser.execs)
|
||||
|
||||
# Add varflags, assuming an exclusion list is set
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||
if varflagsexcl:
|
||||
varfdeps = []
|
||||
for f in varflags:
|
||||
if f not in varflagsexcl:
|
||||
varfdeps.append('%s[%s]' % (key, f))
|
||||
varflags = d.getVarFlags(key)
|
||||
if varflags:
|
||||
for f in varflags:
|
||||
if f not in varflagsexcl:
|
||||
varfdeps.append('%s[%s]' % (key, f))
|
||||
if varfdeps:
|
||||
deps |= set(varfdeps)
|
||||
|
||||
deps |= set((vardeps or "").split())
|
||||
deps -= set(varflags.get("vardepsexclude", "").split())
|
||||
deps -= set((d.getVarFlag(key, "vardepsexclude", True) or "").split())
|
||||
except Exception as e:
|
||||
raise bb.data_smart.ExpansionError(key, None, e)
|
||||
return deps, value
|
||||
@@ -342,16 +336,16 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
|
||||
|
||||
def generate_dependencies(d):
|
||||
|
||||
keys = set(key for key in d if not key.startswith("__"))
|
||||
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||
varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
|
||||
keys = set(key for key in d.keys() if not key.startswith("__"))
|
||||
shelldeps = set(key for key in keys if d.getVarFlag(key, "export") and not d.getVarFlag(key, "unexport"))
|
||||
vardepvals = set(key for key in keys if d.getVarFlag(key, "vardepvalue"))
|
||||
|
||||
deps = {}
|
||||
values = {}
|
||||
|
||||
tasklist = d.getVar('__BBTASKS') or []
|
||||
for task in tasklist:
|
||||
deps[task], values[task] = build_dependencies(task, keys, shelldeps, varflagsexcl, d)
|
||||
deps[task], values[task] = build_dependencies(task, keys, shelldeps, vardepvals, d)
|
||||
newdeps = deps[task]
|
||||
seen = set()
|
||||
while newdeps:
|
||||
@@ -360,7 +354,7 @@ def generate_dependencies(d):
|
||||
newdeps = set()
|
||||
for dep in nextdeps:
|
||||
if dep not in deps:
|
||||
deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, varflagsexcl, d)
|
||||
deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, vardepvals, d)
|
||||
newdeps |= deps[dep]
|
||||
newdeps -= seen
|
||||
#print "For %s: %s" % (task, str(deps[task]))
|
||||
|
||||
@@ -38,9 +38,9 @@ from bb.COW import COWDictBase
|
||||
|
||||
logger = logging.getLogger("BitBake.Data")
|
||||
|
||||
__setvar_keyword__ = ["_append", "_prepend", "_remove"]
|
||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>.*))?$')
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}@]+}")
|
||||
__setvar_keyword__ = ["_append", "_prepend"]
|
||||
__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?$')
|
||||
__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
|
||||
__expand_python_regexp__ = re.compile(r"\${@.+?}")
|
||||
|
||||
def infer_caller_details(loginfo, parent = False, varval = True):
|
||||
@@ -94,14 +94,9 @@ class VariableParse:
|
||||
if self.varname and key:
|
||||
if self.varname == key:
|
||||
raise Exception("variable %s references itself!" % self.varname)
|
||||
if key in self.d.expand_cache:
|
||||
varparse = self.d.expand_cache[key]
|
||||
self.references |= varparse.references
|
||||
self.execs |= varparse.execs
|
||||
return varparse.value
|
||||
var = self.d.getVar(key, True)
|
||||
self.references.add(key)
|
||||
if var is not None:
|
||||
self.references.add(key)
|
||||
return var
|
||||
else:
|
||||
return match.group()
|
||||
@@ -362,8 +357,7 @@ class DataSmart(MutableMapping):
|
||||
|
||||
#
|
||||
# First we apply all overrides
|
||||
# Then we will handle _append and _prepend and store the _remove
|
||||
# information for later.
|
||||
# Then we will handle _append and _prepend
|
||||
#
|
||||
|
||||
# We only want to report finalization once per variable overridden.
|
||||
@@ -398,7 +392,7 @@ class DataSmart(MutableMapping):
|
||||
except Exception:
|
||||
logger.info("Untracked delVar")
|
||||
|
||||
# now on to the appends and prepends, and stashing the removes
|
||||
# now on to the appends and prepends
|
||||
for op in __setvar_keyword__:
|
||||
if op in self._special_values:
|
||||
appends = self._special_values[op] or []
|
||||
@@ -421,10 +415,6 @@ class DataSmart(MutableMapping):
|
||||
elif op == "_prepend":
|
||||
sval = a + (self.getVar(append, False) or "")
|
||||
self.setVar(append, sval)
|
||||
elif op == "_remove":
|
||||
removes = self.getVarFlag(append, "_removeactive", False) or []
|
||||
removes.extend(a.split())
|
||||
self.setVarFlag(append, "_removeactive", removes, ignore=True)
|
||||
|
||||
# We save overrides that may be applied at some later stage
|
||||
if keep:
|
||||
@@ -495,22 +485,23 @@ class DataSmart(MutableMapping):
|
||||
|
||||
# more cookies for the cookie monster
|
||||
if '_' in var:
|
||||
self._setvar_update_overrides(var)
|
||||
override = var[var.rfind('_')+1:]
|
||||
if len(override) > 0:
|
||||
if override not in self._seen_overrides:
|
||||
self._seen_overrides[override] = set()
|
||||
self._seen_overrides[override].add( var )
|
||||
|
||||
# setting var
|
||||
self.dict[var]["_content"] = value
|
||||
self.varhistory.record(**loginfo)
|
||||
|
||||
def _setvar_update_overrides(self, var):
|
||||
# aka pay the cookie monster
|
||||
override = var[var.rfind('_')+1:]
|
||||
if len(override) > 0:
|
||||
if override not in self._seen_overrides:
|
||||
self._seen_overrides[override] = set()
|
||||
self._seen_overrides[override].add( var )
|
||||
|
||||
def getVar(self, var, expand=False, noweakdefault=False):
|
||||
return self.getVarFlag(var, "_content", expand, noweakdefault)
|
||||
value = self.getVarFlag(var, "_content", False, noweakdefault)
|
||||
|
||||
# Call expand() separately to make use of the expand cache
|
||||
if expand and value:
|
||||
return self.expand(value, var)
|
||||
return value
|
||||
|
||||
def renameVar(self, key, newkey, **loginfo):
|
||||
"""
|
||||
@@ -524,7 +515,7 @@ class DataSmart(MutableMapping):
|
||||
self.varhistory.record(**loginfo)
|
||||
self.setVar(newkey, val, ignore=True)
|
||||
|
||||
for i in (__setvar_keyword__):
|
||||
for i in ('_append', '_prepend'):
|
||||
src = self.getVarFlag(key, i)
|
||||
if src is None:
|
||||
continue
|
||||
@@ -575,36 +566,16 @@ class DataSmart(MutableMapping):
|
||||
self._makeShadowCopy(var)
|
||||
self.dict[var][flag] = value
|
||||
|
||||
if flag == "defaultval" and '_' in var:
|
||||
self._setvar_update_overrides(var)
|
||||
|
||||
if flag == "unexport" or flag == "export":
|
||||
if not "__exportlist" in self.dict:
|
||||
self._makeShadowCopy("__exportlist")
|
||||
if not "_content" in self.dict["__exportlist"]:
|
||||
self.dict["__exportlist"]["_content"] = set()
|
||||
self.dict["__exportlist"]["_content"].add(var)
|
||||
|
||||
def getVarFlag(self, var, flag, expand=False, noweakdefault=False):
|
||||
local_var = self._findVar(var)
|
||||
value = None
|
||||
if local_var is not None:
|
||||
if local_var:
|
||||
if flag in local_var:
|
||||
value = copy.copy(local_var[flag])
|
||||
elif flag == "_content" and "defaultval" in local_var and not noweakdefault:
|
||||
value = copy.copy(local_var["defaultval"])
|
||||
if expand and value:
|
||||
# Only getvar (flag == _content) hits the expand cache
|
||||
cachename = None
|
||||
if flag == "_content":
|
||||
cachename = var
|
||||
else:
|
||||
cachename = var + "[" + flag + "]"
|
||||
value = self.expand(value, cachename)
|
||||
if value is not None and flag == "_content" and local_var is not None and "_removeactive" in local_var:
|
||||
filtered = filter(lambda v: v not in local_var["_removeactive"],
|
||||
value.split(" "))
|
||||
value = " ".join(filtered)
|
||||
value = self.expand(value, None)
|
||||
return value
|
||||
|
||||
def delVarFlag(self, var, flag, **loginfo):
|
||||
@@ -649,17 +620,16 @@ class DataSmart(MutableMapping):
|
||||
self.varhistory.record(**loginfo)
|
||||
self.dict[var][i] = flags[i]
|
||||
|
||||
def getVarFlags(self, var, expand = False, internalflags=False):
|
||||
def getVarFlags(self, var):
|
||||
local_var = self._findVar(var)
|
||||
flags = {}
|
||||
|
||||
if local_var:
|
||||
for i in local_var:
|
||||
if i.startswith("_") and not internalflags:
|
||||
if i.startswith("_"):
|
||||
continue
|
||||
flags[i] = local_var[i]
|
||||
if expand and i in expand:
|
||||
flags[i] = self.expand(flags[i], var + "[" + i + "]")
|
||||
|
||||
if len(flags) == 0:
|
||||
return None
|
||||
return flags
|
||||
@@ -765,16 +735,13 @@ class DataSmart(MutableMapping):
|
||||
for key in keys:
|
||||
if key in config_whitelist:
|
||||
continue
|
||||
|
||||
value = d.getVar(key, False) or ""
|
||||
data.update({key:value})
|
||||
|
||||
varflags = d.getVarFlags(key, internalflags = True)
|
||||
varflags = d.getVarFlags(key)
|
||||
if not varflags:
|
||||
continue
|
||||
for f in varflags:
|
||||
if f == "_content":
|
||||
continue
|
||||
data.update({'%s[%s]' % (key, f):varflags[f]})
|
||||
|
||||
for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
|
||||
|
||||
@@ -33,12 +33,11 @@ import atexit
|
||||
import traceback
|
||||
import bb.utils
|
||||
import bb.compat
|
||||
import bb.exceptions
|
||||
|
||||
# This is the pid for which we should generate the event. This is set when
|
||||
# the runqueue forks off.
|
||||
worker_pid = 0
|
||||
worker_fire = None
|
||||
worker_pipe = None
|
||||
|
||||
logger = logging.getLogger('BitBake.Event')
|
||||
|
||||
@@ -48,25 +47,20 @@ class Event(object):
|
||||
def __init__(self):
|
||||
self.pid = worker_pid
|
||||
|
||||
NotHandled = 0
|
||||
Handled = 1
|
||||
|
||||
Registered = 10
|
||||
AlreadyRegistered = 14
|
||||
|
||||
def get_class_handlers():
|
||||
return _handlers
|
||||
|
||||
def set_class_handlers(h):
|
||||
_handlers = h
|
||||
|
||||
def clean_class_handlers():
|
||||
return bb.compat.OrderedDict()
|
||||
|
||||
# Internal
|
||||
_handlers = clean_class_handlers()
|
||||
_handlers = bb.compat.OrderedDict()
|
||||
_ui_handlers = {}
|
||||
_ui_logfilters = {}
|
||||
_ui_handler_seq = 0
|
||||
_event_handler_map = {}
|
||||
_catchall_handlers = {}
|
||||
|
||||
# For compatibility
|
||||
bb.utils._context["NotHandled"] = NotHandled
|
||||
bb.utils._context["Handled"] = Handled
|
||||
|
||||
def execute_handler(name, handler, event, d):
|
||||
event.data = d
|
||||
@@ -86,18 +80,19 @@ def execute_handler(name, handler, event, d):
|
||||
finally:
|
||||
del event.data
|
||||
|
||||
if ret is not None:
|
||||
warnings.warn("Using Handled/NotHandled in event handlers is deprecated",
|
||||
DeprecationWarning, stacklevel = 2)
|
||||
|
||||
def fire_class_handlers(event, d):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
return
|
||||
|
||||
eid = str(event.__class__)[8:-2]
|
||||
evt_hmap = _event_handler_map.get(eid, {})
|
||||
for name, handler in _handlers.iteritems():
|
||||
if name in _catchall_handlers or name in evt_hmap:
|
||||
try:
|
||||
execute_handler(name, handler, event, d)
|
||||
except Exception:
|
||||
continue
|
||||
try:
|
||||
execute_handler(name, handler, event, d)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
ui_queue = []
|
||||
@atexit.register
|
||||
@@ -136,8 +131,6 @@ def fire_ui_handlers(event, d):
|
||||
for h in _ui_handlers:
|
||||
#print "Sending event %s" % event
|
||||
try:
|
||||
if not _ui_logfilters[h].filter(event):
|
||||
continue
|
||||
# We use pickle here since it better handles object instances
|
||||
# which xmlrpc's marshaller does not. Events *must* be serializable
|
||||
# by pickle.
|
||||
@@ -159,16 +152,24 @@ def fire(event, d):
|
||||
# don't have a datastore so the datastore context isn't a problem.
|
||||
|
||||
fire_class_handlers(event, d)
|
||||
if worker_fire:
|
||||
if worker_pid != 0:
|
||||
worker_fire(event, d)
|
||||
else:
|
||||
fire_ui_handlers(event, d)
|
||||
|
||||
def worker_fire(event, d):
|
||||
data = "<event>" + pickle.dumps(event) + "</event>"
|
||||
worker_pipe.write(data)
|
||||
|
||||
def fire_from_worker(event, d):
|
||||
if not event.startswith("<event>") or not event.endswith("</event>"):
|
||||
print("Error, not an event %s" % event)
|
||||
return
|
||||
event = pickle.loads(event[7:-8])
|
||||
fire_ui_handlers(event, d)
|
||||
|
||||
noop = lambda _: None
|
||||
def register(name, handler, mask=[]):
|
||||
def register(name, handler):
|
||||
"""Register an Event handler"""
|
||||
|
||||
# already registered
|
||||
@@ -193,14 +194,6 @@ def register(name, handler, mask=[]):
|
||||
else:
|
||||
_handlers[name] = handler
|
||||
|
||||
if not mask or '*' in mask:
|
||||
_catchall_handlers[name] = True
|
||||
else:
|
||||
for m in mask:
|
||||
if _event_handler_map.get(m, None) is None:
|
||||
_event_handler_map[m] = {}
|
||||
_event_handler_map[m][name] = True
|
||||
|
||||
return Registered
|
||||
|
||||
def remove(name, handler):
|
||||
@@ -210,8 +203,6 @@ def remove(name, handler):
|
||||
def register_UIHhandler(handler):
|
||||
bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1
|
||||
_ui_handlers[_ui_handler_seq] = handler
|
||||
level, debug_domains = bb.msg.constructLogOptions()
|
||||
_ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains)
|
||||
return _ui_handler_seq
|
||||
|
||||
def unregister_UIHhandler(handlerNum):
|
||||
@@ -219,37 +210,6 @@ def unregister_UIHhandler(handlerNum):
|
||||
del _ui_handlers[handlerNum]
|
||||
return
|
||||
|
||||
# Class to allow filtering of events and specific filtering of LogRecords *before* we put them over the IPC
|
||||
class UIEventFilter(object):
|
||||
def __init__(self, level, debug_domains):
|
||||
self.update(None, level, debug_domains)
|
||||
|
||||
def update(self, eventmask, level, debug_domains):
|
||||
self.eventmask = eventmask
|
||||
self.stdlevel = level
|
||||
self.debug_domains = debug_domains
|
||||
|
||||
def filter(self, event):
|
||||
if isinstance(event, logging.LogRecord):
|
||||
if event.levelno >= self.stdlevel:
|
||||
return True
|
||||
if event.name in self.debug_domains and event.levelno >= self.debug_domains[event.name]:
|
||||
return True
|
||||
return False
|
||||
eid = str(event.__class__)[8:-2]
|
||||
if self.eventmask and eid not in self.eventmask:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_UIHmask(handlerNum, level, debug_domains, mask):
|
||||
if not handlerNum in _ui_handlers:
|
||||
return False
|
||||
if '*' in mask:
|
||||
_ui_logfilters[handlerNum].update(None, level, debug_domains)
|
||||
else:
|
||||
_ui_logfilters[handlerNum].update(mask, level, debug_domains)
|
||||
return True
|
||||
|
||||
def getName(e):
|
||||
"""Returns the name of a class or class instance"""
|
||||
if getattr(e, "__name__", None) == None:
|
||||
@@ -377,13 +337,12 @@ class DiskFull(Event):
|
||||
class NoProvider(Event):
|
||||
"""No Provider for an Event"""
|
||||
|
||||
def __init__(self, item, runtime=False, dependees=None, reasons=[], close_matches=[]):
|
||||
def __init__(self, item, runtime=False, dependees=None, reasons=[]):
|
||||
Event.__init__(self)
|
||||
self._item = item
|
||||
self._runtime = runtime
|
||||
self._dependees = dependees
|
||||
self._reasons = reasons
|
||||
self._close_matches = close_matches
|
||||
|
||||
def getItem(self):
|
||||
return self._item
|
||||
|
||||
@@ -28,8 +28,6 @@ BitBake build tools.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
import os, re
|
||||
import signal
|
||||
import glob
|
||||
import logging
|
||||
import urllib
|
||||
import urlparse
|
||||
@@ -40,8 +38,6 @@ import operator
|
||||
import bb.persist_data, bb.utils
|
||||
import bb.checksum
|
||||
from bb import data
|
||||
import bb.process
|
||||
import subprocess
|
||||
|
||||
__version__ = "2"
|
||||
_checksum_cache = bb.checksum.FileChecksumCache()
|
||||
@@ -121,7 +117,7 @@ class ParameterError(BBFetchException):
|
||||
class NetworkAccess(BBFetchException):
|
||||
"""Exception raised when network access is disabled but it is required."""
|
||||
def __init__(self, url, cmd):
|
||||
msg = "Network access disabled through BB_NO_NETWORK (or set indirectly due to use of BB_FETCH_PREMIRRORONLY) but access requested with command %s (for url %s)" % (cmd, url)
|
||||
msg = "Network access disabled through BB_NO_NETWORK but access requested with command %s (for url %s)" % (cmd, url)
|
||||
self.url = url
|
||||
self.cmd = cmd
|
||||
BBFetchException.__init__(self, msg)
|
||||
@@ -329,7 +325,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)
|
||||
|
||||
@@ -588,6 +584,7 @@ def update_stamp(u, ud, d):
|
||||
open(ud.donestamp, 'w').close()
|
||||
|
||||
def subprocess_setup():
|
||||
import signal
|
||||
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||
# non-Python subprocesses expect.
|
||||
# SIGPIPE errors are known issues with gzip/bash
|
||||
@@ -619,12 +616,7 @@ def get_srcrev(d):
|
||||
raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
|
||||
|
||||
if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
|
||||
autoinc, rev = urldata[scms[0]].method.sortable_revision(scms[0], urldata[scms[0]], d, urldata[scms[0]].names[0])
|
||||
if len(rev) > 10:
|
||||
rev = rev[:10]
|
||||
if autoinc:
|
||||
return "AUTOINC+" + rev
|
||||
return rev
|
||||
return urldata[scms[0]].method.sortable_revision(scms[0], urldata[scms[0]], d, urldata[scms[0]].names[0])
|
||||
|
||||
#
|
||||
# Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
|
||||
@@ -633,17 +625,19 @@ def get_srcrev(d):
|
||||
if not format:
|
||||
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
|
||||
|
||||
seenautoinc = False
|
||||
autoinc = False
|
||||
autoinc_templ = 'AUTOINC+'
|
||||
for scm in scms:
|
||||
ud = urldata[scm]
|
||||
for name in ud.names:
|
||||
autoinc, rev = ud.method.sortable_revision(scm, ud, d, name)
|
||||
seenautoinc = seenautoinc or autoinc
|
||||
if len(rev) > 10:
|
||||
rev = rev[:10]
|
||||
rev = ud.method.sortable_revision(scm, ud, d, name)
|
||||
if rev.startswith(autoinc_templ):
|
||||
if not autoinc:
|
||||
autoinc = True
|
||||
format = "%s%s" % (autoinc_templ, format)
|
||||
rev = rev[len(autoinc_templ):]
|
||||
|
||||
format = format.replace(name, rev)
|
||||
if seenautoinc:
|
||||
format = "AUTOINC+" + format
|
||||
|
||||
return format
|
||||
|
||||
@@ -659,6 +653,9 @@ def runfetchcmd(cmd, d, quiet = False, cleanup = []):
|
||||
Optionally remove the files/directories listed in cleanup upon failure
|
||||
"""
|
||||
|
||||
import bb.process
|
||||
import subprocess
|
||||
|
||||
# Need to export PATH as binary could be in metadata paths
|
||||
# rather than host provided
|
||||
# Also include some other variables.
|
||||
@@ -929,6 +926,7 @@ def get_file_checksums(filelist, pn):
|
||||
try:
|
||||
checksum = _checksum_cache.get_checksum(f)
|
||||
except OSError as e:
|
||||
import traceback
|
||||
bb.warn("Unable to get checksum for %s SRC_URI entry %s: %s" % (pn, os.path.basename(f), e))
|
||||
return None
|
||||
return checksum
|
||||
@@ -938,6 +936,7 @@ def get_file_checksums(filelist, pn):
|
||||
checksum = None
|
||||
if '*' in pth:
|
||||
# Handle globs
|
||||
import glob
|
||||
for f in glob.glob(pth):
|
||||
checksum = checksum_file(f)
|
||||
if checksum:
|
||||
@@ -1105,7 +1104,7 @@ class FetchMethod(object):
|
||||
def recommends_checksum(self, urldata):
|
||||
"""
|
||||
Is the backend on where checksumming is recommended (should warnings
|
||||
be displayed if there is no checksum)?
|
||||
by displayed if there is no checksum)?
|
||||
"""
|
||||
return False
|
||||
|
||||
@@ -1147,6 +1146,7 @@ class FetchMethod(object):
|
||||
raise NoMethodError(url)
|
||||
|
||||
def unpack(self, urldata, rootdir, data):
|
||||
import subprocess
|
||||
iterate = False
|
||||
file = urldata.localpath
|
||||
|
||||
@@ -1157,7 +1157,7 @@ class FetchMethod(object):
|
||||
(file, urldata.parm.get('unpack')))
|
||||
|
||||
dots = file.split(".")
|
||||
if dots[-1] in ['gz', 'bz2', 'Z', 'xz']:
|
||||
if dots[-1] in ['gz', 'bz2', 'Z']:
|
||||
efile = os.path.join(rootdir, os.path.basename('.'.join(dots[0:-1])))
|
||||
else:
|
||||
efile = file
|
||||
@@ -1293,8 +1293,14 @@ class FetchMethod(object):
|
||||
return rev
|
||||
|
||||
def sortable_revision(self, url, ud, d, name):
|
||||
"""
|
||||
|
||||
"""
|
||||
if hasattr(self, "_sortable_revision"):
|
||||
return self._sortable_revision(url, ud, d)
|
||||
|
||||
latest_rev = self._build_revision(url, ud, d, name)
|
||||
return True, str(latest_rev)
|
||||
return 'AUTOINC+%s' % str(latest_rev)
|
||||
|
||||
def generate_revision_key(self, url, ud, d, name):
|
||||
key = self._revision_key(url, ud, d, name)
|
||||
|
||||
@@ -132,12 +132,12 @@ class Bzr(FetchMethod):
|
||||
|
||||
return output.strip()
|
||||
|
||||
def sortable_revision(self, url, ud, d, name):
|
||||
def _sortable_revision(self, url, ud, d):
|
||||
"""
|
||||
Return a sortable revision number which in our case is the revision number
|
||||
"""
|
||||
|
||||
return False, self._build_revision(url, ud, d)
|
||||
return self._build_revision(url, ud, d)
|
||||
|
||||
def _build_revision(self, url, ud, d):
|
||||
return ud.revision
|
||||
|
||||
@@ -92,7 +92,10 @@ class Hg(FetchMethod):
|
||||
if not ud.user:
|
||||
hgroot = host + ud.path
|
||||
else:
|
||||
hgroot = ud.user + "@" + host + ud.path
|
||||
if ud.pswd:
|
||||
hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path
|
||||
else:
|
||||
hgroot = ud.user + "@" + host + ud.path
|
||||
|
||||
if command == "info":
|
||||
return "%s identify -i %s://%s/%s" % (basecmd, proto, hgroot, ud.module)
|
||||
@@ -112,7 +115,10 @@ class Hg(FetchMethod):
|
||||
# do not pass options list; limiting pull to rev causes the local
|
||||
# repo not to contain it and immediately following "update" command
|
||||
# will crash
|
||||
cmd = "%s pull" % (basecmd)
|
||||
if ud.user and ud.pswd:
|
||||
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" pull" % (basecmd, ud.user, ud.pswd, proto)
|
||||
else:
|
||||
cmd = "%s pull" % (basecmd)
|
||||
elif command == "update":
|
||||
cmd = "%s update -C %s" % (basecmd, " ".join(options))
|
||||
else:
|
||||
|
||||
@@ -112,7 +112,7 @@ class Perforce(FetchMethod):
|
||||
base = path
|
||||
which = path.find('/...')
|
||||
if which != -1:
|
||||
base = path[:which]
|
||||
base = path[:which-1]
|
||||
|
||||
base = self._strip_leading_slashes(base)
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import bb
|
||||
import re
|
||||
from bb import data
|
||||
from bb.fetch2 import FetchMethod
|
||||
from bb.fetch2 import FetchError
|
||||
@@ -81,8 +82,6 @@ class Svn(FetchMethod):
|
||||
|
||||
options = []
|
||||
|
||||
options.append("--no-auth-cache")
|
||||
|
||||
if ud.user:
|
||||
options.append("--username %s" % ud.user)
|
||||
|
||||
@@ -91,6 +90,8 @@ class Svn(FetchMethod):
|
||||
|
||||
if command == "info":
|
||||
svncmd = "%s info %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module)
|
||||
elif command == "log1":
|
||||
svncmd = "%s log --limit 1 %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module)
|
||||
else:
|
||||
suffix = ""
|
||||
if ud.revision:
|
||||
@@ -167,23 +168,22 @@ class Svn(FetchMethod):
|
||||
"""
|
||||
Return the latest upstream revision number
|
||||
"""
|
||||
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "info"))
|
||||
bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"))
|
||||
|
||||
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "info"), d, True)
|
||||
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True)
|
||||
|
||||
revision = None
|
||||
for line in output.splitlines():
|
||||
if "Last Changed Rev" in line:
|
||||
revision = line.split(":")[1].strip()
|
||||
# skip the first line, as per output of svn log
|
||||
# then we expect the revision on the 2nd line
|
||||
revision = re.search('^r([0-9]*)', output.splitlines()[1]).group(1)
|
||||
|
||||
return revision
|
||||
|
||||
def sortable_revision(self, url, ud, d, name):
|
||||
def _sortable_revision(self, url, ud, d):
|
||||
"""
|
||||
Return a sortable revision number which in our case is the revision number
|
||||
"""
|
||||
|
||||
return False, self._build_revision(url, ud, d)
|
||||
return self._build_revision(url, ud, d)
|
||||
|
||||
def _build_revision(self, url, ud, d):
|
||||
return ud.revision
|
||||
|
||||
@@ -26,4 +26,3 @@ def insert_method(modulename, code, fn):
|
||||
"""
|
||||
comp = better_compile(code, modulename, fn )
|
||||
better_exec(comp, None, code, fn)
|
||||
|
||||
|
||||
@@ -146,7 +146,8 @@ def init_msgconfig(verbose, debug, debug_domains = []):
|
||||
bb.msg.loggerVerboseLogs = True
|
||||
bb.msg.loggerDefaultDomains = debug_domains
|
||||
|
||||
def constructLogOptions():
|
||||
def addDefaultlogFilter(handler):
|
||||
|
||||
debug = loggerDefaultDebugLevel
|
||||
verbose = loggerDefaultVerbose
|
||||
domains = loggerDefaultDomains
|
||||
@@ -162,10 +163,6 @@ def constructLogOptions():
|
||||
for (domainarg, iterator) in groupby(domains):
|
||||
dlevel = len(tuple(iterator))
|
||||
debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
|
||||
return level, debug_domains
|
||||
|
||||
def addDefaultlogFilter(handler):
|
||||
level, debug_domains = constructLogOptions()
|
||||
|
||||
BBLogFilter(handler, level, debug_domains)
|
||||
|
||||
|
||||
@@ -107,9 +107,6 @@ def resolve_file(fn, d):
|
||||
raise IOError("file %s not found in %s" % (fn, bbpath))
|
||||
fn = newfn
|
||||
|
||||
if not os.path.isfile(fn):
|
||||
raise IOError("file %s not found" % fn)
|
||||
|
||||
logger.debug(2, "LOAD %s", fn)
|
||||
return fn
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ class MethodNode(AstNode):
|
||||
def eval(self, data):
|
||||
text = '\n'.join(self.body)
|
||||
if self.func_name == "__anonymous":
|
||||
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(string.maketrans('/.+-@', '_____'))))
|
||||
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(string.maketrans('/.+-', '____'))))
|
||||
text = "def %s(d):\n" % (funcname) + text
|
||||
bb.methodpool.insert_method(funcname, text, self.filename)
|
||||
anonfuncs = data.getVar('__BBANONFUNCS') or []
|
||||
@@ -320,7 +320,8 @@ def finalize(fn, d, variant = None):
|
||||
all_handlers = {}
|
||||
for var in d.getVar('__BBHANDLERS') or []:
|
||||
# try to add the handler
|
||||
bb.event.register(var, d.getVar(var), (d.getVarFlag(var, "eventmask", True) or "").split())
|
||||
handler = d.getVar(var)
|
||||
bb.event.register(var, handler)
|
||||
|
||||
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
|
||||
|
||||
|
||||
@@ -104,7 +104,6 @@ def get_statements(filename, absolute_filename, base_name):
|
||||
if not s: break
|
||||
s = s.rstrip()
|
||||
feeder(lineno, s, filename, base_name, statements)
|
||||
file.close()
|
||||
if __inpython__:
|
||||
# add a blank line to close out any python definition
|
||||
feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
|
||||
|
||||
@@ -89,7 +89,7 @@ def include(oldfn, fn, lineno, data, error_out):
|
||||
from bb.parse import handle
|
||||
try:
|
||||
ret = handle(fn, data, True)
|
||||
except (IOError, OSError):
|
||||
except IOError:
|
||||
if error_out:
|
||||
raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
|
||||
logger.debug(2, "CONF file '%s' not found", fn)
|
||||
@@ -145,8 +145,6 @@ def handle(fn, data, include):
|
||||
if oldfile:
|
||||
data.setVar('FILE', oldfile)
|
||||
|
||||
f.close()
|
||||
|
||||
for f in confFilters:
|
||||
f(fn, data)
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def _logged_communicate(pipe, log, input):
|
||||
rlist = rin
|
||||
try:
|
||||
r,w,e = select.select (rlist, [], [])
|
||||
except OSError as e:
|
||||
except OSError, e:
|
||||
if e.errno != errno.EINTR:
|
||||
raise
|
||||
|
||||
@@ -102,10 +102,6 @@ def _logged_communicate(pipe, log, input):
|
||||
log.write(data)
|
||||
finally:
|
||||
log.flush()
|
||||
if pipe.stdout is not None:
|
||||
pipe.stdout.close()
|
||||
if pipe.stderr is not None:
|
||||
pipe.stderr.close()
|
||||
return ''.join(outdata), ''.join(errdata)
|
||||
|
||||
def run(cmd, input=None, log=None, **options):
|
||||
|
||||
@@ -92,9 +92,9 @@ def sortPriorities(pn, dataCache, pkg_pn = None):
|
||||
priorities[priority][preference] = []
|
||||
priorities[priority][preference].append(f)
|
||||
tmp_pn = []
|
||||
for pri in sorted(priorities):
|
||||
for pri in sorted(priorities, lambda a, b: a - b):
|
||||
tmp_pref = []
|
||||
for pref in sorted(priorities[pri]):
|
||||
for pref in sorted(priorities[pri], lambda a, b: b - a):
|
||||
tmp_pref.extend(priorities[pri][pref])
|
||||
tmp_pn = [tmp_pref] + tmp_pn
|
||||
|
||||
@@ -341,7 +341,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
|
||||
if numberPreferred > 1:
|
||||
logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s", item, preferred, preferred_vars)
|
||||
|
||||
logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
|
||||
logger.debug(1, "sorted providers for %s are: %s", item, eligible)
|
||||
|
||||
return eligible, numberPreferred
|
||||
|
||||
@@ -376,6 +376,5 @@ def getRuntimeProviders(dataCache, rdepend):
|
||||
regexp_cache[pattern] = regexp
|
||||
if regexp.match(rdepend):
|
||||
rproviders += dataCache.packages_dynamic[pattern]
|
||||
logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
|
||||
|
||||
return rproviders
|
||||
|
||||
@@ -292,7 +292,7 @@ class WordLexer:
|
||||
elif sep=='${':
|
||||
parsefunc = self._parse_parameter
|
||||
else:
|
||||
raise NotImplementedError(sep)
|
||||
raise NotImplementedError()
|
||||
|
||||
pos, closed = parsefunc(buf, result, eof)
|
||||
return pos, closed
|
||||
|
||||
@@ -28,17 +28,10 @@ import sys
|
||||
import signal
|
||||
import stat
|
||||
import fcntl
|
||||
import errno
|
||||
import logging
|
||||
import bb
|
||||
from bb import msg, data, event
|
||||
from bb import monitordisk
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
bblogger = logging.getLogger("BitBake")
|
||||
logger = logging.getLogger("BitBake.RunQueue")
|
||||
@@ -84,6 +77,7 @@ runQueueRunning = 6
|
||||
runQueueFailed = 7
|
||||
runQueueCleanUp = 8
|
||||
runQueueComplete = 9
|
||||
runQueueChildProcess = 10
|
||||
|
||||
class RunQueueScheduler(object):
|
||||
"""
|
||||
@@ -382,7 +376,6 @@ class RunQueueData:
|
||||
|
||||
runq_build = []
|
||||
recursivetasks = {}
|
||||
recursiveitasks = {}
|
||||
recursivetasksselfref = set()
|
||||
|
||||
taskData = self.taskData
|
||||
@@ -505,12 +498,6 @@ class RunQueueData:
|
||||
if taskData.tasks_name[task] in tasknames:
|
||||
recursivetasksselfref.add(task)
|
||||
|
||||
if 'recideptask' in task_deps and taskData.tasks_name[task] in task_deps['recideptask']:
|
||||
recursiveitasks[task] = []
|
||||
for t in task_deps['recideptask'][taskData.tasks_name[task]].split():
|
||||
newdep = taskData.gettask_id_fromfnid(fnid, t)
|
||||
recursiveitasks[task].append(newdep)
|
||||
|
||||
self.runq_fnid.append(taskData.tasks_fnid[task])
|
||||
self.runq_task.append(taskData.tasks_name[task])
|
||||
self.runq_depends.append(depends)
|
||||
@@ -543,10 +530,6 @@ class RunQueueData:
|
||||
generate_recdeps(n)
|
||||
generate_recdeps(task)
|
||||
|
||||
if task in recursiveitasks:
|
||||
for dep in recursiveitasks[task]:
|
||||
generate_recdeps(dep)
|
||||
|
||||
# Remove circular references so that do_a[recrdeptask] = "do_a do_b" can work
|
||||
for task in recursivetasks:
|
||||
extradeps[task].difference_update(recursivetasksselfref)
|
||||
@@ -601,13 +584,7 @@ class RunQueueData:
|
||||
continue
|
||||
|
||||
if target[1] not in taskData.tasks_lookup[fnid]:
|
||||
import difflib
|
||||
close_matches = difflib.get_close_matches(target[1], taskData.tasks_lookup[fnid], cutoff=0.7)
|
||||
if close_matches:
|
||||
extra = ". Close matches:\n %s" % "\n ".join(close_matches)
|
||||
else:
|
||||
extra = ""
|
||||
bb.msg.fatal("RunQueue", "Task %s does not exist for target %s%s" % (target[1], target[0], extra))
|
||||
bb.msg.fatal("RunQueue", "Task %s does not exist for target %s" % (target[1], target[0]))
|
||||
|
||||
listid = taskData.tasks_lookup[fnid][target[1]]
|
||||
|
||||
@@ -696,14 +673,6 @@ class RunQueueData:
|
||||
prov_list[prov].append(fn)
|
||||
for prov in prov_list:
|
||||
if len(prov_list[prov]) > 1 and prov not in self.multi_provider_whitelist:
|
||||
seen_pn = []
|
||||
# If two versions of the same PN are being built its fatal, we don't support it.
|
||||
for fn in prov_list[prov]:
|
||||
pn = self.dataCache.pkg_fn[fn]
|
||||
if pn not in seen_pn:
|
||||
seen_pn.append(pn)
|
||||
else:
|
||||
bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn))
|
||||
msg = "Multiple .bb files are due to be built which each provide %s (%s)." % (prov, " ".join(prov_list[prov]))
|
||||
if self.warn_multi_bb:
|
||||
logger.warn(msg)
|
||||
@@ -711,6 +680,7 @@ class RunQueueData:
|
||||
msg += "\n This usually means one provides something the other doesn't and should."
|
||||
logger.error(msg)
|
||||
|
||||
|
||||
# Create a whitelist usable by the stamp checks
|
||||
stampfnwhitelist = []
|
||||
for entry in self.stampwhitelist.split():
|
||||
@@ -733,9 +703,6 @@ class RunQueueData:
|
||||
|
||||
def invalidate_task(fn, taskname, error_nostamp):
|
||||
taskdep = self.dataCache.task_deps[fn]
|
||||
fnid = self.taskData.getfn_id(fn)
|
||||
if taskname not in taskData.tasks_lookup[fnid]:
|
||||
logger.warn("Task %s does not exist, invalidating this task will have no effect" % taskname)
|
||||
if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
|
||||
if error_nostamp:
|
||||
bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
|
||||
@@ -826,92 +793,6 @@ class RunQueue:
|
||||
self.dm = monitordisk.diskMonitor(cfgData)
|
||||
|
||||
self.rqexe = None
|
||||
self.worker = None
|
||||
self.workerpipe = None
|
||||
self.fakeworker = None
|
||||
self.fakeworkerpipe = None
|
||||
|
||||
def _start_worker(self, fakeroot = False, rqexec = None):
|
||||
logger.debug(1, "Starting bitbake-worker")
|
||||
if fakeroot:
|
||||
fakerootcmd = self.cfgData.getVar("FAKEROOTCMD", True)
|
||||
fakerootenv = (self.cfgData.getVar("FAKEROOTBASEENV", True) or "").split()
|
||||
env = os.environ.copy()
|
||||
for key, value in (var.split('=') for var in fakerootenv):
|
||||
env[key] = value
|
||||
worker = subprocess.Popen([fakerootcmd, "bitbake-worker", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
|
||||
else:
|
||||
worker = subprocess.Popen(["bitbake-worker", "decafbad"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
bb.utils.nonblockingfd(worker.stdout)
|
||||
workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, rqexec)
|
||||
|
||||
workerdata = {
|
||||
"taskdeps" : self.rqdata.dataCache.task_deps,
|
||||
"fakerootenv" : self.rqdata.dataCache.fakerootenv,
|
||||
"fakerootdirs" : self.rqdata.dataCache.fakerootdirs,
|
||||
"fakerootnoenv" : self.rqdata.dataCache.fakerootnoenv,
|
||||
"hashes" : self.rqdata.hashes,
|
||||
"hash_deps" : self.rqdata.hash_deps,
|
||||
"sigchecksums" : bb.parse.siggen.file_checksum_values,
|
||||
"runq_hash" : self.rqdata.runq_hash,
|
||||
"logdefaultdebug" : bb.msg.loggerDefaultDebugLevel,
|
||||
"logdefaultverbose" : bb.msg.loggerDefaultVerbose,
|
||||
"logdefaultverboselogs" : bb.msg.loggerVerboseLogs,
|
||||
"logdefaultdomain" : bb.msg.loggerDefaultDomains,
|
||||
"prhost" : self.cooker.prhost,
|
||||
"buildname" : self.cfgData.getVar("BUILDNAME", True),
|
||||
"date" : self.cfgData.getVar("DATE", True),
|
||||
"time" : self.cfgData.getVar("TIME", True),
|
||||
}
|
||||
|
||||
worker.stdin.write("<cookerconfig>" + pickle.dumps(self.cooker.configuration) + "</cookerconfig>")
|
||||
worker.stdin.write("<workerdata>" + pickle.dumps(workerdata) + "</workerdata>")
|
||||
worker.stdin.flush()
|
||||
|
||||
return worker, workerpipe
|
||||
|
||||
def _teardown_worker(self, worker, workerpipe):
|
||||
if not worker:
|
||||
return
|
||||
logger.debug(1, "Teardown for bitbake-worker")
|
||||
worker.stdin.write("<quit></quit>")
|
||||
worker.stdin.flush()
|
||||
while worker.returncode is None:
|
||||
workerpipe.read()
|
||||
worker.poll()
|
||||
while workerpipe.read():
|
||||
continue
|
||||
workerpipe.close()
|
||||
|
||||
def start_worker(self):
|
||||
if self.worker:
|
||||
self.teardown_workers()
|
||||
self.worker, self.workerpipe = self._start_worker()
|
||||
|
||||
def start_fakeworker(self, rqexec):
|
||||
if not self.fakeworker:
|
||||
self.fakeworker, self.fakeworkerpipe = self._start_worker(True, rqexec)
|
||||
|
||||
def teardown_workers(self):
|
||||
self._teardown_worker(self.worker, self.workerpipe)
|
||||
self.worker = None
|
||||
self.workerpipe = None
|
||||
self._teardown_worker(self.fakeworker, self.fakeworkerpipe)
|
||||
self.fakeworker = None
|
||||
self.fakeworkerpipe = None
|
||||
|
||||
def read_workers(self):
|
||||
self.workerpipe.read()
|
||||
if self.fakeworkerpipe:
|
||||
self.fakeworkerpipe.read()
|
||||
|
||||
def active_fds(self):
|
||||
fds = []
|
||||
if self.workerpipe:
|
||||
fds.append(self.workerpipe.input)
|
||||
if self.fakeworkerpipe:
|
||||
fds.append(self.fakeworkerpipe.input)
|
||||
return fds
|
||||
|
||||
def check_stamp_task(self, task, taskname = None, recurse = False, cache = None):
|
||||
def get_timestamp(f):
|
||||
@@ -990,7 +871,7 @@ class RunQueue:
|
||||
(if the abort on failure configuration option isn't set)
|
||||
"""
|
||||
|
||||
retval = True
|
||||
retval = 0.5
|
||||
|
||||
if self.state is runQueuePrepare:
|
||||
self.rqexe = RunQueueExecuteDummy(self)
|
||||
@@ -1003,7 +884,6 @@ class RunQueue:
|
||||
if self.cooker.configuration.dump_signatures:
|
||||
self.dump_signatures()
|
||||
else:
|
||||
self.start_worker()
|
||||
self.rqexe = RunQueueExecuteScenequeue(self)
|
||||
|
||||
if self.state in [runQueueSceneRun, runQueueRunning, runQueueCleanUp]:
|
||||
@@ -1024,7 +904,6 @@ class RunQueue:
|
||||
self.rqexe.finish()
|
||||
|
||||
if self.state is runQueueComplete or self.state is runQueueFailed:
|
||||
self.teardown_workers()
|
||||
if self.rqexe.stats.failed:
|
||||
logger.info("Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed.", self.rqexe.stats.completed + self.rqexe.stats.failed, self.rqexe.stats.skipped, self.rqexe.stats.failed)
|
||||
else:
|
||||
@@ -1042,6 +921,10 @@ class RunQueue:
|
||||
# All done
|
||||
return False
|
||||
|
||||
if self.state is runQueueChildProcess:
|
||||
print("Child process, eeek, shouldn't happen!")
|
||||
return False
|
||||
|
||||
# Loop
|
||||
return retval
|
||||
|
||||
@@ -1055,10 +938,6 @@ class RunQueue:
|
||||
raise
|
||||
except:
|
||||
logger.error("An uncaught exception occured in runqueue, please see the failure below:")
|
||||
try:
|
||||
self.teardown_workers()
|
||||
except:
|
||||
pass
|
||||
self.state = runQueueComplete
|
||||
raise
|
||||
|
||||
@@ -1078,7 +957,7 @@ class RunQueue:
|
||||
for task in range(len(self.rqdata.runq_fnid)):
|
||||
if self.rqdata.runq_fnid[task] not in done:
|
||||
fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.collection.get_file_appends(fn), self.cooker.data)
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.get_file_appends(fn), self.cooker.configuration.data)
|
||||
done.add(self.rqdata.runq_fnid[task])
|
||||
|
||||
bb.parse.siggen.dump_sigs(self.rqdata.dataCache)
|
||||
@@ -1100,21 +979,38 @@ class RunQueueExecute:
|
||||
self.runq_buildable = []
|
||||
self.runq_running = []
|
||||
self.runq_complete = []
|
||||
|
||||
self.build_pids = {}
|
||||
self.build_pipes = {}
|
||||
self.build_stamps = {}
|
||||
self.failed_fnids = []
|
||||
|
||||
self.stampcache = {}
|
||||
|
||||
rq.workerpipe.setrunqueueexec(self)
|
||||
if rq.fakeworkerpipe:
|
||||
rq.fakeworkerpipe.setrunqueueexec(self)
|
||||
def runqueue_process_waitpid(self):
|
||||
"""
|
||||
Return none is there are no processes awaiting result collection, otherwise
|
||||
collect the process exit codes and close the information pipe.
|
||||
"""
|
||||
pid, status = os.waitpid(-1, os.WNOHANG)
|
||||
if pid == 0 or os.WIFSTOPPED(status):
|
||||
return None
|
||||
|
||||
def runqueue_process_waitpid(self, task, status):
|
||||
if os.WIFEXITED(status):
|
||||
status = os.WEXITSTATUS(status)
|
||||
elif os.WIFSIGNALED(status):
|
||||
# Per shell conventions for $?, when a process exits due to
|
||||
# a signal, we return an exit code of 128 + SIGNUM
|
||||
status = 128 + os.WTERMSIG(status)
|
||||
|
||||
task = self.build_pids[pid]
|
||||
del self.build_pids[pid]
|
||||
|
||||
self.build_pipes[pid].close()
|
||||
del self.build_pipes[pid]
|
||||
|
||||
# self.build_stamps[pid] may not exist when use shared work directory.
|
||||
if task in self.build_stamps:
|
||||
del self.build_stamps[task]
|
||||
if pid in self.build_stamps:
|
||||
del self.build_stamps[pid]
|
||||
|
||||
if status != 0:
|
||||
self.task_fail(task, status)
|
||||
@@ -1123,12 +1019,16 @@ class RunQueueExecute:
|
||||
return True
|
||||
|
||||
def finish_now(self):
|
||||
|
||||
self.rq.worker.stdin.write("<finishnow></finishnow>")
|
||||
self.rq.worker.stdin.flush()
|
||||
if self.rq.fakeworker:
|
||||
self.rq.fakeworker.stdin.write("<finishnow></finishnow>")
|
||||
self.rq.fakeworker.stdin.flush()
|
||||
if self.stats.active:
|
||||
logger.info("Sending SIGTERM to remaining %s tasks", self.stats.active)
|
||||
for k, v in self.build_pids.iteritems():
|
||||
try:
|
||||
os.kill(-k, signal.SIGTERM)
|
||||
os.waitpid(-1, 0)
|
||||
except:
|
||||
pass
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
self.rq.state = runQueueFailed
|
||||
@@ -1140,10 +1040,12 @@ class RunQueueExecute:
|
||||
def finish(self):
|
||||
self.rq.state = runQueueCleanUp
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData)
|
||||
self.rq.read_workers()
|
||||
|
||||
self.runqueue_process_waitpid()
|
||||
return
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
@@ -1153,6 +1055,114 @@ class RunQueueExecute:
|
||||
self.rq.state = runQueueComplete
|
||||
return
|
||||
|
||||
def fork_off_task(self, fn, task, taskname, quieterrors=False):
|
||||
# We need to setup the environment BEFORE the fork, since
|
||||
# a fork() or exec*() activates PSEUDO...
|
||||
|
||||
envbackup = {}
|
||||
fakeenv = {}
|
||||
umask = None
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
if 'umask' in taskdep and taskname in taskdep['umask']:
|
||||
# umask might come in as a number or text string..
|
||||
try:
|
||||
umask = int(taskdep['umask'][taskname],8)
|
||||
except TypeError:
|
||||
umask = taskdep['umask'][taskname]
|
||||
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
|
||||
envvars = (self.rqdata.dataCache.fakerootenv[fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
fakedirs = (self.rqdata.dataCache.fakerootdirs[fn] or "").split()
|
||||
for p in fakedirs:
|
||||
bb.utils.mkdirhier(p)
|
||||
|
||||
logger.debug(2, 'Running %s:%s under fakeroot, fakedirs: %s' %
|
||||
(fn, taskname, ', '.join(fakedirs)))
|
||||
else:
|
||||
envvars = (self.rqdata.dataCache.fakerootnoenv[fn] or "").split()
|
||||
for key, value in (var.split('=') for var in envvars):
|
||||
envbackup[key] = os.environ.get(key)
|
||||
os.environ[key] = value
|
||||
fakeenv[key] = value
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
pipein, pipeout = os.pipe()
|
||||
pipein = os.fdopen(pipein, 'rb', 4096)
|
||||
pipeout = os.fdopen(pipeout, 'wb', 0)
|
||||
pid = os.fork()
|
||||
except OSError as e:
|
||||
bb.msg.fatal("RunQueue", "fork failed: %d (%s)" % (e.errno, e.strerror))
|
||||
|
||||
if pid == 0:
|
||||
pipein.close()
|
||||
|
||||
# Save out the PID so that the event can include it the
|
||||
# events
|
||||
bb.event.worker_pid = os.getpid()
|
||||
bb.event.worker_pipe = pipeout
|
||||
|
||||
self.rq.state = runQueueChildProcess
|
||||
# Make the child the process group leader
|
||||
os.setpgid(0, 0)
|
||||
# No stdin
|
||||
newsi = os.open(os.devnull, os.O_RDWR)
|
||||
os.dup2(newsi, sys.stdin.fileno())
|
||||
|
||||
if umask:
|
||||
os.umask(umask)
|
||||
|
||||
self.cooker.configuration.data.setVar("BB_WORKERCONTEXT", "1")
|
||||
bb.parse.siggen.set_taskdata(self.rqdata.hashes, self.rqdata.hash_deps)
|
||||
ret = 0
|
||||
try:
|
||||
the_data = bb.cache.Cache.loadDataFull(fn, self.cooker.get_file_appends(fn), self.cooker.configuration.data)
|
||||
the_data.setVar('BB_TASKHASH', self.rqdata.runq_hash[task])
|
||||
for h in self.rqdata.hashes:
|
||||
the_data.setVar("BBHASH_%s" % h, self.rqdata.hashes[h])
|
||||
for h in self.rqdata.hash_deps:
|
||||
the_data.setVar("BBHASHDEPS_%s" % h, self.rqdata.hash_deps[h])
|
||||
|
||||
# exported_vars() returns a generator which *cannot* be passed to os.environ.update()
|
||||
# successfully. We also need to unset anything from the environment which shouldn't be there
|
||||
exports = bb.data.exported_vars(the_data)
|
||||
bb.utils.empty_environment()
|
||||
for e, v in exports:
|
||||
os.environ[e] = v
|
||||
for e in fakeenv:
|
||||
os.environ[e] = fakeenv[e]
|
||||
the_data.setVar(e, fakeenv[e])
|
||||
|
||||
if quieterrors:
|
||||
the_data.setVarFlag(taskname, "quieterrors", "1")
|
||||
|
||||
except Exception as exc:
|
||||
if not quieterrors:
|
||||
logger.critical(str(exc))
|
||||
os._exit(1)
|
||||
try:
|
||||
if not self.cooker.configuration.dry_run:
|
||||
profile = self.cooker.configuration.profile
|
||||
ret = bb.build.exec_task(fn, taskname, the_data, profile)
|
||||
os._exit(ret)
|
||||
except:
|
||||
os._exit(1)
|
||||
else:
|
||||
for key, value in envbackup.iteritems():
|
||||
if value is None:
|
||||
del os.environ[key]
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
return pid, pipein, pipeout
|
||||
|
||||
def check_dependencies(self, task, taskdeps, setscene = False):
|
||||
if not self.rq.depvalidate:
|
||||
return False
|
||||
@@ -1169,7 +1179,7 @@ class RunQueueExecute:
|
||||
taskname = self.rqdata.runq_task[depid]
|
||||
taskdata[dep] = [pn, taskname, fn]
|
||||
call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
|
||||
locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
|
||||
locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.configuration.data }
|
||||
valid = bb.utils.better_eval(call, locs)
|
||||
return valid
|
||||
|
||||
@@ -1236,7 +1246,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
|
||||
call = self.rq.setsceneverify + "(covered, tasknames, fnids, fns, d, invalidtasks=invalidtasks)"
|
||||
call2 = self.rq.setsceneverify + "(covered, tasknames, fnids, fns, d)"
|
||||
locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : self.rqdata.runq_task, "fnids" : self.rqdata.runq_fnid, "fns" : self.rqdata.taskData.fn_index, "d" : self.cooker.data, "invalidtasks" : invalidtasks }
|
||||
locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : self.rqdata.runq_task, "fnids" : self.rqdata.runq_fnid, "fns" : self.rqdata.taskData.fn_index, "d" : self.cooker.configuration.data, "invalidtasks" : invalidtasks }
|
||||
# Backwards compatibility with older versions without invalidtasks
|
||||
try:
|
||||
covered_remove = bb.utils.better_eval(call, locs)
|
||||
@@ -1264,6 +1274,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
|
||||
(self.scheduler, ", ".join(obj.name for obj in schedulers)))
|
||||
|
||||
|
||||
def get_schedulers(self):
|
||||
schedulers = set(obj for obj in globals().values()
|
||||
if type(obj) is type and
|
||||
@@ -1325,10 +1336,9 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if self.rqdata.taskData.abort:
|
||||
self.rq.state = runQueueCleanUp
|
||||
|
||||
def task_skip(self, task, reason):
|
||||
def task_skip(self, task):
|
||||
self.runq_running[task] = 1
|
||||
self.runq_buildable[task] = 1
|
||||
bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
|
||||
self.task_completeoutright(task)
|
||||
self.stats.taskCompleted()
|
||||
self.stats.taskSkipped()
|
||||
@@ -1338,9 +1348,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
Run the tasks in a queue prepared by rqdata.prepare()
|
||||
"""
|
||||
|
||||
self.rq.read_workers()
|
||||
|
||||
|
||||
if self.stats.total == 0:
|
||||
# nothing to do
|
||||
self.rq.state = runQueueCleanUp
|
||||
@@ -1353,13 +1360,13 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if task in self.rq.scenequeue_covered:
|
||||
logger.debug(2, "Setscene covered task %s (%s)", task,
|
||||
self.rqdata.get_user_idstring(task))
|
||||
self.task_skip(task, "covered")
|
||||
self.task_skip(task)
|
||||
return True
|
||||
|
||||
if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
|
||||
logger.debug(2, "Stamp current task %s (%s)", task,
|
||||
self.rqdata.get_user_idstring(task))
|
||||
self.task_skip(task, "existing")
|
||||
self.task_skip(task)
|
||||
return True
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
@@ -1376,25 +1383,23 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
startevent = runQueueTaskStarted(task, self.stats, self.rq)
|
||||
bb.event.fire(startevent, self.cfgData)
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
|
||||
if not self.rq.fakeworker:
|
||||
self.rq.start_fakeworker(self)
|
||||
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
|
||||
self.rq.fakeworker.stdin.flush()
|
||||
else:
|
||||
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
|
||||
self.rq.worker.stdin.flush()
|
||||
pid, pipein, pipeout = self.fork_off_task(fn, task, taskname)
|
||||
|
||||
self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData)
|
||||
self.build_stamps[pid] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
|
||||
self.runq_running[task] = 1
|
||||
self.stats.taskActive()
|
||||
if self.stats.active < self.number_tasks:
|
||||
return True
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
self.rq.read_workers()
|
||||
return self.rq.active_fds()
|
||||
if self.runqueue_process_waitpid() is None:
|
||||
return 0.5
|
||||
return True
|
||||
|
||||
if len(self.failed_fnids) != 0:
|
||||
self.rq.state = runQueueFailed
|
||||
@@ -1409,7 +1414,6 @@ class RunQueueExecuteTasks(RunQueueExecute):
|
||||
if self.runq_complete[task] == 0:
|
||||
logger.error("Task %s never completed!", task)
|
||||
self.rq.state = runQueueComplete
|
||||
|
||||
return True
|
||||
|
||||
class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
@@ -1431,7 +1435,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
sq_revdeps = []
|
||||
sq_revdeps_new = []
|
||||
sq_revdeps_squash = []
|
||||
self.sq_harddeps = []
|
||||
|
||||
# We need to construct a dependency graph for the setscene functions. Intermediate
|
||||
# dependencies between the setscene tasks only complicate the code. This code
|
||||
@@ -1544,7 +1547,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
if taskid is None:
|
||||
bb.msg.fatal("RunQueue", "Task %s:%s depends upon non-existent task %s:%s" % (self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realid]], self.rqdata.taskData.tasks_name[realid], dep, idependtask))
|
||||
|
||||
self.sq_harddeps.append(self.rqdata.runq_setscene.index(taskid))
|
||||
sq_revdeps_squash[self.rqdata.runq_setscene.index(task)].add(self.rqdata.runq_setscene.index(taskid))
|
||||
# Have to zero this to avoid circular dependencies
|
||||
sq_revdeps_squash[self.rqdata.runq_setscene.index(taskid)] = set()
|
||||
@@ -1604,7 +1606,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
sq_taskname.append(taskname)
|
||||
sq_task.append(task)
|
||||
call = self.rq.hashvalidate + "(sq_fn, sq_task, sq_hash, sq_hashfn, d)"
|
||||
locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.data }
|
||||
locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.configuration.data }
|
||||
valid = bb.utils.better_eval(call, locs)
|
||||
|
||||
valid_new = stamppresent
|
||||
@@ -1622,10 +1624,8 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
|
||||
self.rq.state = runQueueSceneRun
|
||||
|
||||
def scenequeue_updatecounters(self, task, fail = False):
|
||||
def scenequeue_updatecounters(self, task):
|
||||
for dep in self.sq_deps[task]:
|
||||
if fail and task in self.sq_harddeps:
|
||||
continue
|
||||
self.sq_revdeps2[dep].remove(task)
|
||||
if len(self.sq_revdeps2[dep]) == 0:
|
||||
self.runq_buildable[dep] = 1
|
||||
@@ -1646,14 +1646,13 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
|
||||
def task_complete(self, task):
|
||||
self.stats.taskCompleted()
|
||||
bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
|
||||
self.task_completeoutright(task)
|
||||
|
||||
def task_fail(self, task, result):
|
||||
self.stats.taskFailed()
|
||||
bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
|
||||
self.scenequeue_notcovered.add(task)
|
||||
self.scenequeue_updatecounters(task, True)
|
||||
self.scenequeue_updatecounters(task)
|
||||
|
||||
def task_failoutright(self, task):
|
||||
self.runq_running[task] = 1
|
||||
@@ -1662,7 +1661,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
self.stats.taskSkipped()
|
||||
index = self.rqdata.runq_setscene[task]
|
||||
self.scenequeue_notcovered.add(task)
|
||||
self.scenequeue_updatecounters(task, True)
|
||||
self.scenequeue_updatecounters(task)
|
||||
|
||||
def task_skip(self, task):
|
||||
self.runq_running[task] = 1
|
||||
@@ -1676,8 +1675,6 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
Run the tasks in a queue prepared by prepare_runqueue
|
||||
"""
|
||||
|
||||
self.rq.read_workers()
|
||||
|
||||
task = None
|
||||
if self.stats.active < self.number_tasks:
|
||||
# Find the next setscene to run
|
||||
@@ -1718,24 +1715,22 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
|
||||
bb.event.fire(startevent, self.cfgData)
|
||||
|
||||
taskdep = self.rqdata.dataCache.task_deps[fn]
|
||||
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot']:
|
||||
if not self.rq.fakeworker:
|
||||
self.rq.start_fakeworker(self)
|
||||
self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
|
||||
self.rq.fakeworker.stdin.flush()
|
||||
else:
|
||||
self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn))) + "</runtask>")
|
||||
self.rq.worker.stdin.flush()
|
||||
pid, pipein, pipeout = self.fork_off_task(fn, realtask, taskname)
|
||||
|
||||
self.build_pids[pid] = task
|
||||
self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData)
|
||||
self.runq_running[task] = 1
|
||||
self.stats.taskActive()
|
||||
if self.stats.active < self.number_tasks:
|
||||
return True
|
||||
|
||||
for pipe in self.build_pipes:
|
||||
self.build_pipes[pipe].read()
|
||||
|
||||
if self.stats.active > 0:
|
||||
self.rq.read_workers()
|
||||
return self.rq.active_fds()
|
||||
if self.runqueue_process_waitpid() is None:
|
||||
return 0.5
|
||||
return True
|
||||
|
||||
# Convert scenequeue_covered task numbers into full taskgraph ids
|
||||
oldcovered = self.scenequeue_covered
|
||||
@@ -1751,10 +1746,8 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
|
||||
self.rq.state = runQueueRunInit
|
||||
return True
|
||||
|
||||
def runqueue_process_waitpid(self, task, status):
|
||||
task = self.rq.rqdata.runq_setscene.index(task)
|
||||
|
||||
RunQueueExecute.runqueue_process_waitpid(self, task, status)
|
||||
def fork_off_task(self, fn, task, taskname):
|
||||
return RunQueueExecute.fork_off_task(self, fn, task, taskname, quieterrors=True)
|
||||
|
||||
class TaskFailure(Exception):
|
||||
"""
|
||||
@@ -1830,60 +1823,29 @@ class runQueueTaskCompleted(runQueueEvent):
|
||||
Event notifing a task completed
|
||||
"""
|
||||
|
||||
class sceneQueueTaskCompleted(sceneQueueEvent):
|
||||
"""
|
||||
Event notifing a setscene task completed
|
||||
"""
|
||||
|
||||
class runQueueTaskSkipped(runQueueEvent):
|
||||
"""
|
||||
Event notifing a task was skipped
|
||||
"""
|
||||
def __init__(self, task, stats, rq, reason):
|
||||
runQueueEvent.__init__(self, task, stats, rq)
|
||||
self.reason = reason
|
||||
|
||||
class runQueuePipe():
|
||||
"""
|
||||
Abstraction for a pipe between a worker thread and the server
|
||||
"""
|
||||
def __init__(self, pipein, pipeout, d, rq):
|
||||
def __init__(self, pipein, pipeout, d):
|
||||
self.input = pipein
|
||||
if pipeout:
|
||||
pipeout.close()
|
||||
pipeout.close()
|
||||
bb.utils.nonblockingfd(self.input)
|
||||
self.queue = ""
|
||||
self.d = d
|
||||
self.rq = rq
|
||||
|
||||
def setrunqueueexec(self, rq):
|
||||
self.rq = rq
|
||||
|
||||
def read(self):
|
||||
start = len(self.queue)
|
||||
try:
|
||||
self.queue = self.queue + self.input.read(102400)
|
||||
except (OSError, IOError) as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
end = len(self.queue)
|
||||
found = True
|
||||
while found and len(self.queue):
|
||||
found = False
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1:
|
||||
bb.event.fire_from_worker(self.queue[:index+8], self.d)
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find("</event>")
|
||||
while index != -1 and self.queue.startswith("<event>"):
|
||||
event = pickle.loads(self.queue[7:index])
|
||||
bb.event.fire_from_worker(event, self.d)
|
||||
found = True
|
||||
self.queue = self.queue[index+8:]
|
||||
index = self.queue.find("</event>")
|
||||
index = self.queue.find("</exitcode>")
|
||||
while index != -1 and self.queue.startswith("<exitcode>"):
|
||||
task, status = pickle.loads(self.queue[10:index])
|
||||
self.rq.runqueue_process_waitpid(task, status)
|
||||
found = True
|
||||
self.queue = self.queue[index+11:]
|
||||
index = self.queue.find("</exitcode>")
|
||||
return (end > start)
|
||||
|
||||
def close(self):
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#
|
||||
# BitBake Base Server Code
|
||||
#
|
||||
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2006 - 2008 Richard Purdie
|
||||
# Copyright (C) 2013 Alexandru Damian
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
""" Base code for Bitbake server process
|
||||
|
||||
Have a common base for that all Bitbake server classes ensures a consistent
|
||||
approach to the interface, and minimize risks associated with code duplication.
|
||||
|
||||
"""
|
||||
|
||||
""" BaseImplServer() the base class for all XXServer() implementations.
|
||||
|
||||
These classes contain the actual code that runs the server side, i.e.
|
||||
listens for the commands and executes them. Although these implementations
|
||||
contain all the data of the original bitbake command, i.e the cooker instance,
|
||||
they may well run on a different process or even machine.
|
||||
|
||||
"""
|
||||
|
||||
class BaseImplServer():
|
||||
def __init__(self):
|
||||
self._idlefuns = {}
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
|
||||
|
||||
""" BitBakeBaseServerConnection class is the common ancestor to all
|
||||
BitBakeServerConnection classes.
|
||||
|
||||
These classes control the remote server. The only command currently
|
||||
implemented is the terminate() command.
|
||||
|
||||
"""
|
||||
|
||||
class BitBakeBaseServerConnection():
|
||||
def __init__(self, serverImpl):
|
||||
pass
|
||||
|
||||
def terminate(self):
|
||||
pass
|
||||
|
||||
|
||||
""" BitBakeBaseServer class is the common ancestor to all Bitbake servers
|
||||
|
||||
Derive this class in order to implement a BitBakeServer which is the
|
||||
controlling stub for the actual server implementation
|
||||
|
||||
"""
|
||||
class BitBakeBaseServer(object):
|
||||
def initServer(self):
|
||||
self.serverImpl = None # we ensure a runtime crash if not overloaded
|
||||
self.connection = None
|
||||
return
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.serverImpl.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.serverImpl.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def detach(self):
|
||||
return
|
||||
|
||||
def establishConnection(self):
|
||||
raise "Must redefine the %s.establishConnection()" % self.__class__.__name__
|
||||
|
||||
def endSession(self):
|
||||
self.connection.terminate()
|
||||
|
||||
203
bitbake/lib/bb/server/none.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#
|
||||
# BitBake 'dummy' Passthrough Server
|
||||
#
|
||||
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
||||
# Copyright (C) 2006 - 2008 Richard Purdie
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
This module implements a passthrough server for BitBake.
|
||||
|
||||
Use register_idle_function() to add a function which the server
|
||||
calls from within idle_commands when no requests are pending. Make sure
|
||||
that those functions are non-blocking or else you will introduce latency
|
||||
in the server's main loop.
|
||||
"""
|
||||
|
||||
import time
|
||||
import bb
|
||||
import signal
|
||||
|
||||
DEBUG = False
|
||||
|
||||
import inspect, select
|
||||
|
||||
class BitBakeServerCommands():
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
|
||||
def runCommand(self, command):
|
||||
"""
|
||||
Run a cooker command on the server
|
||||
"""
|
||||
#print "Running Command %s" % command
|
||||
return self.cooker.command.runCommand(command)
|
||||
|
||||
def terminateServer(self):
|
||||
"""
|
||||
Trigger the server to quit
|
||||
"""
|
||||
self.server.server_exit()
|
||||
#print "Server (cooker) exitting"
|
||||
return
|
||||
|
||||
def ping(self):
|
||||
"""
|
||||
Dummy method which can be used to check the server is still alive
|
||||
"""
|
||||
return True
|
||||
|
||||
eventQueue = []
|
||||
|
||||
class BBUIEventQueue:
|
||||
class event:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
@staticmethod
|
||||
def send(event):
|
||||
bb.server.none.eventQueue.append(event)
|
||||
@staticmethod
|
||||
def quit():
|
||||
return
|
||||
|
||||
def __init__(self, BBServer):
|
||||
self.eventQueue = bb.server.none.eventQueue
|
||||
self.BBServer = BBServer
|
||||
self.EventHandle = bb.event.register_UIHhandler(self)
|
||||
|
||||
def __popEvent(self):
|
||||
if len(self.eventQueue) == 0:
|
||||
return None
|
||||
return self.eventQueue.pop(0)
|
||||
|
||||
def getEvent(self):
|
||||
if len(self.eventQueue) == 0:
|
||||
self.BBServer.idle_commands(0)
|
||||
return self.__popEvent()
|
||||
|
||||
def waitEvent(self, delay):
|
||||
event = self.__popEvent()
|
||||
if event:
|
||||
return event
|
||||
self.BBServer.idle_commands(delay)
|
||||
return self.__popEvent()
|
||||
|
||||
def queue_event(self, event):
|
||||
self.eventQueue.append(event)
|
||||
|
||||
def system_quit( self ):
|
||||
bb.event.unregister_UIHhandler(self.EventHandle)
|
||||
|
||||
# Dummy signal handler to ensure we break out of sleep upon SIGCHLD
|
||||
def chldhandler(signum, stackframe):
|
||||
pass
|
||||
|
||||
class BitBakeNoneServer():
|
||||
# remove this when you're done with debugging
|
||||
# allow_reuse_address = True
|
||||
|
||||
def __init__(self):
|
||||
self._idlefuns = {}
|
||||
self.commands = BitBakeServerCommands(self)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.commands.cooker = cooker
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
def idle_commands(self, delay):
|
||||
#print "Idle queue length %s" % len(self._idlefuns)
|
||||
#print "Idle timeout, running idle functions"
|
||||
#if len(self._idlefuns) == 0:
|
||||
nextsleep = delay
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
#print "Idle function returned %s" % (retval)
|
||||
if retval is False:
|
||||
del self._idlefuns[function]
|
||||
elif retval is True:
|
||||
nextsleep = None
|
||||
elif nextsleep is None:
|
||||
continue
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.commands.runCommand(["stateShutdown"])
|
||||
pass
|
||||
if nextsleep is not None:
|
||||
#print "Sleeping for %s (%s)" % (nextsleep, delay)
|
||||
signal.signal(signal.SIGCHLD, chldhandler)
|
||||
time.sleep(nextsleep)
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
|
||||
def server_exit(self):
|
||||
# Tell idle functions we're exiting
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, True)
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, server):
|
||||
self.server = server.server
|
||||
self.connection = self.server.commands
|
||||
self.events = bb.server.none.BBUIEventQueue(self.server)
|
||||
for event in bb.event.ui_queue:
|
||||
self.events.queue_event(event)
|
||||
|
||||
def terminate(self):
|
||||
try:
|
||||
self.events.system_quit()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.connection.terminateServer()
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServer(object):
|
||||
def initServer(self):
|
||||
self.server = BitBakeNoneServer()
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def detach(self):
|
||||
return
|
||||
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeServerConnection(self)
|
||||
return self.connection
|
||||
|
||||
def launchUI(self, uifunc, *args):
|
||||
return bb.cooker.server_main(self.cooker, uifunc, *args)
|
||||
|
||||
@@ -29,18 +29,14 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import select
|
||||
from Queue import Empty
|
||||
from multiprocessing import Event, Process, util, Queue, Pipe, queues
|
||||
|
||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||
|
||||
logger = logging.getLogger('BitBake')
|
||||
|
||||
class ServerCommunicator():
|
||||
def __init__(self, connection, event_handle):
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
self.event_handle = event_handle
|
||||
|
||||
def runCommand(self, command):
|
||||
# @todo try/except
|
||||
@@ -56,8 +52,6 @@ class ServerCommunicator():
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def getEventHandle(self):
|
||||
return self.event_handle.value
|
||||
|
||||
class EventAdapter():
|
||||
"""
|
||||
@@ -74,26 +68,30 @@ class EventAdapter():
|
||||
print("EventAdapter puked: %s" % str(err))
|
||||
|
||||
|
||||
class ProcessServer(Process, BaseImplServer):
|
||||
class ProcessServer(Process):
|
||||
profile_filename = "profile.log"
|
||||
profile_processed_filename = "profile.log.processed"
|
||||
|
||||
def __init__(self, command_channel, event_queue):
|
||||
BaseImplServer.__init__(self)
|
||||
Process.__init__(self)
|
||||
self.command_channel = command_channel
|
||||
self.event_queue = event_queue
|
||||
self.event = EventAdapter(event_queue)
|
||||
self._idlefunctions = {}
|
||||
self.quit = False
|
||||
|
||||
self.keep_running = Event()
|
||||
self.keep_running.set()
|
||||
self.event_handle = multiprocessing.Value("i")
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefunctions[function] = data
|
||||
|
||||
def run(self):
|
||||
for event in bb.event.ui_queue:
|
||||
self.event_queue.put(event)
|
||||
self.event_handle.value = bb.event.register_UIHhandler(self)
|
||||
self.event_handle = bb.event.register_UIHhandler(self)
|
||||
bb.cooker.server_main(self.cooker, self.main)
|
||||
|
||||
def main(self):
|
||||
@@ -106,37 +104,37 @@ class ProcessServer(Process, BaseImplServer):
|
||||
command = self.command_channel.recv()
|
||||
self.runCommand(command)
|
||||
|
||||
self.idle_commands(.1, [self.event_queue._reader, self.command_channel])
|
||||
self.idle_commands(.1)
|
||||
except Exception:
|
||||
logger.exception('Running command %s', command)
|
||||
|
||||
self.event_queue.close()
|
||||
bb.event.unregister_UIHhandler(self.event_handle.value)
|
||||
self.event_queue.cancel_join_thread()
|
||||
bb.event.unregister_UIHhandler(self.event_handle)
|
||||
self.command_channel.close()
|
||||
self.cooker.shutdown(True)
|
||||
self.cooker.stop()
|
||||
self.idle_commands(.1)
|
||||
|
||||
def idle_commands(self, delay, fds = []):
|
||||
def idle_commands(self, delay):
|
||||
nextsleep = delay
|
||||
|
||||
for function, data in self._idlefuns.items():
|
||||
for function, data in self._idlefunctions.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
if retval is False:
|
||||
del self._idlefuns[function]
|
||||
del self._idlefunctions[function]
|
||||
elif retval is True:
|
||||
nextsleep = None
|
||||
elif nextsleep is None:
|
||||
continue
|
||||
else:
|
||||
fds = fds + retval
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception:
|
||||
logger.exception('Running idle function')
|
||||
|
||||
if nextsleep is not None:
|
||||
select.select(fds,[],[],nextsleep)
|
||||
time.sleep(nextsleep)
|
||||
|
||||
def runCommand(self, command):
|
||||
"""
|
||||
@@ -147,33 +145,80 @@ class ProcessServer(Process, BaseImplServer):
|
||||
def stop(self):
|
||||
self.keep_running.clear()
|
||||
|
||||
class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
|
||||
def __init__(self, serverImpl, ui_channel, event_queue):
|
||||
self.procserver = serverImpl
|
||||
self.ui_channel = ui_channel
|
||||
self.event_queue = event_queue
|
||||
self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle)
|
||||
self.events = self.event_queue
|
||||
def bootstrap_2_6_6(self):
|
||||
"""Pulled from python 2.6.6. Needed to ensure we have the fix from
|
||||
http://bugs.python.org/issue5313 when running on python version 2.6.2
|
||||
or lower."""
|
||||
|
||||
def terminate(self):
|
||||
def flushevents():
|
||||
while True:
|
||||
try:
|
||||
event = self.event_queue.get(block=False)
|
||||
except (Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
try:
|
||||
self._children = set()
|
||||
self._counter = itertools.count(1)
|
||||
try:
|
||||
sys.stdin.close()
|
||||
sys.stdin = open(os.devnull)
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
multiprocessing._current_process = self
|
||||
util._finalizer_registry.clear()
|
||||
util._run_after_forkers()
|
||||
util.info('child process calling self.run()')
|
||||
try:
|
||||
self.run()
|
||||
exitcode = 0
|
||||
finally:
|
||||
util._exit_function()
|
||||
except SystemExit as e:
|
||||
if not e.args:
|
||||
exitcode = 1
|
||||
elif type(e.args[0]) is int:
|
||||
exitcode = e.args[0]
|
||||
else:
|
||||
sys.stderr.write(e.args[0] + '\n')
|
||||
sys.stderr.flush()
|
||||
exitcode = 1
|
||||
except:
|
||||
exitcode = 1
|
||||
import traceback
|
||||
sys.stderr.write('Process %s:\n' % self.name)
|
||||
sys.stderr.flush()
|
||||
traceback.print_exc()
|
||||
|
||||
util.info('process exiting with exitcode %d' % exitcode)
|
||||
return exitcode
|
||||
|
||||
# Python versions 2.6.0 through 2.6.2 suffer from a multiprocessing bug
|
||||
# which can result in a bitbake server hang during the parsing process
|
||||
if (2, 6, 0) <= sys.version_info < (2, 6, 3):
|
||||
_bootstrap = bootstrap_2_6_6
|
||||
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.procserver = server.server
|
||||
self.connection = ServerCommunicator(server.ui_channel)
|
||||
self.events = server.event_queue
|
||||
|
||||
def terminate(self, force = False):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
self.procserver.stop()
|
||||
|
||||
while self.procserver.is_alive():
|
||||
flushevents()
|
||||
self.procserver.join(0.1)
|
||||
|
||||
self.ui_channel.close()
|
||||
self.event_queue.close()
|
||||
if force:
|
||||
self.procserver.join(0.5)
|
||||
if self.procserver.is_alive():
|
||||
self.procserver.terminate()
|
||||
self.procserver.join()
|
||||
else:
|
||||
self.procserver.join()
|
||||
while True:
|
||||
try:
|
||||
event = self.server.event_queue.get(block=False)
|
||||
except (Empty, IOError):
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
logger.handle(event)
|
||||
self.server.ui_channel.close()
|
||||
self.server.event_queue.close()
|
||||
if force:
|
||||
sys.exit(1)
|
||||
|
||||
# Wrap Queue to provide API which isn't server implementation specific
|
||||
class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
@@ -190,7 +235,7 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
|
||||
return None
|
||||
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
class BitBakeServer(object):
|
||||
def initServer(self):
|
||||
# establish communication channels. We use bidirectional pipes for
|
||||
# ui <--> server command/response pairs
|
||||
@@ -198,13 +243,28 @@ class BitBakeServer(BitBakeBaseServer):
|
||||
#
|
||||
self.ui_channel, self.server_channel = Pipe()
|
||||
self.event_queue = ProcessEventQueue(0)
|
||||
self.serverImpl = ProcessServer(self.server_channel, self.event_queue)
|
||||
|
||||
self.server = ProcessServer(self.server_channel, self.event_queue)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.cooker = cooker
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
return
|
||||
|
||||
def detach(self):
|
||||
self.serverImpl.start()
|
||||
self.server.start()
|
||||
return
|
||||
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
|
||||
signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate())
|
||||
self.connection = BitBakeServerConnection(self)
|
||||
signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate(force=True))
|
||||
return self.connection
|
||||
|
||||
def launchUI(self, uifunc, *args):
|
||||
return uifunc(*args)
|
||||
|
||||
|
||||
@@ -35,68 +35,103 @@ import bb
|
||||
import xmlrpclib, sys
|
||||
from bb import daemonize
|
||||
from bb.ui import uievent
|
||||
import hashlib, time
|
||||
import socket
|
||||
import os, signal
|
||||
import threading
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
DEBUG = False
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
import inspect, select, httplib
|
||||
import inspect, select
|
||||
|
||||
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
|
||||
if sys.hexversion < 0x020600F0:
|
||||
print("Sorry, python 2.6 or later is required for bitbake's XMLRPC mode")
|
||||
sys.exit(1)
|
||||
|
||||
##
|
||||
# The xmlrpclib.Transport class has undergone various changes in Python 2.7
|
||||
# which break BitBake's XMLRPC implementation.
|
||||
# To work around this we subclass Transport and have a copy/paste of method
|
||||
# implementations from Python 2.6.6's xmlrpclib.
|
||||
#
|
||||
# Upstream Python bug is #8194 (http://bugs.python.org/issue8194)
|
||||
# This bug is relevant for Python 2.7.0 and 2.7.1 but was fixed for
|
||||
# Python > 2.7.2
|
||||
##
|
||||
|
||||
class BBTransport(xmlrpclib.Transport):
|
||||
def __init__(self, timeout):
|
||||
self.timeout = timeout
|
||||
self.connection_token = None
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
def request(self, host, handler, request_body, verbose=0):
|
||||
h = self.make_connection(host)
|
||||
if verbose:
|
||||
h.set_debuglevel(1)
|
||||
|
||||
self.send_request(h, handler, request_body)
|
||||
self.send_host(h, host)
|
||||
self.send_user_agent(h)
|
||||
self.send_content(h, request_body)
|
||||
|
||||
errcode, errmsg, headers = h.getreply()
|
||||
|
||||
if errcode != 200:
|
||||
raise ProtocolError(
|
||||
host + handler,
|
||||
errcode, errmsg,
|
||||
headers
|
||||
)
|
||||
|
||||
self.verbose = verbose
|
||||
|
||||
try:
|
||||
sock = h._conn.sock
|
||||
except AttributeError:
|
||||
sock = None
|
||||
|
||||
return self._parse_response(h.getfile(), sock)
|
||||
|
||||
# Modified from default to pass timeout to HTTPConnection
|
||||
def make_connection(self, host):
|
||||
#return an existing connection if possible. This allows
|
||||
#HTTP/1.1 keep-alive.
|
||||
if self._connection and host == self._connection[0]:
|
||||
return self._connection[1]
|
||||
import httplib
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
return httplib.HTTP(host)
|
||||
|
||||
# create a HTTP connection object from a host descriptor
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
#store the host argument along with the connection object
|
||||
self._connection = host, httplib.HTTPConnection(chost, timeout=self.timeout)
|
||||
return self._connection[1]
|
||||
def _parse_response(self, file, sock):
|
||||
p, u = self.getparser()
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection_token = token
|
||||
while 1:
|
||||
if sock:
|
||||
response = sock.recv(1024)
|
||||
else:
|
||||
response = file.read(1024)
|
||||
if not response:
|
||||
break
|
||||
if self.verbose:
|
||||
print "body:", repr(response)
|
||||
p.feed(response)
|
||||
|
||||
def send_content(self, h, body):
|
||||
if self.connection_token:
|
||||
h.putheader("Bitbake-token", self.connection_token)
|
||||
xmlrpclib.Transport.send_content(self, h, body)
|
||||
file.close()
|
||||
p.close()
|
||||
|
||||
def _create_server(host, port, timeout = 60):
|
||||
t = BBTransport(timeout)
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), transport=t, allow_none=True)
|
||||
return s, t
|
||||
return u.close()
|
||||
|
||||
def _create_server(host, port):
|
||||
# Python 2.7.0 and 2.7.1 have a buggy Transport implementation
|
||||
# For those versions of Python, and only those versions, use our
|
||||
# own copy/paste BBTransport class.
|
||||
if (2, 7, 0) <= sys.version_info < (2, 7, 2):
|
||||
t = BBTransport()
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), transport=t, allow_none=True)
|
||||
else:
|
||||
s = xmlrpclib.Server("http://%s:%d/" % (host, port), allow_none=True)
|
||||
|
||||
return s
|
||||
|
||||
class BitBakeServerCommands():
|
||||
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.has_client = False
|
||||
|
||||
def registerEventHandler(self, host, port):
|
||||
"""
|
||||
Register a remote UI Event Handler
|
||||
"""
|
||||
s, t = _create_server(host, port)
|
||||
s = _create_server(host, port)
|
||||
|
||||
self.event_handle = bb.event.register_UIHhandler(s)
|
||||
return self.event_handle
|
||||
return bb.event.register_UIHhandler(s)
|
||||
|
||||
def unregisterEventHandler(self, handlerNum):
|
||||
"""
|
||||
@@ -108,10 +143,7 @@ class BitBakeServerCommands():
|
||||
"""
|
||||
Run a cooker command on the server
|
||||
"""
|
||||
return self.cooker.command.runCommand(command, self.server.readonly)
|
||||
|
||||
def getEventHandle(self):
|
||||
return self.event_handle
|
||||
return self.cooker.command.runCommand(command)
|
||||
|
||||
def terminateServer(self):
|
||||
"""
|
||||
@@ -121,64 +153,13 @@ class BitBakeServerCommands():
|
||||
print("Server (cooker) exiting")
|
||||
return
|
||||
|
||||
def addClient(self):
|
||||
if self.has_client:
|
||||
return None
|
||||
token = hashlib.md5(str(time.time())).hexdigest()
|
||||
self.server.set_connection_token(token)
|
||||
self.has_client = True
|
||||
return token
|
||||
def ping(self):
|
||||
"""
|
||||
Dummy method which can be used to check the server is still alive
|
||||
"""
|
||||
return True
|
||||
|
||||
def removeClient(self):
|
||||
if self.has_client:
|
||||
self.server.set_connection_token(None)
|
||||
self.has_client = False
|
||||
if self.server.single_use:
|
||||
self.server.quit = True
|
||||
|
||||
# This request handler checks if the request has a "Bitbake-token" header
|
||||
# field (this comes from the client side) and compares it with its internal
|
||||
# "Bitbake-token" field (this comes from the server). If the two are not
|
||||
# equal, it is assumed that a client is trying to connect to the server
|
||||
# while another client is connected to the server. In this case, a 503 error
|
||||
# ("service unavailable") is returned to the client.
|
||||
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
def __init__(self, request, client_address, server):
|
||||
self.server = server
|
||||
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
||||
|
||||
def do_POST(self):
|
||||
try:
|
||||
remote_token = self.headers["Bitbake-token"]
|
||||
except:
|
||||
remote_token = None
|
||||
if remote_token != self.server.connection_token and remote_token != "observer":
|
||||
self.report_503()
|
||||
else:
|
||||
if remote_token == "observer":
|
||||
self.server.readonly = True
|
||||
else:
|
||||
self.server.readonly = False
|
||||
SimpleXMLRPCRequestHandler.do_POST(self)
|
||||
|
||||
def report_503(self):
|
||||
self.send_response(503)
|
||||
response = 'No more client allowed'
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
|
||||
class XMLRPCProxyServer(BaseImplServer):
|
||||
""" not a real working server, but a stub for a proxy server connection
|
||||
|
||||
"""
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||
# remove this when you're done with debugging
|
||||
# allow_reuse_address = True
|
||||
|
||||
@@ -186,22 +167,17 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
BaseImplServer.__init__(self)
|
||||
SimpleXMLRPCServer.__init__(self, interface,
|
||||
requestHandler=BitBakeXMLRPCRequestHandler,
|
||||
requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=False, allow_none=True)
|
||||
self._idlefuns = {}
|
||||
self.host, self.port = self.socket.getsockname()
|
||||
self.connection_token = None
|
||||
#self.register_introspection_functions()
|
||||
self.commands = BitBakeServerCommands(self)
|
||||
self.autoregister_all_functions(self.commands, "")
|
||||
self.interface = interface
|
||||
self.single_use = False
|
||||
if (interface[1] == 0): # anonymous port, not getting reused
|
||||
self.single_use = True
|
||||
|
||||
def addcooker(self, cooker):
|
||||
BaseImplServer.addcooker(self, cooker)
|
||||
self.cooker = cooker
|
||||
self.commands.cooker = cooker
|
||||
|
||||
def autoregister_all_functions(self, context, prefix):
|
||||
@@ -214,9 +190,12 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
if name.startswith(prefix):
|
||||
self.register_function(method, name[len(prefix):])
|
||||
|
||||
def register_idle_function(self, function, data):
|
||||
"""Register a function to be called while the server is idle"""
|
||||
assert hasattr(function, '__call__')
|
||||
self._idlefuns[function] = data
|
||||
|
||||
def serve_forever(self):
|
||||
# Start the actual XMLRPC server
|
||||
bb.cooker.server_main(self.cooker, self._serve_forever)
|
||||
|
||||
def _serve_forever(self):
|
||||
@@ -224,9 +203,12 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
Serve Requests. Overloaded to honor a quit command
|
||||
"""
|
||||
self.quit = False
|
||||
self.timeout = 0 # Run Idle calls for our first callback
|
||||
while not self.quit:
|
||||
fds = [self]
|
||||
nextsleep = 0.1
|
||||
#print "Idle queue length %s" % len(self._idlefuns)
|
||||
self.handle_request()
|
||||
#print "Idle timeout, running idle functions"
|
||||
nextsleep = None
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, False)
|
||||
@@ -234,57 +216,42 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||
del self._idlefuns[function]
|
||||
elif retval is True:
|
||||
nextsleep = 0
|
||||
else:
|
||||
fds = fds + retval
|
||||
elif nextsleep is 0:
|
||||
continue
|
||||
elif nextsleep is None:
|
||||
nextsleep = retval
|
||||
elif retval < nextsleep:
|
||||
nextsleep = retval
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
socktimeout = self.socket.gettimeout() or nextsleep
|
||||
socktimeout = min(socktimeout, nextsleep)
|
||||
# Mirror what BaseServer handle_request would do
|
||||
fd_sets = select.select(fds, [], [], socktimeout)
|
||||
if fd_sets[0] and self in fd_sets[0]:
|
||||
self._handle_request_noblock()
|
||||
|
||||
if nextsleep is None and len(self._idlefuns) > 0:
|
||||
nextsleep = 0
|
||||
self.timeout = nextsleep
|
||||
# Tell idle functions we're exiting
|
||||
for function, data in self._idlefuns.items():
|
||||
try:
|
||||
retval = function(self, data, True)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.server_close()
|
||||
return
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection_token = token
|
||||
class BitbakeServerInfo():
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||
def __init__(self, serverImpl, clientinfo=("localhost", 0), observer_only = False):
|
||||
self.connection, self.transport = _create_server(serverImpl.host, serverImpl.port)
|
||||
self.clientinfo = clientinfo
|
||||
self.serverImpl = serverImpl
|
||||
self.observer_only = observer_only
|
||||
|
||||
def connect(self):
|
||||
if not self.observer_only:
|
||||
token = self.connection.addClient()
|
||||
else:
|
||||
token = "observer"
|
||||
if token is None:
|
||||
return None
|
||||
self.transport.set_connection_token(token)
|
||||
self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo)
|
||||
class BitBakeServerConnection():
|
||||
def __init__(self, serverinfo, clientinfo=("localhost", 0)):
|
||||
self.connection = _create_server(serverinfo.host, serverinfo.port)
|
||||
self.events = uievent.BBUIEventQueue(self.connection, clientinfo)
|
||||
for event in bb.event.ui_queue:
|
||||
self.events.queue_event(event)
|
||||
return self
|
||||
|
||||
def removeClient(self):
|
||||
if not self.observer_only:
|
||||
self.connection.removeClient()
|
||||
|
||||
def terminate(self):
|
||||
# Don't wait for server indefinitely
|
||||
@@ -295,53 +262,34 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.connection.removeClient()
|
||||
self.connection.terminateServer()
|
||||
except:
|
||||
pass
|
||||
|
||||
class BitBakeServer(BitBakeBaseServer):
|
||||
class BitBakeServer(object):
|
||||
def initServer(self, interface = ("localhost", 0)):
|
||||
self.serverImpl = XMLRPCServer(interface)
|
||||
self.server = BitBakeXMLRPCServer(interface)
|
||||
|
||||
def addcooker(self, cooker):
|
||||
self.cooker = cooker
|
||||
self.server.addcooker(cooker)
|
||||
|
||||
def getServerIdleCB(self):
|
||||
return self.server.register_idle_function
|
||||
|
||||
def saveConnectionDetails(self):
|
||||
self.serverinfo = BitbakeServerInfo(self.server.host, self.server.port)
|
||||
|
||||
def detach(self):
|
||||
daemonize.createDaemon(self.serverImpl.serve_forever, "bitbake-cookerdaemon.log")
|
||||
daemonize.createDaemon(self.server.serve_forever, "bitbake-cookerdaemon.log")
|
||||
del self.cooker
|
||||
del self.server
|
||||
|
||||
def establishConnection(self):
|
||||
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl)
|
||||
return self.connection.connect()
|
||||
self.connection = BitBakeServerConnection(self.serverinfo)
|
||||
return self.connection
|
||||
|
||||
def set_connection_token(self, token):
|
||||
self.connection.transport.set_connection_token(token)
|
||||
def launchUI(self, uifunc, *args):
|
||||
return uifunc(*args)
|
||||
|
||||
class BitBakeXMLRPCClient(BitBakeBaseServer):
|
||||
|
||||
def __init__(self, observer_only = False):
|
||||
self.observer_only = observer_only
|
||||
pass
|
||||
|
||||
def saveConnectionDetails(self, remote):
|
||||
self.remote = remote
|
||||
|
||||
def establishConnection(self):
|
||||
# The format of "remote" must be "server:port"
|
||||
try:
|
||||
[host, port] = self.remote.split(":")
|
||||
port = int(port)
|
||||
except:
|
||||
return None
|
||||
# We need our IP for the server connection. We get the IP
|
||||
# by trying to connect with the server
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect((host, port))
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
return None
|
||||
self.serverImpl = XMLRPCProxyServer(host, port)
|
||||
self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only)
|
||||
return self.connection.connect()
|
||||
|
||||
def endSession(self):
|
||||
self.connection.removeClient()
|
||||
|
||||
@@ -24,7 +24,7 @@ def init(d):
|
||||
break
|
||||
else:
|
||||
logger.error("Invalid signature generator '%s', using default 'noop'\n"
|
||||
"Available generators: %s", desired,
|
||||
"Available generators: %s",
|
||||
', '.join(obj.name for obj in siggens))
|
||||
return SignatureGenerator(d)
|
||||
|
||||
@@ -91,7 +91,8 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
basehash = {}
|
||||
|
||||
for task in tasklist:
|
||||
data = lookupcache[task]
|
||||
data = d.getVar(task, False)
|
||||
lookupcache[task] = data
|
||||
|
||||
if data is None:
|
||||
bb.error("Task %s from %s seems to be empty?!" % (task, fn))
|
||||
@@ -114,8 +115,16 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
alldeps = sorted(seen)
|
||||
for dep in alldeps:
|
||||
data = data + dep
|
||||
var = lookupcache[dep]
|
||||
if var is not None:
|
||||
if dep in lookupcache:
|
||||
var = lookupcache[dep]
|
||||
elif dep[-1] == ']':
|
||||
vf = dep[:-1].split('[')
|
||||
var = d.getVarFlag(vf[0], vf[1], False)
|
||||
lookupcache[dep] = var
|
||||
else:
|
||||
var = d.getVar(dep, False)
|
||||
lookupcache[dep] = var
|
||||
if var:
|
||||
data = data + str(var)
|
||||
self.basehash[fn + "." + task] = hashlib.md5(data).hexdigest()
|
||||
taskdeps[task] = alldeps
|
||||
@@ -192,10 +201,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
#d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
|
||||
return h
|
||||
|
||||
def set_taskdata(self, hashes, deps, checksums):
|
||||
def set_taskdata(self, hashes, deps):
|
||||
self.runtaskdeps = deps
|
||||
self.taskhash = hashes
|
||||
self.file_checksum_values = checksums
|
||||
|
||||
def dump_sigtask(self, fn, task, stampbase, runtime):
|
||||
k = fn + "." + task
|
||||
@@ -241,7 +249,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
os.fsync(fd)
|
||||
os.chmod(tmpfile, 0664)
|
||||
os.rename(tmpfile, sigfile)
|
||||
except (OSError, IOError) as err:
|
||||
except (OSError, IOError), err:
|
||||
try:
|
||||
os.unlink(tmpfile)
|
||||
except OSError:
|
||||
|
||||
@@ -390,17 +390,6 @@ class TaskData:
|
||||
reasons.append("%s PROVIDES %s but was skipped: %s" % (skipitem.pn, item, skipitem.skipreason))
|
||||
return reasons
|
||||
|
||||
def get_close_matches(self, item, provider_list):
|
||||
import difflib
|
||||
if self.skiplist:
|
||||
skipped = []
|
||||
for fn in self.skiplist:
|
||||
skipped.append(self.skiplist[fn].pn)
|
||||
full_list = provider_list + skipped
|
||||
else:
|
||||
full_list = provider_list
|
||||
return difflib.get_close_matches(item, full_list, cutoff=0.7)
|
||||
|
||||
def add_provider(self, cfgData, dataCache, item):
|
||||
try:
|
||||
self.add_provider_internal(cfgData, dataCache, item)
|
||||
@@ -422,7 +411,7 @@ class TaskData:
|
||||
return
|
||||
|
||||
if not item in dataCache.providers:
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item), close_matches=self.get_close_matches(item, dataCache.providers.keys())), cfgData)
|
||||
bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item)), cfgData)
|
||||
raise bb.providers.NoProvider(item)
|
||||
|
||||
if self.have_build_target(item):
|
||||
|
||||
@@ -407,7 +407,8 @@ class URLHandle(unittest.TestCase):
|
||||
datatable = {
|
||||
"http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}),
|
||||
"cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}),
|
||||
"cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
|
||||
"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'})
|
||||
}
|
||||
|
||||
def test_decodeurl(self):
|
||||
|
||||
@@ -25,8 +25,7 @@ import bb.cache
|
||||
import bb.cooker
|
||||
import bb.providers
|
||||
import bb.utils
|
||||
from bb.cooker import state, BBCooker
|
||||
from bb.cookerdata import CookerConfiguration, ConfigParameters
|
||||
from bb.cooker import state
|
||||
import bb.fetch2
|
||||
|
||||
class Tinfoil:
|
||||
@@ -44,12 +43,13 @@ class Tinfoil:
|
||||
console.setFormatter(format)
|
||||
self.logger.addHandler(console)
|
||||
|
||||
self.config = CookerConfiguration()
|
||||
configparams = TinfoilConfigParameters(parse_only=True)
|
||||
self.config.setConfigParameters(configparams)
|
||||
self.config.setServerRegIdleCallback(self.register_idle_function)
|
||||
self.cooker = BBCooker(self.config)
|
||||
self.config_data = self.cooker.data
|
||||
initialenv = os.environ.copy()
|
||||
bb.utils.clean_environment()
|
||||
self.config = TinfoilConfig(parse_only=True)
|
||||
self.cooker = bb.cooker.BBCooker(self.config,
|
||||
self.register_idle_function,
|
||||
initialenv)
|
||||
self.config_data = self.cooker.configuration.data
|
||||
bb.providers.logger.setLevel(logging.ERROR)
|
||||
self.cooker_data = None
|
||||
|
||||
@@ -71,26 +71,30 @@ class Tinfoil:
|
||||
self.logger.setLevel(logging.INFO)
|
||||
sys.stderr.write("done.\n")
|
||||
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
self.cooker_data = self.cooker.status
|
||||
|
||||
def prepare(self, config_only = False):
|
||||
if not self.cooker_data:
|
||||
if config_only:
|
||||
self.cooker.parseConfiguration()
|
||||
self.cooker_data = self.cooker.recipecache
|
||||
self.cooker_data = self.cooker.status
|
||||
else:
|
||||
self.parseRecipes()
|
||||
|
||||
class TinfoilConfigParameters(ConfigParameters):
|
||||
|
||||
class TinfoilConfig(object):
|
||||
def __init__(self, **options):
|
||||
self.initial_options = options
|
||||
super(TinfoilConfigParameters, self).__init__()
|
||||
self.pkgs_to_build = []
|
||||
self.debug_domains = []
|
||||
self.extra_assume_provided = []
|
||||
self.prefile = []
|
||||
self.postfile = []
|
||||
self.debug = 0
|
||||
self.__dict__.update(options)
|
||||
|
||||
def parseCommandLine(self):
|
||||
class DummyOptions:
|
||||
def __init__(self, initial_options):
|
||||
for key, val in initial_options.items():
|
||||
setattr(self, key, val)
|
||||
def __getattr__(self, attribute):
|
||||
try:
|
||||
return super(TinfoilConfig, self).__getattribute__(attribute)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return DummyOptions(self.initial_options), None
|
||||
|
||||
@@ -30,7 +30,7 @@ import shlex
|
||||
import re
|
||||
import logging
|
||||
import sys
|
||||
import signal
|
||||
from bb.ui.crumbs.template import TemplateMgr
|
||||
from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
|
||||
from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
|
||||
from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
|
||||
@@ -108,8 +108,6 @@ class Configuration:
|
||||
self.extra_setting = {}
|
||||
self.toolchain_build = False
|
||||
self.image_fstypes = ""
|
||||
self.image_size = None
|
||||
self.image_packages = []
|
||||
# bblayers.conf
|
||||
self.layers = []
|
||||
# image/recipes/packages
|
||||
@@ -192,7 +190,36 @@ class Configuration:
|
||||
self.split_proxy("socks", params["socks_proxy"])
|
||||
self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"])
|
||||
|
||||
def save(self, handler, defaults=False):
|
||||
def load(self, template):
|
||||
try:
|
||||
self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
|
||||
except:
|
||||
pass
|
||||
# image_overhead_factor is read-only.
|
||||
self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE")
|
||||
self.curr_sdk_machine = template.getVar("SDKMACHINE")
|
||||
self.extra_setting = eval(template.getVar("EXTRA_SETTING"))
|
||||
self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD"))
|
||||
self.image_fstypes = template.getVar("IMAGE_FSTYPES")
|
||||
# image/recipes/packages
|
||||
self.selected_image = template.getVar("__SELECTED_IMAGE__")
|
||||
self.selected_recipes = template.getVar("DEPENDS").split()
|
||||
self.selected_packages = template.getVar("IMAGE_INSTALL").split()
|
||||
# proxy
|
||||
self.enable_proxy = eval(template.getVar("enable_proxy"))
|
||||
self.same_proxy = eval(template.getVar("use_same_proxy"))
|
||||
self.split_proxy("http", template.getVar("http_proxy"))
|
||||
self.split_proxy("https", template.getVar("https_proxy"))
|
||||
self.split_proxy("ftp", template.getVar("ftp_proxy"))
|
||||
self.split_proxy("socks", template.getVar("all_proxy"))
|
||||
self.split_proxy("cvs", template.getVar("CVS_PROXY_HOST") + ":" + template.getVar("CVS_PROXY_PORT"))
|
||||
|
||||
def save(self, handler, template, defaults=False):
|
||||
template.setVar("VERSION", "%s" % hobVer)
|
||||
# bblayers.conf
|
||||
handler.set_var_in_file("BBLAYERS", self.layers, "bblayers.conf")
|
||||
# local.conf
|
||||
@@ -211,29 +238,29 @@ class Configuration:
|
||||
handler.set_var_in_file("PARALLEL_MAKE", "-j %s" % self.pmake, "local.conf")
|
||||
handler.set_var_in_file("BB_NUMBER_THREADS", self.bbthread, "local.conf")
|
||||
handler.set_var_in_file("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]), "local.conf")
|
||||
handler.set_var_in_file("IMAGE_ROOTFS_SIZE", self.image_rootfs_size, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_EXTRA_SPACE", self.image_extra_size, "local.conf")
|
||||
handler.set_var_in_file("INCOMPATIBLE_LICENSE", self.incompat_license, "local.conf")
|
||||
handler.set_var_in_file("SDKMACHINE", self.curr_sdk_machine, "local.conf")
|
||||
template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size)
|
||||
template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size)
|
||||
template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license)
|
||||
template.setVar("SDKMACHINE", self.curr_sdk_machine)
|
||||
handler.set_var_in_file("CONF_VERSION", self.conf_version, "local.conf")
|
||||
handler.set_var_in_file("LCONF_VERSION", self.lconf_version, "bblayers.conf")
|
||||
handler.set_var_in_file("EXTRA_SETTING", self.extra_setting, "local.conf")
|
||||
handler.set_var_in_file("TOOLCHAIN_BUILD", self.toolchain_build, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_FSTYPES", self.image_fstypes, "local.conf")
|
||||
template.setVar("EXTRA_SETTING", self.extra_setting)
|
||||
template.setVar("TOOLCHAIN_BUILD", self.toolchain_build)
|
||||
template.setVar("IMAGE_FSTYPES", self.image_fstypes)
|
||||
if not defaults:
|
||||
# image/recipes/packages
|
||||
handler.set_var_in_file("__SELECTED_IMAGE__", self.selected_image, "local.conf")
|
||||
handler.set_var_in_file("DEPENDS", self.selected_recipes, "local.conf")
|
||||
handler.set_var_in_file("IMAGE_INSTALL", self.user_selected_packages, "local.conf")
|
||||
template.setVar("__SELECTED_IMAGE__", self.selected_image)
|
||||
template.setVar("DEPENDS", self.selected_recipes)
|
||||
template.setVar("IMAGE_INSTALL", self.user_selected_packages)
|
||||
# proxy
|
||||
handler.set_var_in_file("enable_proxy", self.enable_proxy, "local.conf")
|
||||
handler.set_var_in_file("use_same_proxy", self.same_proxy, "local.conf")
|
||||
handler.set_var_in_file("http_proxy", self.combine_proxy("http"), "local.conf")
|
||||
handler.set_var_in_file("https_proxy", self.combine_proxy("https"), "local.conf")
|
||||
handler.set_var_in_file("ftp_proxy", self.combine_proxy("ftp"), "local.conf")
|
||||
handler.set_var_in_file("all_proxy", self.combine_proxy("socks"), "local.conf")
|
||||
handler.set_var_in_file("CVS_PROXY_HOST", self.combine_host_only("cvs"), "local.conf")
|
||||
handler.set_var_in_file("CVS_PROXY_PORT", self.combine_port_only("cvs"), "local.conf")
|
||||
template.setVar("enable_proxy", self.enable_proxy)
|
||||
template.setVar("use_same_proxy", self.same_proxy)
|
||||
template.setVar("http_proxy", self.combine_proxy("http"))
|
||||
template.setVar("https_proxy", self.combine_proxy("https"))
|
||||
template.setVar("ftp_proxy", self.combine_proxy("ftp"))
|
||||
template.setVar("all_proxy", self.combine_proxy("socks"))
|
||||
template.setVar("CVS_PROXY_HOST", self.combine_host_only("cvs"))
|
||||
template.setVar("CVS_PROXY_PORT", self.combine_port_only("cvs"))
|
||||
|
||||
def __str__(self):
|
||||
s = "VERSION: '%s', BBLAYERS: '%s', MACHINE: '%s', DISTRO: '%s', DL_DIR: '%s'," % \
|
||||
@@ -378,6 +405,8 @@ class Builder(gtk.Window):
|
||||
# handler
|
||||
self.handler = hobHandler
|
||||
|
||||
self.template = None
|
||||
|
||||
# logger
|
||||
self.logger = logging.getLogger("BitBake")
|
||||
self.consolelog = None
|
||||
@@ -440,11 +469,8 @@ class Builder(gtk.Window):
|
||||
self.handler.connect("recipe-populated", self.handler_recipe_populated_cb)
|
||||
self.handler.connect("package-populated", self.handler_package_populated_cb)
|
||||
|
||||
self.handler.append_to_bbfiles("${TOPDIR}/recipes/images/*.bb")
|
||||
self.initiate_new_build_async()
|
||||
|
||||
signal.signal(signal.SIGINT, self.event_handle_SIGINT)
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_title("Hob")
|
||||
self.set_icon_name("applications-development")
|
||||
@@ -512,7 +538,7 @@ class Builder(gtk.Window):
|
||||
if not self.display_sanity_check:
|
||||
func()
|
||||
else:
|
||||
self.sanity_check_post_func = func
|
||||
sanity_check_post_func = func
|
||||
|
||||
def generate_configuration(self):
|
||||
if not self.sanity_checked:
|
||||
@@ -520,11 +546,11 @@ class Builder(gtk.Window):
|
||||
self.handler.generate_configuration()
|
||||
|
||||
def initiate_new_build_async(self):
|
||||
self.configuration.selected_image = None
|
||||
self.switch_page(self.MACHINE_SELECTION)
|
||||
self.handler.init_cooker()
|
||||
self.handler.set_extra_inherit("image_types")
|
||||
self.generate_configuration()
|
||||
self.load_template(TemplateMgr.convert_to_template_pathfilename("default", ".hob/"))
|
||||
|
||||
def update_config_async(self):
|
||||
self.switch_page(self.MACHINE_SELECTION)
|
||||
@@ -595,16 +621,6 @@ class Builder(gtk.Window):
|
||||
toolchain_packages,
|
||||
self.configuration.default_task)
|
||||
|
||||
def generate_new_image(self, image, description):
|
||||
base_image = self.configuration.initial_selected_image
|
||||
if base_image == self.recipe_model.__custom_image__:
|
||||
base_image = None
|
||||
packages = self.package_model.get_selected_packages()
|
||||
self.handler.generate_new_image(image, base_image, packages, description)
|
||||
|
||||
def ensure_dir(self, directory):
|
||||
self.handler.ensure_dir(directory)
|
||||
|
||||
def get_parameters_sync(self):
|
||||
return self.handler.get_parameters()
|
||||
|
||||
@@ -617,6 +633,61 @@ class Builder(gtk.Window):
|
||||
def cancel_parse_sync(self):
|
||||
self.handler.cancel_parse()
|
||||
|
||||
def load_template(self, path):
|
||||
if not os.path.isfile(path):
|
||||
return False
|
||||
|
||||
self.template = TemplateMgr()
|
||||
# check compatibility
|
||||
tempVer = self.template.getVersion(path)
|
||||
if not tempVer or int(tempVer) < hobVer:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
return False
|
||||
|
||||
try:
|
||||
self.template.load(path)
|
||||
self.configuration.load(self.template)
|
||||
except Exception as e:
|
||||
self.show_error_dialog("Hob Exception - %s" % (str(e)))
|
||||
self.reset()
|
||||
finally:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
|
||||
for layer in self.configuration.layers:
|
||||
if not os.path.exists(layer+'/conf/layer.conf'):
|
||||
return False
|
||||
|
||||
self.set_user_config_extra()
|
||||
return True
|
||||
|
||||
def save_template(self, path, defaults=False):
|
||||
if path.rfind("/") == -1:
|
||||
filename = "default"
|
||||
path = "."
|
||||
else:
|
||||
filename = path[path.rfind("/") + 1:len(path)]
|
||||
path = path[0:path.rfind("/")]
|
||||
|
||||
self.template = TemplateMgr()
|
||||
try:
|
||||
self.template.open(filename, path)
|
||||
self.configuration.save(self.handler, self.template, defaults)
|
||||
|
||||
self.template.save()
|
||||
except Exception as e:
|
||||
self.show_error_dialog("Hob Exception - %s" % (str(e)))
|
||||
self.reset()
|
||||
finally:
|
||||
self.template.destroy()
|
||||
self.template = None
|
||||
|
||||
def save_defaults(self):
|
||||
if not os.path.exists(".hob/"):
|
||||
os.mkdir(".hob/")
|
||||
self.save_template(".hob/default", True)
|
||||
|
||||
def switch_page(self, next_step):
|
||||
# Main Workflow (Business Logic)
|
||||
self.nb.set_current_page(self.__step2page__[next_step])
|
||||
@@ -699,11 +770,12 @@ class Builder(gtk.Window):
|
||||
self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
|
||||
self.handler.set_image_fstypes(self.configuration.image_fstypes)
|
||||
self.handler.set_extra_config(self.configuration.extra_setting)
|
||||
self.handler.set_extra_inherit("packageinfo image_types")
|
||||
self.handler.set_extra_inherit("packageinfo")
|
||||
self.handler.set_extra_inherit("image_types")
|
||||
self.set_user_config_proxies()
|
||||
|
||||
def set_user_config(self):
|
||||
self.handler.reset_cooker()
|
||||
self.handler.init_cooker()
|
||||
# set bb layers
|
||||
self.handler.set_bblayers(self.configuration.layers)
|
||||
# set local configuration
|
||||
@@ -1090,12 +1162,6 @@ class Builder(gtk.Window):
|
||||
else:
|
||||
gtk.main_quit()
|
||||
|
||||
def event_handle_SIGINT(self, signal, frame):
|
||||
for w in gtk.window_list_toplevels():
|
||||
if w.get_modal():
|
||||
w.response(gtk.RESPONSE_DELETE_EVENT)
|
||||
sys.exit(0)
|
||||
|
||||
def build_packages(self):
|
||||
_, all_recipes = self.recipe_model.get_selected_recipes()
|
||||
if not all_recipes:
|
||||
@@ -1187,6 +1253,7 @@ class Builder(gtk.Window):
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration.layers = dialog.layers
|
||||
self.save_defaults() # remember layers
|
||||
# DO refresh layers
|
||||
if dialog.layers_changed:
|
||||
self.update_config_async()
|
||||
@@ -1250,7 +1317,7 @@ class Builder(gtk.Window):
|
||||
settings_changed = False
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration = dialog.configuration
|
||||
self.configuration.save(self.handler, True) # remember settings
|
||||
self.save_defaults() # remember settings
|
||||
settings_changed = dialog.settings_changed
|
||||
dialog.destroy()
|
||||
return response == gtk.RESPONSE_YES, settings_changed
|
||||
@@ -1278,7 +1345,7 @@ class Builder(gtk.Window):
|
||||
settings_changed = False
|
||||
if response == gtk.RESPONSE_YES:
|
||||
self.configuration = dialog.configuration
|
||||
self.configuration.save(self.handler, True) # remember settings
|
||||
self.save_defaults() # remember settings
|
||||
settings_changed = dialog.settings_changed
|
||||
if dialog.proxy_settings_changed:
|
||||
self.set_user_config_proxies()
|
||||
@@ -1463,6 +1530,3 @@ class Builder(gtk.Window):
|
||||
self.consolelog.setFormatter(format)
|
||||
|
||||
self.logger.addHandler(self.consolelog)
|
||||
|
||||
def get_topdir(self):
|
||||
return self.handler.get_topdir()
|
||||
|
||||
@@ -131,7 +131,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.rootfs_size_spinner = None
|
||||
self.extra_size_spinner = None
|
||||
self.gplv3_checkbox = None
|
||||
self.sdk_checkbox = None
|
||||
self.toolchain_checkbox = None
|
||||
self.image_types_checkbuttons = {}
|
||||
|
||||
self.md5 = self.config_md5()
|
||||
@@ -264,7 +264,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
sub_vbox = gtk.VBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("Image basic size (in MB)")
|
||||
tooltip = "Defines the size for the generated image. The OpenEmbedded build system determines the final size for the generated image using an algorithm that takes into account the initial disk space used for the generated image, the Image basic size value, and the Additional free space value.\n\nFor more information, check the <a href=\"http://www.yoctoproject.org/docs/current/poky-ref-manual/poky-ref-manual.html#var-IMAGE_ROOTFS_SIZE\">Yocto Project Reference Manual</a>."
|
||||
tooltip = "Sets the basic size of your target image.\nThis is the basic size of your target image unless your selected package size exceeds this value or you select \'Image Extra Size\'."
|
||||
rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536,"<b>Image basic size</b>" + "*" + tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(rootfs_size_widget, expand=False, fill=False)
|
||||
@@ -272,7 +272,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
sub_vbox = gtk.VBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = self.gen_label_widget("Additional free space (in MB)")
|
||||
tooltip = "Sets extra free disk space to be added to the generated image. Use this variable when you want to ensure that a specific amount of free disk space is available on a device after an image is installed and running."
|
||||
tooltip = "Sets the extra free space of your target image.\nBy default, the system reserves 30% of your image size as free space. If your image contains zypper, it brings in 50MB more space. The maximum free space is 64GB."
|
||||
extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536,"<b>Additional free space</b>" + "*" + tooltip)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(extra_size_widget, expand=False, fill=False)
|
||||
@@ -286,17 +286,16 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.gplv3_checkbox.set_active(False)
|
||||
advanced_vbox.pack_start(self.gplv3_checkbox, expand=False, fill=False)
|
||||
|
||||
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">SDK</span>'), expand=False, fill=False)
|
||||
advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Toolchain</span>'), expand=False, fill=False)
|
||||
sub_hbox = gtk.HBox(False, 6)
|
||||
advanced_vbox.pack_start(sub_hbox, expand=False, fill=False)
|
||||
self.sdk_checkbox = gtk.CheckButton("Populate SDK")
|
||||
tooltip = "Check this box to generate an SDK tarball that consists of the cross-toolchain and a sysroot that contains development packages for your image."
|
||||
self.sdk_checkbox.set_tooltip_text(tooltip)
|
||||
self.sdk_checkbox.set_active(self.configuration.toolchain_build)
|
||||
sub_hbox.pack_start(self.sdk_checkbox, expand=False, fill=False)
|
||||
self.toolchain_checkbox = gtk.CheckButton("Build toolchain")
|
||||
self.toolchain_checkbox.set_tooltip_text("Check this box to build the related toolchain with your image")
|
||||
self.toolchain_checkbox.set_active(self.configuration.toolchain_build)
|
||||
sub_hbox.pack_start(self.toolchain_checkbox, expand=False, fill=False)
|
||||
|
||||
tooltip = "Select the host platform for which you want to run the toolchain contained in the SDK tarball."
|
||||
sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines,"<b>Populate SDK</b>" + "*" + tooltip)
|
||||
tooltip = "Selects the host platform for which you want to run the toolchain"
|
||||
sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines,"<b>Build toolchain</b>" + "*" + tooltip)
|
||||
sub_hbox.pack_start(sdk_machine_widget, expand=False, fill=False)
|
||||
|
||||
return advanced_vbox
|
||||
@@ -331,7 +330,7 @@ class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
self.configuration.incompat_license = " ".join(self.configuration.incompat_license or [])
|
||||
self.configuration.incompat_license = self.configuration.incompat_license.strip()
|
||||
|
||||
self.configuration.toolchain_build = self.sdk_checkbox.get_active()
|
||||
self.configuration.toolchain_build = self.toolchain_checkbox.get_active()
|
||||
self.configuration.curr_sdk_machine = self.sdk_machine_combo.get_active_text()
|
||||
md5 = self.config_md5()
|
||||
self.settings_changed = (self.md5 != md5)
|
||||
|
||||
@@ -132,12 +132,13 @@ class LayerSelectionDialog (CrumbsDialog):
|
||||
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,
|
||||
self.TARGETS,
|
||||
gtk.gdk.ACTION_DEFAULT|
|
||||
dnd_targets,
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
layer_tv.enable_model_drag_dest(dnd_targets,
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
layer_tv.enable_model_drag_dest(self.TARGETS,
|
||||
gtk.gdk.ACTION_DEFAULT)
|
||||
layer_tv.connect("drag_data_get", self.drag_data_get_cb)
|
||||
layer_tv.connect("drag_data_received", self.drag_data_received_cb)
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ class PropertyDialog(CrumbsDialog):
|
||||
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:
|
||||
@@ -275,23 +274,24 @@ class PropertyDialog(CrumbsDialog):
|
||||
binb_items_count = len(binb.split(','))
|
||||
binb_items = binb.split(',')
|
||||
|
||||
vbox = gtk.VBox(False,spacing = 0)
|
||||
|
||||
vbox = gtk.VBox(True,spacing = 0)
|
||||
|
||||
######################################## SUMMARY LABEL #########################################
|
||||
|
||||
if summary != '':
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_width_chars(37)
|
||||
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("<b>" + summary + "</b>")
|
||||
self.label_short.set_property("xalign", 0)
|
||||
|
||||
self.vbox.add(self.label_short)
|
||||
self.vbox.pack_start(self.label_short, expand=False, fill=False, padding=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)
|
||||
@@ -302,6 +302,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
####################################### VERSION 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\">Version: </span>" + version)
|
||||
@@ -312,6 +313,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
##################################### REVISION ROW + COL #####################################
|
||||
|
||||
self.label_short = gtk.Label()
|
||||
self.label_short.set_size_request(300,-1)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_markup("<span weight=\"bold\">Revision: </span>" + revision)
|
||||
@@ -322,6 +324,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
################################## GROUP 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\">Group: </span>" + group)
|
||||
@@ -343,6 +346,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
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("<b>Homepage: </b>")
|
||||
@@ -364,6 +368,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
self.label_info.set_property("xalign", 0)
|
||||
|
||||
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("<b>Bugtracker: </b>")
|
||||
@@ -375,6 +380,7 @@ class PropertyDialog(CrumbsDialog):
|
||||
################################# LICENSE ROW + COL ############################################
|
||||
|
||||
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(license)
|
||||
@@ -397,25 +403,16 @@ class PropertyDialog(CrumbsDialog):
|
||||
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_size_request(300,-1)
|
||||
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)
|
||||
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_short)
|
||||
self.vbox.add(self.label_info)
|
||||
|
||||
################################ DESCRIPTION TAG ROW #################################################
|
||||
|
||||
@@ -430,22 +427,11 @@ class PropertyDialog(CrumbsDialog):
|
||||
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.label_short.set_size_request(300,-1)
|
||||
self.label_short.set_selectable(True)
|
||||
self.label_short.set_text(description)
|
||||
self.label_short.set_line_wrap(True)
|
||||
self.label_short.set_property("xalign", 0)
|
||||
self.vbox.add(self.label_short)
|
||||
|
||||
self.vbox.show_all()
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
|
||||
class RetrieveImageDialog (gtk.FileChooserDialog):
|
||||
"""
|
||||
This class is used to create a dialog that permits to retrieve
|
||||
a custom image saved previously from Hob.
|
||||
"""
|
||||
def __init__(self, directory,title, parent, flags, buttons=None):
|
||||
super(RetrieveImageDialog, self).__init__(title, None, gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||
self.directory = directory
|
||||
|
||||
# create visual elements on the dialog
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_show_hidden(True)
|
||||
self.set_default_response(gtk.RESPONSE_OK)
|
||||
self.set_current_folder(self.directory)
|
||||
|
||||
vbox = self.get_children()[0].get_children()[0].get_children()[0]
|
||||
for child in vbox.get_children()[0].get_children()[0].get_children()[0].get_children():
|
||||
vbox.get_children()[0].get_children()[0].get_children()[0].remove(child)
|
||||
|
||||
label1 = gtk.Label()
|
||||
label1.set_text("File system" + self.directory)
|
||||
label1.show()
|
||||
vbox.get_children()[0].get_children()[0].get_children()[0].pack_start(label1, expand=False, fill=False, padding=0)
|
||||
vbox.get_children()[0].get_children()[1].get_children()[0].hide()
|
||||
|
||||
self.get_children()[0].get_children()[1].get_children()[0].set_label("Select")
|
||||
@@ -1,160 +0,0 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gtk
|
||||
import glib
|
||||
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||
from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
|
||||
from bb.ui.crumbs.hobwidget import HobButton
|
||||
|
||||
class SaveImageDialog (CrumbsDialog):
|
||||
"""
|
||||
This class is used to create a dialog that permits to save
|
||||
a custom image in a predefined directory.
|
||||
"""
|
||||
def __init__(self, directory, name, description, title, parent, flags, buttons=None):
|
||||
super(SaveImageDialog, self).__init__(title, parent, flags, buttons)
|
||||
self.directory = directory
|
||||
self.builder = parent
|
||||
self.name_field = name
|
||||
self.description_field = description
|
||||
|
||||
# create visual elements on the dialog
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
self.set_default_response(gtk.RESPONSE_OK)
|
||||
self.vbox.set_border_width(6)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("<b>Name</b>")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
content = "Image recipe names should be all lowercase and include only alphanumeric\n"
|
||||
content += "characters. The only special character you can use is the ASCII hyphen (-)."
|
||||
sub_label.set_markup(content)
|
||||
self.name_entry = gtk.Entry()
|
||||
self.name_entry.set_text(self.name_field)
|
||||
self.name_entry.set_size_request(350,30)
|
||||
self.name_entry.connect("changed", self.name_entry_changed)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(self.name_entry, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("<b>Description</b> (optional)")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
sub_label.set_markup("The description should be less than 150 characters long.")
|
||||
self.description_entry = gtk.TextView()
|
||||
description_buffer = self.description_entry.get_buffer()
|
||||
description_buffer.set_text(self.description_field)
|
||||
description_buffer.connect("insert-text", self.limit_description_length)
|
||||
self.description_entry.set_wrap_mode(gtk.WRAP_WORD)
|
||||
self.description_entry.set_size_request(350,50)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(self.description_entry, expand=False, fill=False)
|
||||
|
||||
sub_vbox = gtk.VBox(False, 12)
|
||||
self.vbox.pack_start(sub_vbox, expand=False, fill=False)
|
||||
label = gtk.Label()
|
||||
label.set_alignment(0, 0)
|
||||
label.set_markup("Your image recipe will be saved to:")
|
||||
sub_label = gtk.Label()
|
||||
sub_label.set_alignment(0, 0)
|
||||
sub_label.set_markup(self.directory)
|
||||
sub_vbox.pack_start(label, expand=False, fill=False)
|
||||
sub_vbox.pack_start(sub_label, expand=False, fill=False)
|
||||
|
||||
table = gtk.Table(1, 4, True)
|
||||
|
||||
cancel_button = gtk.Button()
|
||||
cancel_button.set_label("Cancel")
|
||||
cancel_button.connect("clicked", self.cancel_button_cb)
|
||||
cancel_button.set_size_request(110, 30)
|
||||
|
||||
self.save_button = gtk.Button()
|
||||
self.save_button.set_label("Save")
|
||||
self.save_button.connect("clicked", self.save_button_cb)
|
||||
self.save_button.set_size_request(110, 30)
|
||||
if self.name_entry.get_text() == '':
|
||||
self.save_button.set_sensitive(False)
|
||||
|
||||
table.attach(cancel_button, 2, 3, 0, 1)
|
||||
table.attach(self.save_button, 3, 4, 0, 1)
|
||||
self.vbox.pack_end(table, expand=False, fill=False)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def limit_description_length(self, textbuffer, iter, text, length):
|
||||
buffer_bounds = textbuffer.get_bounds()
|
||||
entire_text = textbuffer.get_text(*buffer_bounds)
|
||||
entire_text += text
|
||||
if len(entire_text)>150 or text=="\n":
|
||||
textbuffer.emit_stop_by_name("insert-text")
|
||||
|
||||
def name_entry_changed(self, entry):
|
||||
text = entry.get_text()
|
||||
if text == '':
|
||||
self.save_button.set_sensitive(False)
|
||||
else:
|
||||
self.save_button.set_sensitive(True)
|
||||
|
||||
def cancel_button_cb(self, button):
|
||||
self.destroy()
|
||||
|
||||
def save_button_cb(self, button):
|
||||
text = self.name_entry.get_text()
|
||||
new_text = text.replace("-","")
|
||||
description_buffer = self.description_entry.get_buffer()
|
||||
description = description_buffer.get_text(description_buffer.get_start_iter(),description_buffer.get_end_iter())
|
||||
if new_text.islower() and new_text.isalnum():
|
||||
self.builder.image_details_page.image_saved = True
|
||||
self.builder.customized = False
|
||||
self.builder.generate_new_image(self.directory+text, description)
|
||||
self.builder.recipe_model.set_in_list(text, description)
|
||||
self.builder.recipe_model.set_selected_image(text)
|
||||
self.builder.image_details_page.show_page(self.builder.IMAGE_GENERATED)
|
||||
self.builder.image_details_page.name_field_template = text
|
||||
self.builder.image_details_page.description_field_template = description
|
||||
self.destroy()
|
||||
else:
|
||||
self.show_invalid_input_error_dialog()
|
||||
|
||||
def show_invalid_input_error_dialog(self):
|
||||
lbl = "<b>Invalid characters in image recipe name</b>\n"
|
||||
msg = "Image recipe names should be all lowercase and\n"
|
||||
msg += "include only alphanumeric characters. The only\n"
|
||||
msg += "special character you can use is the ASCII hyphen (-)."
|
||||
lbl = lbl + "\n%s\n" % glib.markup_escape_text(msg)
|
||||
dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
|
||||
button = dialog.add_button("Close", gtk.RESPONSE_OK)
|
||||
HobButton.style_button(button)
|
||||
|
||||
res = dialog.run()
|
||||
self.name_entry.grab_focus()
|
||||
dialog.destroy()
|
||||
@@ -310,6 +310,7 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
|
||||
sub_vbox = gtk.VBox(False)
|
||||
advanced_vbox.pack_start(sub_vbox, gtk.TRUE, gtk.TRUE, 0)
|
||||
searched_string = "file://"
|
||||
|
||||
if self.sstatemirrors_changed == 0:
|
||||
self.sstatemirrors_changed = 1
|
||||
@@ -318,12 +319,18 @@ class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
|
||||
sm_list = ["Standard", "", "file://(.*)"]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
else:
|
||||
sstatemirrors = [x for x in sstatemirrors.split('\\n')]
|
||||
for sstatemirror in sstatemirrors:
|
||||
while sstatemirrors.find(searched_string) != -1:
|
||||
if sstatemirrors.find(searched_string,1) != -1:
|
||||
sstatemirror = sstatemirrors[:sstatemirrors.find(searched_string,1)]
|
||||
sstatemirrors = sstatemirrors[sstatemirrors.find(searched_string,1):]
|
||||
else:
|
||||
sstatemirror = sstatemirrors
|
||||
sstatemirrors = sstatemirrors[1:]
|
||||
|
||||
sstatemirror_fields = [x for x in sstatemirror.split(' ') if x.strip()]
|
||||
if len(sstatemirror_fields) == 2:
|
||||
if sstatemirror_fields[0] == "file://(.*)" or sstatemirror_fields[0] == "file://.*":
|
||||
sm_list = ["Standard", sstatemirror_fields[1], sstatemirror_fields[0]]
|
||||
if len(sstatemirror_fields):
|
||||
if sstatemirror_fields[0] == "file://(.*)":
|
||||
sm_list = ["Standard", sstatemirror_fields[1], "file://(.*)"]
|
||||
else:
|
||||
sm_list = ["Custom", sstatemirror_fields[1], sstatemirror_fields[0]]
|
||||
self.sstatemirrors_list.append(sm_list)
|
||||
|
||||
@@ -147,7 +147,7 @@ class HobHandler(gobject.GObject):
|
||||
self.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"])
|
||||
elif next_command == self.SUB_PARSE_CONFIG:
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.runCommand(["parseConfigurationFiles", "conf/.hob.conf", ""])
|
||||
self.runCommand(["parseConfigurationFiles", "", ""])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
elif next_command == self.SUB_GNERATE_TGTS:
|
||||
self.runCommand(["generateTargetsTree", "classes/image.bbclass", []])
|
||||
@@ -166,15 +166,17 @@ class HobHandler(gobject.GObject):
|
||||
self.clear_busy()
|
||||
self.building = True
|
||||
targets = [self.image]
|
||||
if self.package_queue:
|
||||
self.runCommand(["setVariable", "LINGUAS_INSTALL", ""])
|
||||
self.runCommand(["setVariable", "PACKAGE_INSTALL", " ".join(self.package_queue)])
|
||||
if self.toolchain_packages:
|
||||
self.set_var_in_file("TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages), "local.conf")
|
||||
self.runCommand(["setVariable", "TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages)])
|
||||
targets.append(self.toolchain)
|
||||
if targets[0] == "hob-image":
|
||||
self.set_var_in_file("LINGUAS_INSTALL", "", "local.conf")
|
||||
hobImage = self.runCommand(["matchFile", "hob-image.bb"])
|
||||
if self.base_image != "Start with an empty image recipe":
|
||||
if self.base_image != "Create your own image":
|
||||
baseImage = self.runCommand(["matchFile", self.base_image + ".bb"])
|
||||
version = self.runCommand(["generateNewImage", hobImage, baseImage, self.package_queue, True, ""])
|
||||
version = self.runCommand(["generateNewImage", hobImage, baseImage, self.package_queue])
|
||||
targets[0] += version
|
||||
self.recipe_model.set_custom_image_version(version)
|
||||
|
||||
@@ -202,8 +204,8 @@ class HobHandler(gobject.GObject):
|
||||
elif isinstance(event, bb.event.SanityCheckPassed):
|
||||
reparse = self.runCommand(["getVariable", "BB_INVALIDCONF"]) or None
|
||||
if reparse is True:
|
||||
self.set_var_in_file("BB_INVALIDCONF", False, "local.conf")
|
||||
self.runCommand(["parseConfigurationFiles", "conf/.hob.conf", ""])
|
||||
self.runCommand(["setVariable", "BB_INVALIDCONF", False])
|
||||
self.runCommand(["parseConfigurationFiles", "", ""])
|
||||
self.run_next_command()
|
||||
|
||||
elif isinstance(event, bb.event.SanityCheckFailed):
|
||||
@@ -298,85 +300,79 @@ class HobHandler(gobject.GObject):
|
||||
|
||||
def init_cooker(self):
|
||||
self.runCommand(["initCooker"])
|
||||
self.runCommand(["createConfigFile", ".hob.conf"])
|
||||
|
||||
def reset_cooker(self):
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.runCommand(["resetCooker"])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
|
||||
def set_extra_inherit(self, bbclass):
|
||||
inherits = self.runCommand(["getVariable", "INHERIT"]) or ""
|
||||
inherits = inherits + " " + bbclass
|
||||
self.set_var_in_file("INHERIT", inherits, ".hob.conf")
|
||||
self.runCommand(["setVariable", "INHERIT", inherits])
|
||||
|
||||
def set_bblayers(self, bblayers):
|
||||
self.set_var_in_file("BBLAYERS", " ".join(bblayers), "bblayers.conf")
|
||||
self.runCommand(["setVariable", "BBLAYERS", " ".join(bblayers)])
|
||||
|
||||
def set_machine(self, machine):
|
||||
if machine:
|
||||
self.set_var_in_file("MACHINE", machine, "local.conf")
|
||||
self.runCommand(["setVariable", "MACHINE", machine])
|
||||
|
||||
def set_sdk_machine(self, sdk_machine):
|
||||
self.set_var_in_file("SDKMACHINE", sdk_machine, "local.conf")
|
||||
self.runCommand(["setVariable", "SDKMACHINE", sdk_machine])
|
||||
|
||||
def set_image_fstypes(self, image_fstypes):
|
||||
self.set_var_in_file("IMAGE_FSTYPES", image_fstypes, "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_FSTYPES", image_fstypes])
|
||||
|
||||
def set_distro(self, distro):
|
||||
self.set_var_in_file("DISTRO", distro, "local.conf")
|
||||
self.runCommand(["setVariable", "DISTRO", distro])
|
||||
|
||||
def set_package_format(self, format):
|
||||
package_classes = ""
|
||||
for pkgfmt in format.split():
|
||||
package_classes += ("package_%s" % pkgfmt + " ")
|
||||
self.set_var_in_file("PACKAGE_CLASSES", package_classes, "local.conf")
|
||||
self.runCommand(["setVariable", "PACKAGE_CLASSES", package_classes])
|
||||
|
||||
def set_bbthreads(self, threads):
|
||||
self.set_var_in_file("BB_NUMBER_THREADS", threads, "local.conf")
|
||||
self.runCommand(["setVariable", "BB_NUMBER_THREADS", threads])
|
||||
|
||||
def set_pmake(self, threads):
|
||||
pmake = "-j %s" % threads
|
||||
self.set_var_in_file("PARALLEL_MAKE", pmake, "local.conf")
|
||||
self.runCommand(["setVariable", "PARALLEL_MAKE", pmake])
|
||||
|
||||
def set_dl_dir(self, directory):
|
||||
self.set_var_in_file("DL_DIR", directory, "local.conf")
|
||||
self.runCommand(["setVariable", "DL_DIR", directory])
|
||||
|
||||
def set_sstate_dir(self, directory):
|
||||
self.set_var_in_file("SSTATE_DIR", directory, "local.conf")
|
||||
self.runCommand(["setVariable", "SSTATE_DIR", directory])
|
||||
|
||||
def set_sstate_mirrors(self, url):
|
||||
self.set_var_in_file("SSTATE_MIRRORS", url, "local.conf")
|
||||
self.runCommand(["setVariable", "SSTATE_MIRRORS", url])
|
||||
|
||||
def set_extra_size(self, image_extra_size):
|
||||
self.set_var_in_file("IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size), "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size)])
|
||||
|
||||
def set_rootfs_size(self, image_rootfs_size):
|
||||
self.set_var_in_file("IMAGE_ROOTFS_SIZE", str(image_rootfs_size), "local.conf")
|
||||
self.runCommand(["setVariable", "IMAGE_ROOTFS_SIZE", str(image_rootfs_size)])
|
||||
|
||||
def set_incompatible_license(self, incompat_license):
|
||||
self.set_var_in_file("INCOMPATIBLE_LICENSE", incompat_license, "local.conf")
|
||||
self.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", incompat_license])
|
||||
|
||||
def set_extra_config(self, extra_setting):
|
||||
for key in extra_setting.keys():
|
||||
value = extra_setting[key]
|
||||
self.set_var_in_file(key, value, "local.conf")
|
||||
self.runCommand(["setVariable", key, value])
|
||||
|
||||
def set_http_proxy(self, http_proxy):
|
||||
self.set_var_in_file("http_proxy", http_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "http_proxy", http_proxy])
|
||||
|
||||
def set_https_proxy(self, https_proxy):
|
||||
self.set_var_in_file("https_proxy", https_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "https_proxy", https_proxy])
|
||||
|
||||
def set_ftp_proxy(self, ftp_proxy):
|
||||
self.set_var_in_file("ftp_proxy", ftp_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "ftp_proxy", ftp_proxy])
|
||||
|
||||
def set_socks_proxy(self, socks_proxy):
|
||||
self.set_var_in_file("all_proxy", socks_proxy, "local.conf")
|
||||
self.runCommand(["setVariable", "all_proxy", socks_proxy])
|
||||
|
||||
def set_cvs_proxy(self, host, port):
|
||||
self.set_var_in_file("CVS_PROXY_HOST", host, "local.conf")
|
||||
self.set_var_in_file("CVS_PROXY_PORT", port, "local.conf")
|
||||
self.runCommand(["setVariable", "CVS_PROXY_HOST", host])
|
||||
self.runCommand(["setVariable", "CVS_PROXY_PORT", port])
|
||||
|
||||
def request_package_info(self):
|
||||
self.commands_async.append(self.SUB_GENERATE_PKGINFO)
|
||||
@@ -424,13 +420,6 @@ class HobHandler(gobject.GObject):
|
||||
self.commands_async.append(self.SUB_BUILD_IMAGE)
|
||||
self.run_next_command(self.GENERATE_IMAGE)
|
||||
|
||||
def generate_new_image(self, image, base_image, package_queue, description):
|
||||
base_image = self.runCommand(["matchFile", self.base_image + ".bb"])
|
||||
self.runCommand(["generateNewImage", image, base_image, package_queue, False, description])
|
||||
|
||||
def ensure_dir(self, directory):
|
||||
self.runCommand(["ensureDir", directory])
|
||||
|
||||
def build_succeeded_async(self):
|
||||
self.building = False
|
||||
|
||||
@@ -440,12 +429,12 @@ class HobHandler(gobject.GObject):
|
||||
self.building = False
|
||||
|
||||
def cancel_parse(self):
|
||||
self.runCommand(["stateForceShutdown"])
|
||||
self.runCommand(["stateStop"])
|
||||
|
||||
def cancel_build(self, force=False):
|
||||
if force:
|
||||
# Force the cooker to stop as quickly as possible
|
||||
self.runCommand(["stateForceShutdown"])
|
||||
self.runCommand(["stateStop"])
|
||||
else:
|
||||
# Wait for tasks to complete before shutting down, this helps
|
||||
# leave the workdir in a usable state
|
||||
@@ -457,9 +446,6 @@ class HobHandler(gobject.GObject):
|
||||
def get_logfile(self):
|
||||
return self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
|
||||
|
||||
def get_topdir(self):
|
||||
return self.runCommand(["getVariable", "TOPDIR"]) or ""
|
||||
|
||||
def _remove_redundant(self, string):
|
||||
ret = []
|
||||
for i in string.split():
|
||||
@@ -468,18 +454,7 @@ class HobHandler(gobject.GObject):
|
||||
return " ".join(ret)
|
||||
|
||||
def set_var_in_file(self, var, val, default_file=None):
|
||||
self.runCommand(["enableDataTracking"])
|
||||
self.server.runCommand(["setVarFile", var, val, default_file, "set"])
|
||||
self.runCommand(["disableDataTracking"])
|
||||
|
||||
def append_var_in_file(self, var, val, default_file=None):
|
||||
self.server.runCommand(["setVarFile", var, val, default_file, "append"])
|
||||
|
||||
def append_to_bbfiles(self, val):
|
||||
bbfiles = self.runCommand(["getVariable", "BBFILES", "False"]) or ""
|
||||
bbfiles = bbfiles.split()
|
||||
if val not in bbfiles:
|
||||
self.append_var_in_file("BBFILES", val, "local.conf")
|
||||
self.server.runCommand(["setVarFile", var, val, default_file])
|
||||
|
||||
def get_parameters(self):
|
||||
# retrieve the parameters from bitbake
|
||||
|
||||
@@ -63,7 +63,6 @@ class PackageListModel(gtk.ListStore):
|
||||
gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.sort_column_id, self.sort_order = PackageListModel.COL_NAME, gtk.SORT_ASCENDING
|
||||
|
||||
"""
|
||||
Find the model path for the item_name
|
||||
@@ -110,11 +109,11 @@ class PackageListModel(gtk.ListStore):
|
||||
model.set_visible_func(self.tree_model_filter, filter)
|
||||
|
||||
sort = gtk.TreeModelSort(model)
|
||||
sort.connect ('sort-column-changed', self.sort_column_changed_cb)
|
||||
if initial:
|
||||
sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
elif excluded_items_ahead:
|
||||
|
||||
if excluded_items_ahead:
|
||||
sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
|
||||
elif included_items_ahead:
|
||||
sort.set_default_sort_func(self.include_item_sort_func, search_data)
|
||||
@@ -122,18 +121,15 @@ class PackageListModel(gtk.ListStore):
|
||||
if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
|
||||
sort.set_default_sort_func(self.sort_func, search_data)
|
||||
else:
|
||||
sort.set_sort_column_id(self.sort_column_id, self.sort_order)
|
||||
sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
|
||||
sort.set_sort_func(PackageListModel.COL_INC, self.sort_column, PackageListModel.COL_INC)
|
||||
sort.set_sort_func(PackageListModel.COL_SIZE, self.sort_column, PackageListModel.COL_SIZE)
|
||||
sort.set_sort_func(PackageListModel.COL_BINB, self.sort_binb_column)
|
||||
sort.set_sort_func(PackageListModel.COL_BINB, self.sort_column, PackageListModel.COL_BINB)
|
||||
sort.set_sort_func(PackageListModel.COL_RCP, self.sort_column, PackageListModel.COL_RCP)
|
||||
return sort
|
||||
|
||||
def sort_column_changed_cb (self, data):
|
||||
self.sort_column_id, self.sort_order = data.get_sort_column_id ()
|
||||
|
||||
def sort_column(self, model, row1, row2, col):
|
||||
value1 = model.get_value(row1, col)
|
||||
value2 = model.get_value(row2, col)
|
||||
@@ -152,32 +148,16 @@ class PackageListModel(gtk.ListStore):
|
||||
name2 = model.get_value(row2, PackageListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
|
||||
def sort_binb_column(self, model, row1, row2):
|
||||
value1 = model.get_value(row1, PackageListModel.COL_BINB)
|
||||
value2 = model.get_value(row2, PackageListModel.COL_BINB)
|
||||
value1_list = value1.split(', ')
|
||||
value2_list = value2.split(', ')
|
||||
|
||||
value1 = value1_list[0]
|
||||
value2 = value2_list[0]
|
||||
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res==0:
|
||||
cmp_size = cmp(len(value1_list), len(value2_list))
|
||||
if cmp_size==0:
|
||||
name1 = model.get_value(row1, PackageListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, PackageListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
else:
|
||||
return cmp_size
|
||||
else:
|
||||
return cmp_res
|
||||
|
||||
def exclude_item_sort_func(self, model, iter1, iter2, user_data=None):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_FADE_INC)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_INC)
|
||||
@@ -187,7 +167,12 @@ class PackageListModel(gtk.ListStore):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_INC)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_INC)
|
||||
@@ -196,15 +181,14 @@ class PackageListModel(gtk.ListStore):
|
||||
def sort_func(self, model, iter1, iter2, user_data):
|
||||
val1 = model.get_value(iter1, PackageListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, PackageListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
|
||||
def cmp_vals(self, val1, val2, user_data):
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
if val1 is None or val2 is None:
|
||||
return 0
|
||||
elif val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return cmp(val1, val2)
|
||||
return 0
|
||||
|
||||
def convert_vpath_to_path(self, view_model, view_path):
|
||||
# view_model is the model sorted
|
||||
@@ -474,9 +458,9 @@ class RecipeListModel(gtk.ListStore):
|
||||
provide filtered views of the data.
|
||||
"""
|
||||
(COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_INSTALL, COL_PN, COL_FADE_INC, COL_SUMMARY, COL_VERSION,
|
||||
COL_REVISION, COL_HOMEPAGE, COL_BUGTRACKER, COL_FILE) = range(18)
|
||||
COL_REVISION, COL_HOMEPAGE, COL_BUGTRACKER) = range(17)
|
||||
|
||||
__custom_image__ = "Start with an empty image recipe"
|
||||
__custom_image__ = "Create your own image"
|
||||
|
||||
__gsignals__ = {
|
||||
"recipe-selection-changed" : (gobject.SIGNAL_RUN_LAST,
|
||||
@@ -504,9 +488,7 @@ class RecipeListModel(gtk.ListStore):
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.sort_column_id, self.sort_order = RecipeListModel.COL_NAME, gtk.SORT_ASCENDING
|
||||
|
||||
"""
|
||||
Find the model path for the item_name
|
||||
@@ -553,7 +535,12 @@ class RecipeListModel(gtk.ListStore):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_FADE_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
@@ -563,7 +550,12 @@ class RecipeListModel(gtk.ListStore):
|
||||
if user_data:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_INC)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_INC)
|
||||
@@ -572,15 +564,14 @@ class RecipeListModel(gtk.ListStore):
|
||||
def sort_func(self, model, iter1, iter2, user_data):
|
||||
val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
|
||||
val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
|
||||
return self.cmp_vals(val1, val2, user_data)
|
||||
|
||||
def cmp_vals(self, val1, val2, user_data):
|
||||
if val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
if val1 is None or val2 is None:
|
||||
return 0
|
||||
elif val1.startswith(user_data) and not val2.startswith(user_data):
|
||||
return -1
|
||||
elif not val1.startswith(user_data) and val2.startswith(user_data):
|
||||
return 1
|
||||
else:
|
||||
return cmp(val1, val2)
|
||||
return 0
|
||||
|
||||
"""
|
||||
Create, if required, and return a filtered gtk.TreeModelSort
|
||||
@@ -592,11 +583,11 @@ class RecipeListModel(gtk.ListStore):
|
||||
model.set_visible_func(self.tree_model_filter, filter)
|
||||
|
||||
sort = gtk.TreeModelSort(model)
|
||||
sort.connect ('sort-column-changed', self.sort_column_changed_cb)
|
||||
if initial:
|
||||
sort.set_sort_column_id(RecipeListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
elif excluded_items_ahead:
|
||||
|
||||
if excluded_items_ahead:
|
||||
sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
|
||||
elif included_items_ahead:
|
||||
sort.set_default_sort_func(self.include_item_sort_func, search_data)
|
||||
@@ -604,18 +595,15 @@ class RecipeListModel(gtk.ListStore):
|
||||
if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
|
||||
sort.set_default_sort_func(self.sort_func, search_data)
|
||||
else:
|
||||
sort.set_sort_column_id(self.sort_column_id, self.sort_order)
|
||||
sort.set_sort_column_id(RecipeListModel.COL_NAME, gtk.SORT_ASCENDING)
|
||||
sort.set_default_sort_func(None)
|
||||
|
||||
sort.set_sort_func(RecipeListModel.COL_INC, self.sort_column, RecipeListModel.COL_INC)
|
||||
sort.set_sort_func(RecipeListModel.COL_GROUP, self.sort_column, RecipeListModel.COL_GROUP)
|
||||
sort.set_sort_func(RecipeListModel.COL_BINB, self.sort_binb_column)
|
||||
sort.set_sort_func(RecipeListModel.COL_BINB, self.sort_column, RecipeListModel.COL_BINB)
|
||||
sort.set_sort_func(RecipeListModel.COL_LIC, self.sort_column, RecipeListModel.COL_LIC)
|
||||
return sort
|
||||
|
||||
def sort_column_changed_cb (self, data):
|
||||
self.sort_column_id, self.sort_order = data.get_sort_column_id ()
|
||||
|
||||
def sort_column(self, model, row1, row2, col):
|
||||
value1 = model.get_value(row1, col)
|
||||
value2 = model.get_value(row2, col)
|
||||
@@ -630,27 +618,6 @@ class RecipeListModel(gtk.ListStore):
|
||||
name2 = model.get_value(row2, RecipeListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
|
||||
def sort_binb_column(self, model, row1, row2):
|
||||
value1 = model.get_value(row1, RecipeListModel.COL_BINB)
|
||||
value2 = model.get_value(row2, RecipeListModel.COL_BINB)
|
||||
value1_list = value1.split(', ')
|
||||
value2_list = value2.split(', ')
|
||||
|
||||
value1 = value1_list[0]
|
||||
value2 = value2_list[0]
|
||||
|
||||
cmp_res = cmp(value1, value2)
|
||||
if cmp_res==0:
|
||||
cmp_size = cmp(len(value1_list), len(value2_list))
|
||||
if cmp_size==0:
|
||||
name1 = model.get_value(row1, RecipeListModel.COL_NAME)
|
||||
name2 = model.get_value(row2, RecipeListModel.COL_NAME)
|
||||
return cmp(name1,name2)
|
||||
else:
|
||||
return cmp_size
|
||||
else:
|
||||
return cmp_res
|
||||
|
||||
def convert_vpath_to_path(self, view_model, view_path):
|
||||
filtered_model_path = view_model.convert_path_to_child_path(view_path)
|
||||
filtered_model = view_model.get_model()
|
||||
@@ -679,8 +646,15 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.clear()
|
||||
|
||||
# dummy image for prompt
|
||||
self.set_in_list(self.__custom_image__, "Use 'Edit image recipe' to customize recipes and packages " \
|
||||
"to be included in your image ")
|
||||
self.set(self.append(), self.COL_NAME, self.__custom_image__,
|
||||
self.COL_DESC, "Use 'Edit image' to customize recipes and packages " \
|
||||
"to be included in your image ",
|
||||
self.COL_LIC, "", self.COL_GROUP, "",
|
||||
self.COL_DEPS, "", self.COL_BINB, "",
|
||||
self.COL_TYPE, "image", self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, self.__custom_image__,
|
||||
self.COL_SUMMARY, "", self.COL_VERSION, "", self.COL_REVISION, "",
|
||||
self.COL_HOMEPAGE, "", self.COL_BUGTRACKER, "")
|
||||
|
||||
for item in event_model["pn"]:
|
||||
name = item
|
||||
@@ -693,7 +667,6 @@ class RecipeListModel(gtk.ListStore):
|
||||
revision = event_model["pn"][item]["revision"]
|
||||
homepage = event_model["pn"][item]["homepage"]
|
||||
bugtracker = event_model["pn"][item]["bugtracker"]
|
||||
filename = event_model["pn"][item]["filename"]
|
||||
install = []
|
||||
|
||||
depends = event_model["depends"].get(item, []) + event_model["rdepends-pn"].get(item, [])
|
||||
@@ -717,8 +690,7 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.COL_TYPE, atype, self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, " ".join(install), self.COL_PN, item,
|
||||
self.COL_SUMMARY, summary, self.COL_VERSION, version, self.COL_REVISION, revision,
|
||||
self.COL_HOMEPAGE, homepage, self.COL_BUGTRACKER, bugtracker,
|
||||
self.COL_FILE, filename)
|
||||
self.COL_HOMEPAGE, homepage, self.COL_BUGTRACKER, bugtracker)
|
||||
|
||||
self.pn_path = {}
|
||||
it = self.get_iter_first()
|
||||
@@ -728,23 +700,6 @@ class RecipeListModel(gtk.ListStore):
|
||||
self.pn_path[pn] = path
|
||||
it = self.iter_next(it)
|
||||
|
||||
def set_in_list(self, item, desc):
|
||||
self.set(self.append(), self.COL_NAME, item,
|
||||
self.COL_DESC, desc,
|
||||
self.COL_LIC, "", self.COL_GROUP, "",
|
||||
self.COL_DEPS, "", self.COL_BINB, "",
|
||||
self.COL_TYPE, "image", self.COL_INC, False,
|
||||
self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, item,
|
||||
self.COL_SUMMARY, "", self.COL_VERSION, "", self.COL_REVISION, "",
|
||||
self.COL_HOMEPAGE, "", self.COL_BUGTRACKER, "")
|
||||
self.pn_path = {}
|
||||
it = self.get_iter_first()
|
||||
while it:
|
||||
pn = self.get_value(it, self.COL_NAME)
|
||||
path = self.get_path(it)
|
||||
self.pn_path[pn] = path
|
||||
it = self.iter_next(it)
|
||||
|
||||
"""
|
||||
Update the model, send out the notification.
|
||||
"""
|
||||
@@ -895,6 +850,3 @@ class RecipeListModel(gtk.ListStore):
|
||||
|
||||
def get_custom_image_version(self):
|
||||
return self.custom_image_version
|
||||
|
||||
def is_custom_image(self):
|
||||
return self.get_selected_image() == self.__custom_image__
|
||||
|
||||
@@ -28,7 +28,6 @@ from bb.ui.crumbs.hobcolor import HobColors
|
||||
from bb.ui.crumbs.hobwidget import hic, HobImageButton, HobInfoButton, HobAltButton, HobButton
|
||||
from bb.ui.crumbs.hoblistmodel import RecipeListModel
|
||||
from bb.ui.crumbs.hobpages import HobPage
|
||||
from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
|
||||
|
||||
#
|
||||
# ImageConfigurationPage
|
||||
@@ -36,8 +35,7 @@ from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
|
||||
class ImageConfigurationPage (HobPage):
|
||||
|
||||
__dummy_machine__ = "--select a machine--"
|
||||
__dummy_image__ = "--select an image recipe--"
|
||||
__custom_image__ = "Select from my image recipes"
|
||||
__dummy_image__ = "--select a base image--"
|
||||
|
||||
def __init__(self, builder):
|
||||
super(ImageConfigurationPage, self).__init__(builder, "Image configuration")
|
||||
@@ -49,7 +47,6 @@ class ImageConfigurationPage (HobPage):
|
||||
self.machine_combo_changed_by_manual = True
|
||||
self.stopping = False
|
||||
self.warning_shift = 0
|
||||
self.custom_image_selected = None
|
||||
self.create_visual_elements()
|
||||
|
||||
def create_visual_elements(self):
|
||||
@@ -172,8 +169,7 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
def disable_warnings_bar(self):
|
||||
if self.builder.parsing_warnings:
|
||||
if hasattr(self, 'warnings_bar'):
|
||||
self.warnings_bar.hide_all()
|
||||
self.warnings_bar.hide_all()
|
||||
self.builder.parsing_warnings = []
|
||||
|
||||
def create_config_machine(self):
|
||||
@@ -203,9 +199,12 @@ class ImageConfigurationPage (HobPage):
|
||||
markup += "http://www.yoctoproject.org/docs/current/dev-manual/"
|
||||
markup += "dev-manual.html#understanding-and-using-layers\">reference manual</a>."
|
||||
self.layer_info_icon = HobInfoButton("<b>Layers</b>" + "*" + markup, self.get_parent())
|
||||
# self.progress_box = gtk.HBox(False, 6)
|
||||
self.progress_bar = HobProgressBar()
|
||||
# self.progress_box.pack_start(self.progress_bar, expand=True, fill=True)
|
||||
self.stop_button = HobAltButton("Stop")
|
||||
self.stop_button.connect("clicked", self.stop_button_clicked_cb)
|
||||
# self.progress_box.pack_end(stop_button, expand=False, fill=False)
|
||||
self.machine_separator = gtk.HSeparator()
|
||||
|
||||
def set_config_machine_layout(self, show_progress_bar = False):
|
||||
@@ -229,19 +228,17 @@ class ImageConfigurationPage (HobPage):
|
||||
def create_config_baseimg(self):
|
||||
self.image_title = gtk.Label()
|
||||
self.image_title.set_alignment(0, 1.0)
|
||||
mark = "<span %s>Select an image recipe</span>" % self.span_tag('x-large', 'bold')
|
||||
mark = "<span %s>Select a base image</span>" % self.span_tag('x-large', 'bold')
|
||||
self.image_title.set_markup(mark)
|
||||
|
||||
self.image_title_desc = gtk.Label()
|
||||
self.image_title_desc.set_alignment(0, 0.5)
|
||||
|
||||
mark = ("<span %s>Image recipes are a starting point for the type of image you want. "
|
||||
mark = ("<span %s>Base images are a starting point for the type of image you want. "
|
||||
"You can build them as \n"
|
||||
"they are or edit them to suit your needs.\n</span>") % self.span_tag('medium')
|
||||
"they are or customize them to your specific needs.\n</span>") % self.span_tag('medium')
|
||||
self.image_title_desc.set_markup(mark)
|
||||
|
||||
self.image_combo = gtk.combo_box_new_text()
|
||||
self.image_combo.set_row_separator_func(self.combo_separator_func, None)
|
||||
self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
|
||||
|
||||
self.image_desc = gtk.Label()
|
||||
@@ -260,11 +257,6 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
self.image_separator = gtk.HSeparator()
|
||||
|
||||
def combo_separator_func(self, model, iter, user_data):
|
||||
name = model.get_value(iter, 0)
|
||||
if name == "--Separator--":
|
||||
return True
|
||||
|
||||
def set_config_baseimg_layout(self):
|
||||
self.gtable.attach(self.image_title, 0, 40, 15+self.warning_shift, 17+self.warning_shift)
|
||||
self.gtable.attach(self.image_title_desc, 0, 40, 18+self.warning_shift, 22+self.warning_shift)
|
||||
@@ -279,13 +271,15 @@ class ImageConfigurationPage (HobPage):
|
||||
|
||||
# create button "Build image"
|
||||
self.just_bake_button = HobButton("Build image")
|
||||
self.just_bake_button.set_tooltip_text("Build the image recipe as it is")
|
||||
#self.just_bake_button.set_size_request(205, 49)
|
||||
self.just_bake_button.set_tooltip_text("Build target image")
|
||||
self.just_bake_button.connect("clicked", self.just_bake_button_clicked_cb)
|
||||
button_box.pack_end(self.just_bake_button, expand=False, fill=False)
|
||||
|
||||
# create button "Edit image recipe"
|
||||
self.edit_image_button = HobAltButton("Edit image recipe")
|
||||
self.edit_image_button.set_tooltip_text("Customize the recipes and packages to be included in your image")
|
||||
# create button "Edit Image"
|
||||
self.edit_image_button = HobAltButton("Edit image")
|
||||
#self.edit_image_button.set_size_request(205, 49)
|
||||
self.edit_image_button.set_tooltip_text("Edit target image")
|
||||
self.edit_image_button.connect("clicked", self.edit_image_button_clicked_cb)
|
||||
button_box.pack_end(self.edit_image_button, expand=False, fill=False)
|
||||
|
||||
@@ -319,8 +313,6 @@ class ImageConfigurationPage (HobPage):
|
||||
# reset machine_combo_changed_by_manual
|
||||
self.machine_combo_changed_by_manual = True
|
||||
|
||||
self.builder.configuration.selected_image = None
|
||||
|
||||
# Do reparse recipes
|
||||
self.builder.populate_recipe_package_info_async()
|
||||
|
||||
@@ -369,33 +361,7 @@ class ImageConfigurationPage (HobPage):
|
||||
def image_combo_changed_cb(self, combo):
|
||||
self.builder.window_sensitive(False)
|
||||
selected_image = self.image_combo.get_active_text()
|
||||
if selected_image == self.__custom_image__:
|
||||
topdir = self.builder.get_topdir()
|
||||
images_dir = topdir + "/recipes/images/"
|
||||
self.builder.ensure_dir(images_dir)
|
||||
|
||||
dialog = RetrieveImageDialog(images_dir, "Select from my image recipes",
|
||||
self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
image_name = dialog.get_filename()
|
||||
head, tail = os.path.split(image_name)
|
||||
selected_image = os.path.splitext(tail)[0]
|
||||
self.custom_image_selected = selected_image
|
||||
self.update_image_combo(self.builder.recipe_model, selected_image)
|
||||
else:
|
||||
selected_image = self.__dummy_image__
|
||||
self.update_image_combo(self.builder.recipe_model, None)
|
||||
dialog.destroy()
|
||||
else:
|
||||
if self.custom_image_selected:
|
||||
self.custom_image_selected = None
|
||||
self.update_image_combo(self.builder.recipe_model, selected_image)
|
||||
|
||||
if not selected_image or (selected_image == self.__dummy_image__):
|
||||
self.builder.window_sensitive(True)
|
||||
self.just_bake_button.hide()
|
||||
self.edit_image_button.hide()
|
||||
return
|
||||
|
||||
# remove __dummy_image__ item from the store list after first user selection
|
||||
@@ -461,11 +427,6 @@ class ImageConfigurationPage (HobPage):
|
||||
self.image_combo.append_text(self.__dummy_image__)
|
||||
cnt = cnt + 1
|
||||
|
||||
self.image_combo.append_text(self.__custom_image__)
|
||||
self.image_combo.append_text("--Separator--")
|
||||
cnt = cnt + 2
|
||||
|
||||
topdir = self.builder.get_topdir()
|
||||
# append and set active
|
||||
while it:
|
||||
path = image_model.get_path(it)
|
||||
@@ -489,27 +450,16 @@ class ImageConfigurationPage (HobPage):
|
||||
else:
|
||||
allow = True
|
||||
|
||||
file_name = image_model[path][recipe_model.COL_FILE]
|
||||
if file_name and topdir in file_name:
|
||||
allow = False
|
||||
|
||||
if allow:
|
||||
self.image_combo.append_text(image_name)
|
||||
if image_name == selected_image:
|
||||
active = cnt
|
||||
cnt = cnt + 1
|
||||
self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
|
||||
|
||||
self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
|
||||
if selected_image == self.builder.recipe_model.__custom_image__:
|
||||
active = cnt
|
||||
|
||||
if self.custom_image_selected:
|
||||
self.image_combo.append_text("--Separator--")
|
||||
self.image_combo.append_text(self.custom_image_selected)
|
||||
cnt = cnt + 2
|
||||
if self.custom_image_selected == selected_image:
|
||||
active = cnt
|
||||
|
||||
self.image_combo.set_active(active)
|
||||
|
||||
if active != 0:
|
||||
@@ -520,14 +470,14 @@ class ImageConfigurationPage (HobPage):
|
||||
def layer_button_clicked_cb(self, button):
|
||||
# Create a layer selection dialog
|
||||
self.builder.show_layer_selection_dialog()
|
||||
|
||||
|
||||
def view_adv_configuration_button_clicked_cb(self, button):
|
||||
# Create an advanced settings dialog
|
||||
response, settings_changed = self.builder.show_adv_settings_dialog()
|
||||
if not response:
|
||||
return
|
||||
if settings_changed:
|
||||
self.builder.reparse_post_adv_settings()
|
||||
self.builder.reparse_post_adv_settings()
|
||||
|
||||
def just_bake_button_clicked_cb(self, button):
|
||||
self.builder.parsing_warnings = []
|
||||
|
||||
@@ -27,7 +27,6 @@ from bb.ui.crumbs.hobwidget import hic, HobViewTable, HobAltButton, HobButton
|
||||
from bb.ui.crumbs.hobpages import HobPage
|
||||
import subprocess
|
||||
from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
|
||||
from bb.ui.crumbs.hig.saveimagedialog import SaveImageDialog
|
||||
|
||||
#
|
||||
# ImageDetailsPage
|
||||
@@ -189,10 +188,7 @@ class ImageDetailsPage (HobPage):
|
||||
self.image_store = []
|
||||
self.button_ids = {}
|
||||
self.details_bottom_buttons = gtk.HBox(False, 6)
|
||||
self.image_saved = False
|
||||
self.create_visual_elements()
|
||||
self.name_field_template = ""
|
||||
self.description_field_template = ""
|
||||
|
||||
def create_visual_elements(self):
|
||||
# create visual elements
|
||||
@@ -251,7 +247,7 @@ class ImageDetailsPage (HobPage):
|
||||
self.pack_start(self.group_align, expand=True, fill=True)
|
||||
|
||||
self.build_result = None
|
||||
if self.image_saved or (self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING):
|
||||
if self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING:
|
||||
# building is the previous step
|
||||
icon = gtk.Image()
|
||||
pixmap_path = hic.ICON_INDI_CONFIRM_FILE
|
||||
@@ -259,14 +255,11 @@ class ImageDetailsPage (HobPage):
|
||||
pix_buffer = gtk.gdk.pixbuf_new_from_file(pixmap_path)
|
||||
icon.set_from_pixbuf(pix_buffer)
|
||||
varlist = [""]
|
||||
if self.image_saved:
|
||||
vallist = ["Your image recipe has been saved"]
|
||||
else:
|
||||
vallist = ["Your image is ready"]
|
||||
vallist = ["Your image is ready"]
|
||||
self.build_result = self.BuildDetailBox(varlist=varlist, vallist=vallist, icon=icon, color=color)
|
||||
self.box_group_area.pack_start(self.build_result, expand=False, fill=False)
|
||||
|
||||
self.buttonlist = ["Build new image", "Save image recipe", "Run image", "Deploy image"]
|
||||
self.buttonlist = ["Build new image", "Run image", "Deploy image"]
|
||||
|
||||
# Name
|
||||
self.image_store = []
|
||||
@@ -340,18 +333,13 @@ class ImageDetailsPage (HobPage):
|
||||
# self.kernel_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=change_kernel_button)
|
||||
# self.box_group_area.pack_start(self.kernel_detail, expand=True, fill=True)
|
||||
|
||||
# Machine, Image recipe and Layers
|
||||
# Machine, Base image and Layers
|
||||
layer_num_limit = 15
|
||||
varlist = ["Machine: ", "Image recipe: ", "Layers: "]
|
||||
varlist = ["Machine: ", "Base image: ", "Layers: "]
|
||||
vallist = []
|
||||
self.setting_detail = None
|
||||
if self.build_succeeded:
|
||||
vallist.append(machine)
|
||||
if self.builder.recipe_model.is_custom_image():
|
||||
if self.builder.configuration.initial_selected_image == self.builder.recipe_model.__custom_image__:
|
||||
base_image ="New image recipe"
|
||||
else:
|
||||
base_image = self.builder.configuration.initial_selected_image + " (edited)"
|
||||
vallist.append(base_image)
|
||||
i = 0
|
||||
for layer in layers:
|
||||
@@ -371,7 +359,7 @@ class ImageDetailsPage (HobPage):
|
||||
i += 1
|
||||
|
||||
edit_config_button = HobAltButton("Edit configuration")
|
||||
edit_config_button.set_tooltip_text("Edit machine and image recipe")
|
||||
edit_config_button.set_tooltip_text("Edit machine, base image and recipes")
|
||||
edit_config_button.connect("clicked", self.edit_config_button_clicked_cb)
|
||||
self.setting_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=edit_config_button)
|
||||
self.box_group_area.pack_start(self.setting_detail, expand=True, fill=True)
|
||||
@@ -381,8 +369,6 @@ class ImageDetailsPage (HobPage):
|
||||
vallist = []
|
||||
vallist.append(pkg_num)
|
||||
vallist.append(default_image_size)
|
||||
self.builder.configuration.image_size = default_image_size
|
||||
self.builder.configuration.image_packages = self.builder.configuration.selected_packages
|
||||
if self.build_succeeded:
|
||||
edit_packages_button = HobAltButton("Edit packages")
|
||||
edit_packages_button.set_tooltip_text("Edit the packages included in your image")
|
||||
@@ -403,7 +389,6 @@ class ImageDetailsPage (HobPage):
|
||||
self.show_all()
|
||||
if self.kernel_detail and (not is_runnable):
|
||||
self.kernel_detail.hide()
|
||||
self.image_saved = False
|
||||
|
||||
def view_files_clicked_cb(self, button, image_addr):
|
||||
subprocess.call("xdg-open /%s" % image_addr, shell=True)
|
||||
@@ -587,15 +572,6 @@ class ImageDetailsPage (HobPage):
|
||||
created = True
|
||||
is_runnable = True
|
||||
|
||||
name = "Save image recipe"
|
||||
if name in buttonlist and self.builder.recipe_model.is_custom_image():
|
||||
save_button = HobAltButton("Save image recipe")
|
||||
save_button.set_tooltip_text("Keep your changes saving them as an image recipe")
|
||||
save_button.set_sensitive(not self.image_saved)
|
||||
button_id = save_button.connect("clicked", self.save_button_clicked_cb)
|
||||
self.button_ids[button_id] = save_button
|
||||
self.details_bottom_buttons.pack_end(save_button, expand=False, fill=False)
|
||||
|
||||
name = "Build new image"
|
||||
if name in buttonlist:
|
||||
# create button "Build new image"
|
||||
@@ -630,16 +606,6 @@ class ImageDetailsPage (HobPage):
|
||||
else:
|
||||
self.builder.runqemu_image(self.toggled_image, self.sel_kernel)
|
||||
|
||||
def save_button_clicked_cb(self, button):
|
||||
topdir = self.builder.get_topdir()
|
||||
images_dir = topdir + "/recipes/images/"
|
||||
self.builder.ensure_dir(images_dir)
|
||||
|
||||
dialog = SaveImageDialog(images_dir, self.name_field_template, self.description_field_template,
|
||||
"Save image recipe", self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
response = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
def build_new_button_clicked_cb(self, button):
|
||||
self.builder.initiate_new_build_async()
|
||||
|
||||
|
||||
@@ -150,8 +150,12 @@ class PackageSelectionPage (HobPage):
|
||||
sort_model = self.package_model.tree_model(filter, initial=True)
|
||||
tab.set_model(sort_model)
|
||||
tab.connect("toggled", self.table_toggled_cb, name)
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
|
||||
if name == "Included packages":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
if name == "All packages":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
self.ins.append_page(tab, page['name'], page['tooltip'])
|
||||
self.tables.append(tab)
|
||||
|
||||
@@ -195,7 +199,6 @@ class PackageSelectionPage (HobPage):
|
||||
if self.package_model.filtered_nb == 0:
|
||||
if not self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
|
||||
self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
|
||||
self.ins.get_nth_page(current_tab).top_bar.show()
|
||||
self.ins.get_nth_page(current_tab).scroll.hide()
|
||||
else:
|
||||
@@ -243,13 +246,6 @@ class PackageSelectionPage (HobPage):
|
||||
self.builder.parsing_warnings = []
|
||||
self.builder.build_image()
|
||||
|
||||
def refresh_tables(self):
|
||||
self.ins.reset_entry(self.ins.search, 0)
|
||||
for tab in self.tables:
|
||||
index = self.tables.index(tab)
|
||||
filter = self.pages[index]['filter']
|
||||
tab.set_model(self.package_model.tree_model(filter, initial=True))
|
||||
|
||||
def back_button_clicked_cb(self, button):
|
||||
if self.builder.previous_step == self.builder.IMAGE_GENERATED:
|
||||
self.builder.restore_initial_selected_packages()
|
||||
@@ -257,7 +253,6 @@ class PackageSelectionPage (HobPage):
|
||||
self.builder.show_image_details()
|
||||
else:
|
||||
self.builder.show_configuration()
|
||||
self.refresh_tables()
|
||||
|
||||
def refresh_selection(self):
|
||||
self.builder.configuration.selected_packages = self.package_model.get_selected_packages()
|
||||
@@ -266,19 +261,16 @@ class PackageSelectionPage (HobPage):
|
||||
selected_packages_size = self.package_model.get_packages_size()
|
||||
selected_packages_size_str = HobPage._size_to_string(selected_packages_size)
|
||||
|
||||
if self.builder.configuration.image_packages == self.builder.configuration.selected_packages:
|
||||
image_total_size_str = self.builder.configuration.image_size
|
||||
else:
|
||||
image_overhead_factor = self.builder.configuration.image_overhead_factor
|
||||
image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB
|
||||
image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB
|
||||
base_size = image_overhead_factor * selected_packages_size
|
||||
image_total_size = max(base_size, image_rootfs_size) + image_extra_size
|
||||
if "zypper" in self.builder.configuration.selected_packages:
|
||||
image_total_size += (51200 * 1024)
|
||||
image_total_size_str = HobPage._size_to_string(image_total_size)
|
||||
image_overhead_factor = self.builder.configuration.image_overhead_factor
|
||||
image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB
|
||||
image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB
|
||||
base_size = image_overhead_factor * selected_packages_size
|
||||
image_total_size = max(base_size, image_rootfs_size) + image_extra_size
|
||||
if "zypper" in self.builder.configuration.selected_packages:
|
||||
image_total_size += (51200 * 1024)
|
||||
image_total_size_str = HobPage._size_to_string(image_total_size)
|
||||
|
||||
self.label.set_label("Packages included: %s\nSelected packages size: %s\nEstimated image size: %s" %
|
||||
self.label.set_label("Packages included: %s\nSelected packages size: %s\nTotal image size: %s" %
|
||||
(selected_packages_num, selected_packages_size_str, image_total_size_str))
|
||||
self.ins.show_indicator_icon("Included packages", selected_packages_num)
|
||||
|
||||
@@ -286,9 +278,12 @@ class PackageSelectionPage (HobPage):
|
||||
if not self.package_model.path_included(path):
|
||||
self.package_model.include_item(item_path=path, binb="User Selected")
|
||||
else:
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
if pagename == "Included packages":
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
else:
|
||||
self.package_model.exclude_item(item_path=path)
|
||||
|
||||
self.refresh_selection()
|
||||
if not self.builder.customized:
|
||||
@@ -298,9 +293,6 @@ class PackageSelectionPage (HobPage):
|
||||
self.builder.rcppkglist_populated()
|
||||
|
||||
self.builder.window_sensitive(True)
|
||||
view_model = view_tree.get_model()
|
||||
vpath = self.package_model.convert_path_to_vpath(view_model, path)
|
||||
view_tree.set_cursor(vpath)
|
||||
|
||||
def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
|
||||
# Click to include a package
|
||||
@@ -310,15 +302,11 @@ class PackageSelectionPage (HobPage):
|
||||
glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree):
|
||||
#after the fadeout the table will be sorted as before
|
||||
self.sort_column_id = self.package_model.sort_column_id
|
||||
self.sort_order = self.package_model.sort_order
|
||||
|
||||
self.package_model.resync_fadeout_column(self.package_model.get_iter_first())
|
||||
# Check out a model which base on the column COL_FADE_INC,
|
||||
# it's save the prev state of column COL_INC before do exclude_item
|
||||
filter = { PackageListModel.COL_FADE_INC : [True]}
|
||||
new_model = self.package_model.tree_model(filter, excluded_items_ahead=True)
|
||||
new_model = self.package_model.tree_model(filter)
|
||||
tree.set_model(new_model)
|
||||
tree.expand_all()
|
||||
|
||||
@@ -344,10 +332,8 @@ class PackageSelectionPage (HobPage):
|
||||
|
||||
cell.fadeout(tree, 1000, to_render_cells)
|
||||
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
|
||||
self.package_model.sort_column_id = self.sort_column_id
|
||||
self.package_model.sort_order = self.sort_order
|
||||
tree.set_model(self.package_model.tree_model(filter))
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree):
|
||||
tree.set_model(self.package_model.tree_model(self.pages[0]['filter']))
|
||||
tree.expand_all()
|
||||
|
||||
def set_packages_curr_tab(self, curr_page):
|
||||
|
||||
@@ -162,8 +162,15 @@ class RecipeSelectionPage (HobPage):
|
||||
sort_model = self.recipe_model.tree_model(filter, initial=True)
|
||||
tab.set_model(sort_model)
|
||||
tab.connect("toggled", self.table_toggled_cb, name)
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
|
||||
if name == "Included recipes":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
if name == "Package Groups":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include)
|
||||
if name == "All recipes":
|
||||
tab.connect("button-release-event", self.button_click_cb)
|
||||
tab.connect("cell-fadeinout-stopped", self.button_click_cb)
|
||||
self.ins.append_page(tab, page['name'], page['tooltip'])
|
||||
self.tables.append(tab)
|
||||
|
||||
@@ -207,7 +214,6 @@ class RecipeSelectionPage (HobPage):
|
||||
if self.recipe_model.filtered_nb == 0:
|
||||
if not self.ins.get_nth_page(current_tab).top_bar:
|
||||
self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
|
||||
self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
|
||||
self.ins.get_nth_page(current_tab).top_bar.show()
|
||||
self.ins.get_nth_page(current_tab).scroll.hide()
|
||||
else:
|
||||
@@ -237,22 +243,13 @@ class RecipeSelectionPage (HobPage):
|
||||
self.builder.show_recipe_property_dialog(properties)
|
||||
|
||||
def build_packages_clicked_cb(self, button):
|
||||
self.refresh_tables()
|
||||
self.builder.build_packages()
|
||||
|
||||
def refresh_tables(self):
|
||||
self.ins.reset_entry(self.ins.search, 0)
|
||||
for tab in self.tables:
|
||||
index = self.tables.index(tab)
|
||||
filter = self.pages[index]['filter']
|
||||
tab.set_model(self.recipe_model.tree_model(filter, search_data="", initial=True))
|
||||
|
||||
def back_button_clicked_cb(self, button):
|
||||
self.builder.recipe_model.set_selected_image(self.builder.configuration.initial_selected_image)
|
||||
self.builder.image_configuration_page.update_image_combo(self.builder.recipe_model, self.builder.configuration.initial_selected_image)
|
||||
self.builder.image_configuration_page.update_image_desc()
|
||||
self.builder.show_configuration()
|
||||
self.refresh_tables()
|
||||
|
||||
def refresh_selection(self):
|
||||
self.builder.configuration.selected_image = self.recipe_model.get_selected_image()
|
||||
@@ -263,9 +260,12 @@ class RecipeSelectionPage (HobPage):
|
||||
if not self.recipe_model.path_included(path):
|
||||
self.recipe_model.include_item(item_path=path, binb="User Selected", image_contents=False)
|
||||
else:
|
||||
self.pre_fadeout_checkout_include(view_tree, pagename)
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
if pagename == "Included recipes":
|
||||
self.pre_fadeout_checkout_include(view_tree)
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
self.render_fadeout(view_tree, cell)
|
||||
else:
|
||||
self.recipe_model.exclude_item(item_path=path)
|
||||
|
||||
self.refresh_selection()
|
||||
if not self.builder.customized:
|
||||
@@ -275,10 +275,6 @@ class RecipeSelectionPage (HobPage):
|
||||
|
||||
self.builder.window_sensitive(True)
|
||||
|
||||
view_model = view_tree.get_model()
|
||||
vpath = self.recipe_model.convert_path_to_vpath(view_model, path)
|
||||
view_tree.set_cursor(vpath)
|
||||
|
||||
def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
|
||||
# Click to include a recipe
|
||||
self.builder.window_sensitive(False)
|
||||
@@ -286,11 +282,7 @@ class RecipeSelectionPage (HobPage):
|
||||
path = self.recipe_model.convert_vpath_to_path(view_model, view_path)
|
||||
glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree, pagename):
|
||||
#after the fadeout the table will be sorted as before
|
||||
self.sort_column_id = self.recipe_model.sort_column_id
|
||||
self.sort_order = self.recipe_model.sort_order
|
||||
|
||||
def pre_fadeout_checkout_include(self, tree):
|
||||
#resync the included items to a backup fade include column
|
||||
it = self.recipe_model.get_iter_first()
|
||||
while it:
|
||||
@@ -299,14 +291,8 @@ class RecipeSelectionPage (HobPage):
|
||||
it = self.recipe_model.iter_next(it)
|
||||
# Check out a model which base on the column COL_FADE_INC,
|
||||
# it's save the prev state of column COL_INC before do exclude_item
|
||||
filter = { RecipeListModel.COL_FADE_INC:[True] }
|
||||
if pagename == "Included recipes":
|
||||
filter[RecipeListModel.COL_TYPE] = ['recipe', 'packagegroup']
|
||||
elif pagename == "All recipes":
|
||||
filter[RecipeListModel.COL_TYPE] = ['recipe']
|
||||
else:
|
||||
filter[RecipeListModel.COL_TYPE] = ['packagegroup']
|
||||
|
||||
filter = { RecipeListModel.COL_FADE_INC : [True],
|
||||
RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] }
|
||||
new_model = self.recipe_model.tree_model(filter, excluded_items_ahead=True)
|
||||
tree.set_model(new_model)
|
||||
|
||||
@@ -326,10 +312,8 @@ class RecipeSelectionPage (HobPage):
|
||||
|
||||
cell.fadeout(tree, 1000, to_render_cells)
|
||||
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
|
||||
self.recipe_model.sort_column_id = self.sort_column_id
|
||||
self.recipe_model.sort_order = self.sort_order
|
||||
tree.set_model(self.recipe_model.tree_model(filter))
|
||||
def after_fadeout_checkin_include(self, table, ctrl, cell, tree):
|
||||
tree.set_model(self.recipe_model.tree_model(self.pages[0]['filter']))
|
||||
|
||||
def set_recipe_curr_tab(self, curr_page):
|
||||
self.ins.set_current_page(curr_page)
|
||||
|
||||
@@ -375,16 +375,10 @@ class RunningBuild (gobject.GObject):
|
||||
r = "R"
|
||||
else:
|
||||
r = ""
|
||||
|
||||
extra = ''
|
||||
if not event._reasons:
|
||||
if event._close_matches:
|
||||
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
|
||||
|
||||
if event._dependees:
|
||||
msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s\n" % (r, event._item, ", ".join(event._dependees), r, extra)
|
||||
msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)\n" % (r, event._item, ", ".join(event._dependees), r)
|
||||
else:
|
||||
msg = "Nothing %sPROVIDES '%s'%s\n" % (r, event._item, extra)
|
||||
msg = "Nothing %sPROVIDES '%s'\n" % (r, event._item)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
msg += ("%s\n" % reason)
|
||||
|
||||
187
bitbake/lib/bb/ui/crumbs/template.py
Normal file
@@ -0,0 +1,187 @@
|
||||
#
|
||||
# BitBake Graphical GTK User Interface
|
||||
#
|
||||
# Copyright (C) 2011 Intel Corporation
|
||||
#
|
||||
# Authored by Shane Wang <shane.wang@intel.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import gobject
|
||||
import os
|
||||
import re
|
||||
|
||||
class File(gobject.GObject):
|
||||
|
||||
def __init__(self, pathfilename, suffix):
|
||||
if not pathfilename.endswith(suffix):
|
||||
pathfilename = "%s%s" % (pathfilename, suffix)
|
||||
gobject.GObject.__init__(self)
|
||||
self.pathfilename = pathfilename
|
||||
|
||||
def readFile(self):
|
||||
if not os.path.isfile(self.pathfilename):
|
||||
return None
|
||||
if not os.path.exists(self.pathfilename):
|
||||
return None
|
||||
|
||||
with open(self.pathfilename, 'r') as f:
|
||||
contents = f.readlines()
|
||||
f.close()
|
||||
|
||||
return contents
|
||||
|
||||
def writeFile(self, contents):
|
||||
if os.path.exists(self.pathfilename):
|
||||
orig = "%s.orig" % self.pathfilename
|
||||
if os.path.exists(orig):
|
||||
os.remove(orig)
|
||||
os.rename(self.pathfilename, orig)
|
||||
|
||||
with open(self.pathfilename, 'w') as f:
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
class ConfigFile(File):
|
||||
"""
|
||||
This object does save general config file. (say bblayers.conf, or local.conf). Again, it is the base class for other template files and image bb files.
|
||||
"""
|
||||
def __init__(self, pathfilename, suffix = None, header = None):
|
||||
if suffix:
|
||||
File.__init__(self, pathfilename, suffix)
|
||||
else:
|
||||
File.__init__(self, pathfilename, ".conf")
|
||||
if header:
|
||||
self.header = header
|
||||
else:
|
||||
self.header = "# Config generated by Hob\n\n"
|
||||
self.dictionary = {}
|
||||
|
||||
def setVar(self, var, val):
|
||||
if isinstance(val, list):
|
||||
liststr = ""
|
||||
if val:
|
||||
i = 0
|
||||
for value in val:
|
||||
if i < len(val) - 1:
|
||||
liststr += "%s " % value
|
||||
else:
|
||||
liststr += "%s" % value
|
||||
i += 1
|
||||
self.dictionary[var] = liststr
|
||||
else:
|
||||
self.dictionary[var] = val
|
||||
|
||||
def save(self):
|
||||
contents = self.header
|
||||
for var, val in self.dictionary.items():
|
||||
contents += "%s = \"%s\"\n" % (var, val)
|
||||
File.writeFile(self, contents)
|
||||
|
||||
class HobTemplateFile(ConfigFile):
|
||||
"""
|
||||
This object does save or load hob specific file.
|
||||
"""
|
||||
def __init__(self, pathfilename):
|
||||
ConfigFile.__init__(self, pathfilename, ".hob", "# Hob Template generated by Hob\n\n")
|
||||
|
||||
def getVar(self, var):
|
||||
if var in self.dictionary:
|
||||
return self.dictionary[var]
|
||||
else:
|
||||
return ""
|
||||
|
||||
def getVersion(self):
|
||||
contents = ConfigFile.readFile(self)
|
||||
|
||||
pattern = "^\s*(\S+)\s*=\s*(\".*?\")"
|
||||
|
||||
for line in contents:
|
||||
match = re.search(pattern, line)
|
||||
if match:
|
||||
if match.group(1) == "VERSION":
|
||||
return match.group(2).strip('"')
|
||||
return None
|
||||
|
||||
def load(self):
|
||||
contents = ConfigFile.readFile(self)
|
||||
self.dictionary.clear()
|
||||
|
||||
pattern = "^\s*(\S+)\s*=\s*(\".*?\")"
|
||||
|
||||
for line in contents:
|
||||
match = re.search(pattern, line)
|
||||
if match:
|
||||
var = match.group(1)
|
||||
val = match.group(2).strip('"')
|
||||
self.dictionary[var] = val
|
||||
return self.dictionary
|
||||
|
||||
class RecipeFile(ConfigFile):
|
||||
"""
|
||||
This object is for image bb file.
|
||||
"""
|
||||
def __init__(self, pathfilename):
|
||||
ConfigFile.__init__(self, pathfilename, ".bb", "# Recipe generated by Hob\n\ninherit core-image\n")
|
||||
|
||||
class TemplateMgr(gobject.GObject):
|
||||
|
||||
__gRecipeVars__ = ["DEPENDS", "IMAGE_INSTALL"]
|
||||
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
self.template_hob = None
|
||||
self.bblayers_conf = None
|
||||
self.local_conf = None
|
||||
self.image_bb = None
|
||||
|
||||
@classmethod
|
||||
def convert_to_template_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "template-", filename, ".hob")
|
||||
|
||||
@classmethod
|
||||
def convert_to_image_pathfilename(cls, filename, path):
|
||||
return "%s/%s%s%s" % (path, "hob-image-", filename, ".bb")
|
||||
|
||||
def open(self, filename, path):
|
||||
self.template_hob = HobTemplateFile(TemplateMgr.convert_to_template_pathfilename(filename, path))
|
||||
self.image_bb = RecipeFile(TemplateMgr.convert_to_image_pathfilename(filename, path))
|
||||
|
||||
def setVar(self, var, val):
|
||||
if var in TemplateMgr.__gRecipeVars__:
|
||||
self.image_bb.setVar(var, val)
|
||||
|
||||
self.template_hob.setVar(var, val)
|
||||
|
||||
def save(self):
|
||||
self.image_bb.save()
|
||||
self.template_hob.save()
|
||||
|
||||
def getVersion(self, path):
|
||||
return HobTemplateFile(path).getVersion()
|
||||
|
||||
def load(self, path):
|
||||
self.template_hob = HobTemplateFile(path)
|
||||
self.dictionary = self.template_hob.load()
|
||||
|
||||
def getVar(self, var):
|
||||
return self.template_hob.getVar(var)
|
||||
|
||||
def destroy(self):
|
||||
if self.template_hob:
|
||||
del self.template_hob
|
||||
template_hob = None
|
||||
if self.image_bb:
|
||||
del self.image_bb
|
||||
self.image_bb = None
|
||||
@@ -154,7 +154,7 @@ class DepExplorer(gtk.Window):
|
||||
|
||||
def on_cursor_changed(self, selection):
|
||||
(model, it) = selection.get_selected()
|
||||
if it is None:
|
||||
if iter is None:
|
||||
current_package = None
|
||||
else:
|
||||
current_package = model.get_value(it, COL_PKG_NAME)
|
||||
@@ -163,17 +163,23 @@ class DepExplorer(gtk.Window):
|
||||
self.revdep_treeview.set_current_package(current_package)
|
||||
|
||||
|
||||
def parse(self, depgraph):
|
||||
for package in depgraph["pn"]:
|
||||
self.pkg_model.insert(0, (package,))
|
||||
def parse(depgraph, pkg_model, depends_model):
|
||||
for package in depgraph["pn"]:
|
||||
pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
|
||||
|
||||
for package in depgraph["depends"]:
|
||||
for depend in depgraph["depends"][package]:
|
||||
self.depends_model.insert (0, (TYPE_DEP, package, depend))
|
||||
for package in depgraph["depends"]:
|
||||
for depend in depgraph["depends"][package]:
|
||||
depends_model.set (depends_model.append(),
|
||||
COL_DEP_TYPE, TYPE_DEP,
|
||||
COL_DEP_PARENT, package,
|
||||
COL_DEP_PACKAGE, depend)
|
||||
|
||||
for package in depgraph["rdepends-pn"]:
|
||||
for rdepend in depgraph["rdepends-pn"][package]:
|
||||
self.depends_model.insert (0, (TYPE_RDEP, package, rdepend))
|
||||
for package in depgraph["rdepends-pn"]:
|
||||
for rdepend in depgraph["rdepends-pn"][package]:
|
||||
depends_model.set (depends_model.append(),
|
||||
COL_DEP_TYPE, TYPE_RDEP,
|
||||
COL_DEP_PARENT, package,
|
||||
COL_DEP_PACKAGE, rdepend)
|
||||
|
||||
|
||||
class gtkthread(threading.Thread):
|
||||
@@ -190,18 +196,16 @@ class gtkthread(threading.Thread):
|
||||
gtkthread.quit.set()
|
||||
|
||||
|
||||
def main(server, eventHandler, params):
|
||||
def main(server, eventHandler):
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if error:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
if not cmdline or cmdline[0] != "generateDotGraph":
|
||||
elif not cmdline or cmdline[0] != "generateDotGraph":
|
||||
print("This UI is only compatible with the -g option")
|
||||
return 1
|
||||
ret, error = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
|
||||
@@ -287,7 +291,7 @@ def main(server, eventHandler, params):
|
||||
|
||||
if isinstance(event, bb.event.DepTreeGenerated):
|
||||
gtk.gdk.threads_enter()
|
||||
dep.parse(event._depgraph)
|
||||
parse(event._depgraph, dep.pkg_model, dep.depends_model)
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
if isinstance(event, bb.command.CommandCompleted):
|
||||
@@ -314,7 +318,7 @@ def main(server, eventHandler, params):
|
||||
break
|
||||
if shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
print('Unable to cleanly stop: %s' % error)
|
||||
if shutdown == 0:
|
||||
|
||||
@@ -63,7 +63,7 @@ class MainWindow (gtk.Window):
|
||||
scrolled_window.add (self.cur_build_tv)
|
||||
|
||||
|
||||
def main (server, eventHandler, params):
|
||||
def main (server, eventHandler):
|
||||
gobject.threads_init()
|
||||
gtk.gdk.threads_init()
|
||||
|
||||
@@ -80,15 +80,13 @@ def main (server, eventHandler, params):
|
||||
running_build.connect ("build-failed", running_build_failed_cb)
|
||||
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if error:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
@@ -117,5 +115,5 @@ def main (server, eventHandler, params):
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
server.runCommand(["stateForceShutdown"])
|
||||
server.runCommand(["stateStop"])
|
||||
|
||||
|
||||
@@ -58,21 +58,7 @@ def event_handle_idle_func(eventHandler, hobHandler):
|
||||
event = eventHandler.getEvent()
|
||||
return True
|
||||
|
||||
_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
|
||||
"bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
|
||||
"bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
|
||||
"bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
|
||||
"bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
|
||||
"bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
|
||||
"bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
|
||||
"bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
|
||||
"bb.event.SanityCheckPassed", "bb.event.SanityCheckFailed", "bb.event.PackageInfo",
|
||||
"bb.event.TargetsTreeGenerated", "bb.event.ConfigFilesFound", "bb.event.ConfigFilePathFound",
|
||||
"bb.event.FilesMatchingFound", "bb.event.NetworkTestFailed", "bb.event.NetworkTestPassed",
|
||||
"bb.event.BuildStarted", "bb.event.BuildCompleted", "bb.event.DiskFull"]
|
||||
|
||||
def main (server, eventHandler, params):
|
||||
params.updateFromServer(server)
|
||||
def main (server = None, eventHandler = None):
|
||||
gobject.threads_init()
|
||||
|
||||
# That indicates whether the Hob and the bitbake server are
|
||||
@@ -81,8 +67,6 @@ def main (server, eventHandler, params):
|
||||
recipe_model = RecipeListModel()
|
||||
package_model = PackageListModel()
|
||||
|
||||
llevel, debug_domains = bb.msg.constructLogOptions()
|
||||
server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
|
||||
hobHandler = HobHandler(server, recipe_model, package_model)
|
||||
builder = Builder(hobHandler, recipe_model, package_model)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -201,11 +201,11 @@ class TerminalFilter(object):
|
||||
content = "No currently running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total)
|
||||
else:
|
||||
content = "Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total)
|
||||
print(content)
|
||||
print content
|
||||
lines = 1 + int(len(content) / (self.columns + 1))
|
||||
for tasknum, task in enumerate(tasks):
|
||||
content = "%s: %s" % (tasknum, task)
|
||||
print(content)
|
||||
print content
|
||||
lines = lines + 1 + int(len(content) / (self.columns + 1))
|
||||
self.footer_present = lines
|
||||
self.lastpids = runningpids[:]
|
||||
@@ -216,34 +216,21 @@ class TerminalFilter(object):
|
||||
fd = sys.stdin.fileno()
|
||||
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
|
||||
|
||||
def _log_settings_from_server(server):
|
||||
def main(server, eventHandler, tf = TerminalFilter):
|
||||
|
||||
# Get values of variables which control our output
|
||||
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return 1
|
||||
loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return 1
|
||||
consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return includelogs, loglines, consolelogfile
|
||||
|
||||
_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
|
||||
"bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
|
||||
"bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
|
||||
"bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
|
||||
"bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
|
||||
"bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
|
||||
"bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
|
||||
"bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent"]
|
||||
|
||||
def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
includelogs, loglines, consolelogfile = _log_settings_from_server(server)
|
||||
return 1
|
||||
|
||||
if sys.stdin.isatty() and sys.stdout.isatty():
|
||||
log_exec_tty = True
|
||||
@@ -258,12 +245,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
|
||||
if params.options.remote_server and params.options.kill_server:
|
||||
server.terminateServer()
|
||||
return
|
||||
|
||||
if consolelogfile and not params.options.show_environment:
|
||||
if consolelogfile:
|
||||
bb.utils.mkdirhier(os.path.dirname(consolelogfile))
|
||||
conlogformat = bb.msg.BBLogFormatter(format_str)
|
||||
consolelog = logging.FileHandler(consolelogfile)
|
||||
@@ -271,27 +253,24 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
consolelog.setFormatter(conlogformat)
|
||||
logger.addHandler(consolelog)
|
||||
|
||||
llevel, debug_domains = bb.msg.constructLogOptions()
|
||||
server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
|
||||
|
||||
if not params.observe_only:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
if not cmdline:
|
||||
try:
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if error:
|
||||
logger.error("Unable to get bitbake commandline arguments: %s" % error)
|
||||
return 1
|
||||
elif not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
|
||||
ret, error = server.runCommand(cmdline['action'])
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
logger.error("Command '%s' failed: %s" % (cmdline, error))
|
||||
return 1
|
||||
elif ret != True:
|
||||
logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
|
||||
return 1
|
||||
|
||||
except xmlrpclib.Fault as x:
|
||||
logger.error("XMLRPC Fault getting commandline:\n %s" % x)
|
||||
return 1
|
||||
|
||||
parseprogress = None
|
||||
cacheprogress = None
|
||||
@@ -306,10 +285,8 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
while True:
|
||||
try:
|
||||
event = eventHandler.waitEvent(0)
|
||||
if event is None:
|
||||
termfilter.updateFooter()
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
termfilter.updateFooter()
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
if event is None:
|
||||
if main.shutdown > 1:
|
||||
break
|
||||
@@ -323,7 +300,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
if log_exec_tty:
|
||||
tries = event.retries
|
||||
while tries:
|
||||
print("Trying to run: %s" % event.prog)
|
||||
print "Trying to run: %s" % event.prog
|
||||
if os.system(event.prog) == 0:
|
||||
break
|
||||
time.sleep(event.sleep_delay)
|
||||
@@ -340,7 +317,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
elif event.levelno == format.WARNING:
|
||||
warnings = warnings + 1
|
||||
# For "normal" logging conditions, don't show note logs from tasks
|
||||
# but do show them if the user has changed the default log level to
|
||||
# but do show them if the user has changed the default log level to
|
||||
# include verbose/debug messages
|
||||
if event.taskpid != 0 and event.levelno <= format.NOTE:
|
||||
continue
|
||||
@@ -405,9 +382,8 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
|
||||
if isinstance(event, bb.command.CommandFailed):
|
||||
return_value = event.exitcode
|
||||
if event.error:
|
||||
errors = errors + 1
|
||||
logger.error("Command execution failed: %s", event.error)
|
||||
errors = errors + 1
|
||||
logger.error("Command execution failed: %s", event.error)
|
||||
main.shutdown = 2
|
||||
continue
|
||||
if isinstance(event, bb.command.CommandExit):
|
||||
@@ -431,15 +407,10 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
else:
|
||||
r = ""
|
||||
|
||||
extra = ''
|
||||
if not event._reasons:
|
||||
if event._close_matches:
|
||||
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
|
||||
|
||||
if event._dependees:
|
||||
logger.error("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s", r, event._item, ", ".join(event._dependees), r, extra)
|
||||
logger.error("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)", r, event._item, ", ".join(event._dependees), r)
|
||||
else:
|
||||
logger.error("Nothing %sPROVIDES '%s'%s", r, event._item, extra)
|
||||
logger.error("Nothing %sPROVIDES '%s'", r, event._item)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
logger.error("%s", reason)
|
||||
@@ -495,15 +466,12 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
termfilter.clearFooter()
|
||||
if params.observe_only:
|
||||
print("\nKeyboard Interrupt, exiting observer...")
|
||||
main.shutdown = 2
|
||||
if not params.observe_only and main.shutdown == 1:
|
||||
if main.shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
logger.error("Unable to cleanly stop: %s" % error)
|
||||
if not params.observe_only and main.shutdown == 0:
|
||||
if main.shutdown == 0:
|
||||
print("\nKeyboard Interrupt, closing down...\n")
|
||||
interrupted = True
|
||||
_, error = server.runCommand(["stateShutdown"])
|
||||
@@ -521,7 +489,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||
if warnings:
|
||||
summary += pluralise("\nSummary: There was %s WARNING message shown.",
|
||||
"\nSummary: There were %s WARNING messages shown.", warnings)
|
||||
if return_value and errors:
|
||||
if return_value:
|
||||
summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.",
|
||||
"\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors)
|
||||
if summary:
|
||||
|
||||
@@ -196,7 +196,7 @@ class NCursesUI:
|
||||
# t.start()
|
||||
|
||||
#-------------------------------------------------------------------------#
|
||||
def main(self, stdscr, server, eventHandler, params):
|
||||
def main(self, stdscr, server, eventHandler):
|
||||
#-------------------------------------------------------------------------#
|
||||
height, width = stdscr.getmaxyx()
|
||||
|
||||
@@ -236,15 +236,13 @@ class NCursesUI:
|
||||
shutdown = 0
|
||||
|
||||
try:
|
||||
params.updateFromServer(server)
|
||||
cmdline = params.parseActions()
|
||||
cmdline, error = server.runCommand(["getCmdLineAction"])
|
||||
if not cmdline:
|
||||
print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
|
||||
return 1
|
||||
if 'msg' in cmdline and cmdline['msg']:
|
||||
logger.error(cmdline['msg'])
|
||||
return 1
|
||||
cmdline = cmdline['action']
|
||||
return
|
||||
elif error:
|
||||
print("Error getting bitbake commandline: %s" % error)
|
||||
return
|
||||
ret, error = server.runCommand(cmdline)
|
||||
if error:
|
||||
print("Error running command '%s': %s" % (cmdline, error))
|
||||
@@ -350,7 +348,7 @@ class NCursesUI:
|
||||
exitflag = True
|
||||
if shutdown == 1:
|
||||
mw.appendText("Second Keyboard Interrupt, stopping...\n")
|
||||
_, error = server.runCommand(["stateForceShutdown"])
|
||||
_, error = server.runCommand(["stateStop"])
|
||||
if error:
|
||||
print("Unable to cleanly stop: %s" % error)
|
||||
if shutdown == 0:
|
||||
|
||||
@@ -84,7 +84,6 @@ class BBUIEventQueue:
|
||||
|
||||
def startCallbackHandler(self):
|
||||
|
||||
self.server.timeout = 1
|
||||
while not self.server.quit:
|
||||
self.server.handle_request()
|
||||
self.server.server_close()
|
||||
|
||||
@@ -27,31 +27,17 @@ import bb
|
||||
import bb.msg
|
||||
import multiprocessing
|
||||
import fcntl
|
||||
import subprocess
|
||||
import glob
|
||||
import traceback
|
||||
import errno
|
||||
from commands import getstatusoutput
|
||||
from contextlib import contextmanager
|
||||
|
||||
logger = logging.getLogger("BitBake.Util")
|
||||
|
||||
def clean_context():
|
||||
return {
|
||||
"os": os,
|
||||
"bb": bb,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
def get_context():
|
||||
return _context
|
||||
|
||||
|
||||
def set_context(ctx):
|
||||
_context = ctx
|
||||
|
||||
# Context used in better_exec, eval
|
||||
_context = clean_context()
|
||||
_context = {
|
||||
"os": os,
|
||||
"bb": bb,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
def explode_version(s):
|
||||
r = []
|
||||
@@ -250,16 +236,14 @@ def _print_trace(body, line):
|
||||
"""
|
||||
Print the Environment of a Text Body
|
||||
"""
|
||||
error = []
|
||||
# print the environment of the method
|
||||
min_line = max(1, line-4)
|
||||
max_line = min(line + 4, len(body))
|
||||
for i in range(min_line, max_line + 1):
|
||||
for i in xrange(min_line, max_line + 1):
|
||||
if line == i:
|
||||
error.append(' *** %.4d:%s' % (i, body[i-1].rstrip()))
|
||||
logger.error(' *** %.4d:%s', i, body[i-1])
|
||||
else:
|
||||
error.append(' %.4d:%s' % (i, body[i-1].rstrip()))
|
||||
return error
|
||||
logger.error(' %.4d:%s', i, body[i-1])
|
||||
|
||||
def better_compile(text, file, realfile, mode = "exec"):
|
||||
"""
|
||||
@@ -269,78 +253,22 @@ def better_compile(text, file, realfile, mode = "exec"):
|
||||
try:
|
||||
return compile(text, file, mode)
|
||||
except Exception as e:
|
||||
error = []
|
||||
# split the text into lines again
|
||||
body = text.split('\n')
|
||||
error.append("Error in compiling python function in %s:\n" % realfile)
|
||||
logger.error("Error in compiling python function in %s", realfile)
|
||||
logger.error(str(e))
|
||||
if e.lineno:
|
||||
error.append("The code lines resulting in this error were:")
|
||||
error.extend(_print_trace(body, e.lineno))
|
||||
logger.error("The lines leading to this error were:")
|
||||
logger.error("\t%d:%s:'%s'", e.lineno, e.__class__.__name__, body[e.lineno-1])
|
||||
_print_trace(body, e.lineno)
|
||||
else:
|
||||
error.append("The function causing this error was:")
|
||||
logger.error("The function causing this error was:")
|
||||
for line in body:
|
||||
error.append(line)
|
||||
error.append("%s: %s" % (e.__class__.__name__, str(e)))
|
||||
|
||||
logger.error("\n".join(error))
|
||||
logger.error(line)
|
||||
|
||||
e = bb.BBHandledException(e)
|
||||
raise e
|
||||
|
||||
def _print_exception(t, value, tb, realfile, text, context):
|
||||
error = []
|
||||
try:
|
||||
exception = traceback.format_exception_only(t, value)
|
||||
error.append('Error executing a python function in %s:\n' % realfile)
|
||||
|
||||
# Strip 'us' from the stack (better_exec call)
|
||||
tb = tb.tb_next
|
||||
|
||||
textarray = text.split('\n')
|
||||
|
||||
linefailed = tb.tb_lineno
|
||||
|
||||
tbextract = traceback.extract_tb(tb)
|
||||
tbformat = traceback.format_list(tbextract)
|
||||
error.append("The stack trace of python calls that resulted in this exception/failure was:")
|
||||
error.append("File: '%s', lineno: %s, function: %s" % (tbextract[0][0], tbextract[0][1], tbextract[0][2]))
|
||||
error.extend(_print_trace(textarray, linefailed))
|
||||
|
||||
# See if this is a function we constructed and has calls back into other functions in
|
||||
# "text". If so, try and improve the context of the error by diving down the trace
|
||||
level = 0
|
||||
nexttb = tb.tb_next
|
||||
while nexttb is not None and (level+1) < len(tbextract):
|
||||
error.append("File: '%s', lineno: %s, function: %s" % (tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2]))
|
||||
if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
|
||||
# The code was possibly in the string we compiled ourselves
|
||||
error.extend(_print_trace(textarray, tbextract[level+1][1]))
|
||||
elif tbextract[level+1][0].startswith("/"):
|
||||
# The code looks like it might be in a file, try and load it
|
||||
try:
|
||||
with open(tbextract[level+1][0], "r") as f:
|
||||
text = f.readlines()
|
||||
error.extend(_print_trace(text, tbextract[level+1][1]))
|
||||
except:
|
||||
error.append(tbformat[level+1])
|
||||
elif "d" in context and tbextract[level+1][2]:
|
||||
# Try and find the code in the datastore based on the functionname
|
||||
d = context["d"]
|
||||
functionname = tbextract[level+1][2]
|
||||
text = d.getVar(functionname, True)
|
||||
if text:
|
||||
error.extend(_print_trace(text.split('\n'), tbextract[level+1][1]))
|
||||
else:
|
||||
error.append(tbformat[level+1])
|
||||
else:
|
||||
error.append(tbformat[level+1])
|
||||
nexttb = tb.tb_next
|
||||
level = level + 1
|
||||
|
||||
error.append("Exception: %s" % ''.join(exception))
|
||||
finally:
|
||||
logger.error("\n".join(error))
|
||||
|
||||
def better_exec(code, context, text = None, realfile = "<code>"):
|
||||
"""
|
||||
Similiar to better_compile, better_exec will
|
||||
@@ -353,25 +281,64 @@ def better_exec(code, context, text = None, realfile = "<code>"):
|
||||
if not hasattr(code, "co_filename"):
|
||||
code = better_compile(code, realfile, realfile)
|
||||
try:
|
||||
exec(code, get_context(), context)
|
||||
exec(code, _context, context)
|
||||
except Exception as e:
|
||||
(t, value, tb) = sys.exc_info()
|
||||
|
||||
if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
|
||||
raise
|
||||
try:
|
||||
_print_exception(t, value, tb, realfile, text, context)
|
||||
except Exception as e:
|
||||
logger.error("Exception handler error: %s" % str(e))
|
||||
|
||||
import traceback
|
||||
exception = traceback.format_exception_only(t, value)
|
||||
logger.error('Error executing a python function in %s:\n%s',
|
||||
realfile, ''.join(exception))
|
||||
|
||||
# Strip 'us' from the stack (better_exec call)
|
||||
tb = tb.tb_next
|
||||
|
||||
textarray = text.split('\n')
|
||||
linefailed = traceback.tb_lineno(tb)
|
||||
|
||||
tbextract = traceback.extract_tb(tb)
|
||||
tbformat = "\n".join(traceback.format_list(tbextract))
|
||||
logger.error("The stack trace of python calls that resulted in this exception/failure was:")
|
||||
for line in tbformat.split('\n'):
|
||||
logger.error(line)
|
||||
|
||||
logger.error("The code that was being executed was:")
|
||||
_print_trace(textarray, linefailed)
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[0][0], tbextract[0][1], tbextract[0][2])
|
||||
|
||||
# See if this is a function we constructed and has calls back into other functions in
|
||||
# "text". If so, try and improve the context of the error by diving down the trace
|
||||
level = 0
|
||||
nexttb = tb.tb_next
|
||||
while nexttb is not None and (level+1) < len(tbextract):
|
||||
if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
|
||||
_print_trace(textarray, tbextract[level+1][1])
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
|
||||
elif "d" in context and tbextract[level+1][2]:
|
||||
d = context["d"]
|
||||
functionname = tbextract[level+1][2]
|
||||
text = d.getVar(functionname, True)
|
||||
if text:
|
||||
_print_trace(text.split('\n'), tbextract[level+1][1])
|
||||
logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
nexttb = tb.tb_next
|
||||
level = level + 1
|
||||
|
||||
e = bb.BBHandledException(e)
|
||||
raise e
|
||||
|
||||
def simple_exec(code, context):
|
||||
exec(code, get_context(), context)
|
||||
exec(code, _context, context)
|
||||
|
||||
def better_eval(source, locals):
|
||||
return eval(source, get_context(), locals)
|
||||
return eval(source, _context, locals)
|
||||
|
||||
@contextmanager
|
||||
def fileslocked(files):
|
||||
@@ -427,10 +394,6 @@ def lockfile(name, shared=False, retry=True):
|
||||
return lf
|
||||
lf.close()
|
||||
except Exception:
|
||||
try:
|
||||
lf.close()
|
||||
except Exception:
|
||||
pass
|
||||
pass
|
||||
if not retry:
|
||||
return None
|
||||
@@ -460,9 +423,8 @@ def md5_file(filename):
|
||||
import md5
|
||||
m = md5.new()
|
||||
|
||||
with open(filename, "rb") as f:
|
||||
for line in f:
|
||||
m.update(line)
|
||||
for line in open(filename):
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
|
||||
def sha256_file(filename):
|
||||
@@ -478,9 +440,8 @@ def sha256_file(filename):
|
||||
return None
|
||||
|
||||
s = hashlib.sha256()
|
||||
with open(filename, "rb") as f:
|
||||
for line in f:
|
||||
s.update(line)
|
||||
for line in open(filename):
|
||||
s.update(line)
|
||||
return s.hexdigest()
|
||||
|
||||
def preserved_envvars_exported():
|
||||
@@ -532,8 +493,6 @@ def approved_variables():
|
||||
Determine and return the list of whitelisted variables which are approved
|
||||
to remain in the envrionment.
|
||||
"""
|
||||
if 'BB_PRESERVE_ENV' in os.environ:
|
||||
return os.environ.keys()
|
||||
approved = []
|
||||
if 'BB_ENV_WHITELIST' in os.environ:
|
||||
approved = os.environ['BB_ENV_WHITELIST'].split()
|
||||
@@ -580,9 +539,11 @@ def remove(path, recurse=False):
|
||||
if not path:
|
||||
return
|
||||
if recurse:
|
||||
import subprocess, glob
|
||||
# shutil.rmtree(name) would be ideal but its too slow
|
||||
subprocess.call(['rm', '-rf'] + glob.glob(path))
|
||||
return
|
||||
import os, errno, glob
|
||||
for name in glob.glob(path):
|
||||
try:
|
||||
os.unlink(name)
|
||||
@@ -862,8 +823,11 @@ def process_profilelog(fn):
|
||||
pout.close()
|
||||
|
||||
#
|
||||
# Was present to work around multiprocessing pool bugs in python < 2.7.3
|
||||
# Work around multiprocessing pool bugs in python < 2.7.3
|
||||
#
|
||||
def multiprocessingpool(*args, **kwargs):
|
||||
return multiprocessing.Pool(*args, **kwargs)
|
||||
if sys.version_info < (2, 7, 3):
|
||||
return bb.compat.Pool(*args, **kwargs)
|
||||
else:
|
||||
return multiprocessing.pool.Pool(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@ def init_logger(logfile, loglevel):
|
||||
FORMAT = '%(asctime)-15s %(message)s'
|
||||
logging.basicConfig(level=numeric_level, filename=logfile, format=FORMAT)
|
||||
|
||||
class NotFoundError(Exception):
|
||||
class NotFoundError(StandardError):
|
||||
pass
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
import os.path
|
||||
import errno
|
||||
import prserv
|
||||
import time
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
@@ -15,7 +14,7 @@ sqlversion = sqlite3.sqlite_version_info
|
||||
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
|
||||
raise Exception("sqlite3 version 3.3.0 or later is required.")
|
||||
|
||||
class PRTable(object):
|
||||
class PRTable():
|
||||
def __init__(self, conn, table, nohist):
|
||||
self.conn = conn
|
||||
self.nohist = nohist
|
||||
@@ -33,20 +32,16 @@ class PRTable(object):
|
||||
|
||||
def _execute(self, *query):
|
||||
"""Execute a query, waiting to acquire a lock if necessary"""
|
||||
start = time.time()
|
||||
end = start + 20
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
return self.conn.execute(*query)
|
||||
except sqlite3.OperationalError as exc:
|
||||
if 'is locked' in str(exc) and end > time.time():
|
||||
if 'database is locked' in str(exc) and count < 500:
|
||||
count = count + 1
|
||||
continue
|
||||
raise exc
|
||||
|
||||
def sync(self):
|
||||
self.conn.commit()
|
||||
self._execute("BEGIN EXCLUSIVE TRANSACTION")
|
||||
|
||||
def _getValueHist(self, version, pkgarch, checksum):
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
@@ -56,9 +51,11 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table,self.table),
|
||||
(version,pkgarch, checksum,version, pkgarch))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -82,9 +79,11 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table,self.table),
|
||||
(version, pkgarch, checksum, version, pkgarch))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
self.conn.rollback()
|
||||
@@ -113,8 +112,10 @@ class PRTable(object):
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum, value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -128,14 +129,18 @@ class PRTable(object):
|
||||
def _importNohist(self, version, pkgarch, checksum, value):
|
||||
try:
|
||||
#try to insert
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
self._execute("BEGIN")
|
||||
self._execute("INSERT OR ROLLBACK INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum,value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
#already have the record, try to update
|
||||
try:
|
||||
self._execute("BEGIN")
|
||||
self._execute("UPDATE %s SET value=? WHERE version=? AND pkgarch=? AND checksum=? AND value<?"
|
||||
% (self.table),
|
||||
(value,version,pkgarch,checksum,value))
|
||||
self.conn.commit()
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
@@ -218,7 +223,7 @@ class PRData(object):
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise e
|
||||
self.connection=sqlite3.connect(self.filename, isolation_level="EXCLUSIVE", check_same_thread = False)
|
||||
self.connection=sqlite3.connect(self.filename, isolation_level="DEFERRED")
|
||||
self.connection.row_factory=sqlite3.Row
|
||||
self._tables={}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import os,sys,logging
|
||||
import signal, time, atexit, threading
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
import xmlrpclib
|
||||
import threading
|
||||
import Queue
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
@@ -33,17 +31,19 @@ class Handler(SimpleXMLRPCRequestHandler):
|
||||
PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
|
||||
singleton = None
|
||||
|
||||
|
||||
class PRServer(SimpleXMLRPCServer):
|
||||
def __init__(self, dbfile, logfile, interface, daemon=True):
|
||||
''' constructor '''
|
||||
SimpleXMLRPCServer.__init__(self, interface,
|
||||
requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=False, allow_none=True)
|
||||
self.dbfile=dbfile
|
||||
self.daemon=daemon
|
||||
self.logfile=logfile
|
||||
self.working_thread=None
|
||||
self.host, self.port = self.socket.getsockname()
|
||||
self.db=prserv.db.PRData(dbfile)
|
||||
self.table=self.db["PRMAIN"]
|
||||
self.pidfile=PIDPREFIX % (self.host, self.port)
|
||||
|
||||
self.register_function(self.getPR, "getPR")
|
||||
@@ -53,32 +53,6 @@ class PRServer(SimpleXMLRPCServer):
|
||||
self.register_function(self.importone, "importone")
|
||||
self.register_introspection_functions()
|
||||
|
||||
self.db = prserv.db.PRData(self.dbfile)
|
||||
self.table = self.db["PRMAIN"]
|
||||
|
||||
self.requestqueue = Queue.Queue()
|
||||
self.handlerthread = threading.Thread(target = self.process_request_thread)
|
||||
self.handlerthread.daemon = False
|
||||
|
||||
def process_request_thread(self):
|
||||
"""Same as in BaseServer but as a thread.
|
||||
|
||||
In addition, exception handling is done here.
|
||||
|
||||
"""
|
||||
while True:
|
||||
(request, client_address) = self.requestqueue.get()
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
except:
|
||||
self.handle_error(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
self.table.sync()
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
self.requestqueue.put((request, client_address))
|
||||
|
||||
def export(self, version=None, pkgarch=None, checksum=None, colinfo=True):
|
||||
try:
|
||||
return self.table.export(version, pkgarch, checksum, colinfo)
|
||||
@@ -116,11 +90,9 @@ class PRServer(SimpleXMLRPCServer):
|
||||
logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
|
||||
(self.dbfile, self.host, self.port, str(os.getpid())))
|
||||
|
||||
self.handlerthread.start()
|
||||
while not self.quit:
|
||||
self.handle_request()
|
||||
|
||||
self.table.sync()
|
||||
logger.info("PRServer: stopping...")
|
||||
self.server_close()
|
||||
return
|
||||
@@ -171,11 +143,6 @@ class PRServer(SimpleXMLRPCServer):
|
||||
os.dup2(so.fileno(),sys.stdout.fileno())
|
||||
os.dup2(se.fileno(),sys.stderr.fileno())
|
||||
|
||||
# Clear out all log handlers prior to the fork() to avoid calling
|
||||
# event handlers not part of the PRserver
|
||||
for logger_iter in logging.Logger.manager.loggerDict.keys():
|
||||
logging.getLogger(logger_iter).handlers = []
|
||||
|
||||
# Ensure logging makes it to the logfile
|
||||
streamhandler = logging.StreamHandler()
|
||||
streamhandler.setLevel(logging.DEBUG)
|
||||
@@ -190,10 +157,10 @@ class PRServer(SimpleXMLRPCServer):
|
||||
pf.close()
|
||||
|
||||
self.work_forever()
|
||||
self.delpid()
|
||||
self.delpid
|
||||
os._exit(0)
|
||||
|
||||
class PRServSingleton(object):
|
||||
class PRServSingleton():
|
||||
def __init__(self, dbfile, logfile, interface):
|
||||
self.dbfile = dbfile
|
||||
self.logfile = logfile
|
||||
@@ -205,19 +172,23 @@ class PRServSingleton(object):
|
||||
self.prserv = PRServer(self.dbfile, self.logfile, self.interface)
|
||||
self.prserv.start()
|
||||
self.host, self.port = self.prserv.getinfo()
|
||||
del self.prserv.db
|
||||
|
||||
def getinfo(self):
|
||||
return (self.host, self.port)
|
||||
|
||||
class PRServerConnection(object):
|
||||
class PRServerConnection():
|
||||
def __init__(self, host, port):
|
||||
if is_local_special(host, port):
|
||||
host, port = singleton.getinfo()
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.connection, self.transport = bb.server.xmlrpc._create_server(self.host, self.port)
|
||||
self.connection = bb.server.xmlrpc._create_server(self.host, self.port)
|
||||
|
||||
def terminate(self):
|
||||
# Don't wait for server indefinitely
|
||||
import socket
|
||||
socket.setdefaulttimeout(2)
|
||||
try:
|
||||
logger.info("Terminating PRServer...")
|
||||
self.connection.quit()
|
||||
@@ -236,9 +207,6 @@ class PRServerConnection(object):
|
||||
def importone(self, version, pkgarch, checksum, value):
|
||||
return self.connection.importone(version, pkgarch, checksum, value)
|
||||
|
||||
def getinfo(self):
|
||||
return self.host, self.port
|
||||
|
||||
def start_daemon(dbfile, host, port, logfile):
|
||||
pidfile = PIDPREFIX % (host, port)
|
||||
try:
|
||||
@@ -303,7 +271,7 @@ def auto_start(d):
|
||||
|
||||
host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
|
||||
if not host_params:
|
||||
return None
|
||||
return True
|
||||
|
||||
if len(host_params) != 2:
|
||||
logger.critical('\n'.join(['PRSERV_HOST: incorrect format',
|
||||
@@ -328,11 +296,7 @@ def auto_start(d):
|
||||
port = int(host_params[1])
|
||||
|
||||
try:
|
||||
connection = PRServerConnection(host,port)
|
||||
connection.ping()
|
||||
realhost, realport = connection.getinfo()
|
||||
return str(realhost) + ":" + str(realport)
|
||||
|
||||
return PRServerConnection(host,port).ping()
|
||||
except Exception:
|
||||
logger.critical("PRservice %s:%d not available" % (host, port))
|
||||
raise PRServiceConfigError
|
||||
|
||||
@@ -196,11 +196,7 @@ TARFILES = mega-manual.html mega-style.css figures/yocto-environment.png figures
|
||||
figures/perf-wget-g-copy-to-user-expanded-stripped-unresolved-hidden.png figures/pybootchartgui-linux-yocto.png \
|
||||
figures/pychart-linux-yocto-rpm.png figures/pychart-linux-yocto-rpm-nostrip.png \
|
||||
figures/sched-wakeup-profile.png figures/sysprof-callers.png \
|
||||
figures/sysprof-copy-from-user.png figures/sysprof-copy-to-user.png figures/cross-development-toolchains.png \
|
||||
figures/yocto-environment-ref.png figures/user-configuration.png figures/source-input.png \
|
||||
figures/package-feeds.png figures/layer-input.png figures/images.png figures/sdk.png \
|
||||
figures/source-fetching.png figures/patching.png figures/configuration-compile-autoreconf.png \
|
||||
figures/analysis-for-package-splitting.png
|
||||
figures/sysprof-copy-from-user.png figures/sysprof-copy-to-user.png
|
||||
endif
|
||||
|
||||
MANUALS = $(DOC)/$(DOC).html
|
||||
@@ -213,13 +209,7 @@ ifeq ($(DOC),ref-manual)
|
||||
XSLTOPTS = --xinclude
|
||||
ALLPREQ = html pdf eclipse tarball
|
||||
TARFILES = ref-manual.html ref-style.css figures/poky-title.png \
|
||||
figures/buildhistory.png figures/buildhistory-web.png eclipse \
|
||||
figures/cross-development-toolchains.png figures/layer-input.png \
|
||||
figures/package-feeds.png figures/source-input.png \
|
||||
figures/user-configuration.png figures/yocto-environment-ref.png \
|
||||
figures/images.png figures/sdk.png figures/source-fetching.png \
|
||||
figures/patching.png figures/configuration-compile-autoreconf.png \
|
||||
figures/analysis-for-package-splitting.png
|
||||
figures/buildhistory.png figures/buildhistory-web.png eclipse
|
||||
MANUALS = $(DOC)/$(DOC).html $(DOC)/$(DOC).pdf $(DOC)/eclipse
|
||||
FIGURES = figures
|
||||
STYLESHEET = $(DOC)/*.css
|
||||
|
||||
@@ -2,60 +2,60 @@ documentation
|
||||
=============
|
||||
|
||||
This is the directory that contains the Yocto Project documentation. The Yocto
|
||||
Project source repositories at http://git.yoctoproject.org/cgit.cgi have two
|
||||
Project source repositories at http://git.yoctoproject.org/cgit.cgi have two
|
||||
instances of the "documentation" directory. You should understand each of
|
||||
these instances.
|
||||
|
||||
poky/documentation - The directory within the poky Git repository containing
|
||||
the set of Yocto Project manuals. When you clone the
|
||||
poky Git repository, the documentation directory
|
||||
contains the manuals. The state of the manuals in this
|
||||
directory is guaranteed to reflect the latest Yocto
|
||||
Project release. The manuals at the tip of this
|
||||
directory will also likely contain most manual
|
||||
the set of Yocto Project manuals. When you clone the
|
||||
poky Git repository, the documentation directory
|
||||
contains the manuals. The state of the manuals in this
|
||||
directory is guaranteed to reflect the latest Yocto
|
||||
Project release. The manuals at the tip of this
|
||||
directory will also likely contain most manual
|
||||
development changes.
|
||||
|
||||
yocto-docs/documentation - The Git repository for the Yocto Project manuals.
|
||||
This repository is where manual development
|
||||
occurs. If you plan on contributing back to the
|
||||
Yocto Project documentation, you should set up
|
||||
a local Git repository based on this upstream
|
||||
yocto-docs/documentation - The Git repository for the Yocto Project manuals.
|
||||
This repository is where manual development
|
||||
occurs. If you plan on contributing back to the
|
||||
Yocto Project documentation, you should set up
|
||||
a local Git repository based on this upstream
|
||||
repository as follows:
|
||||
|
||||
git clone git://git.yoctoproject.org/yocto-docs
|
||||
|
||||
Changes and patches are first pushed to the
|
||||
yocto-docs Git repository. Later, they make it
|
||||
into the poky Git repository found at
|
||||
git://git.yoctoproject.org/poky.
|
||||
Changes and patches are first pushed to the
|
||||
yocto-docs Git repository. Later, they make it
|
||||
into the poky Git repository found at
|
||||
git://git.yoctoproject.org/poky.
|
||||
|
||||
Manual Organization
|
||||
===================
|
||||
|
||||
Folders exist for individual manuals as follows:
|
||||
|
||||
* adt-manual - The Yocto Project Application Developer's Guide.
|
||||
* bsp-guide - The Yocto Project Board Support Package (BSP) Developer's Guide
|
||||
* adt-manual - The Yocto Project Application Development Toolkit (ADT)
|
||||
User's Guide.
|
||||
* bsp-guide - The Yocto Project Board Support (BSP) Developer's Guide
|
||||
* dev-manual - The Yocto Project Development Manual
|
||||
* kernel-dev - The Yocto Project Linux Kernel Development Manual
|
||||
* ref-manual - The Yocto Project Reference Manual
|
||||
* kernel-manual - The Yocto Project Kernel Architecture and Use Manual
|
||||
* poky-ref-manual - The Yocto Project Reference Manual
|
||||
* yocto-project-qs - The Yocto Project Quick Start
|
||||
* mega-manual - An aggregated manual comprised of all YP manuals and guides
|
||||
* profile-manual - The Yocto Project Profile and Tracing Manual
|
||||
* mega-manual - The aggregated manual comprised of all YP manuals and guides
|
||||
|
||||
Each folder is self-contained regarding content and figures. Note that there
|
||||
is a sed file needed to process the links of the mega-manual. The sed file
|
||||
is located in the tools directory. Also note that the figures folder in the
|
||||
is located in the tools directory. Also note that the figures folder in the
|
||||
mega-manual directory contains duplicates of all the figures in the YP folders
|
||||
directories for all YP manuals and guides.
|
||||
|
||||
If you want to find HTML versions of the Yocto Project manuals on the web,
|
||||
go to http://www.yoctoproject.org and click on the "Documentation" tab. From
|
||||
If you want to find HTML versions of the Yocto Project manuals on the web,
|
||||
go to http://www.yoctoproject.org and click on the "Documentation" tab. From
|
||||
there you have access to archived documentation from previous releases, current
|
||||
documentation for the latest release, and "Docs in Progress" for the release
|
||||
documentation for the latest release, and "Docs in Progress" for the release
|
||||
currently being developed.
|
||||
|
||||
In general, the Yocto Project site (http://www.yoctoproject.org) is a great
|
||||
In general, the Yocto Project site (http://www.yoctoproject.org) is a great
|
||||
reference for both information and downloads.
|
||||
|
||||
Makefile
|
||||
@@ -66,8 +66,8 @@ tarballs, etc. Details on how the Makefile work are documented
|
||||
inside the Makefile. See that file for more information.
|
||||
|
||||
To build a manual, you run the make command and pass it the name
|
||||
of the folder containing the manual's contents.
|
||||
For example, the following command run from the documentation directory
|
||||
of the folder containing the manual's contents.
|
||||
For example, the following command run from the documentation directory
|
||||
creates an HTML and a PDF version of the ADT manual.
|
||||
The DOC variable specifies the manual you are making:
|
||||
|
||||
@@ -86,6 +86,9 @@ Contains various templates, fonts, and some old PNG files.
|
||||
tools
|
||||
=====
|
||||
Contains a tool to convert the DocBook files to PDF format. This folder also
|
||||
contains the mega-manual.sed file, which is used by Makefile to process
|
||||
cross-references from within the manual that normally go to an external
|
||||
contains the mega-manual.sed file, which is used by Makefile to process
|
||||
cross-references from within the manual that normally go to an external
|
||||
manual.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -62,11 +62,31 @@
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
<revnumber>1.4.1</revnumber>
|
||||
<date>June 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.1 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
<revision>
|
||||
<revnumber>1.4.2</revnumber>
|
||||
<date>August 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.3</revnumber>
|
||||
<date>March 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.4</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.5</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.5 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
<year>©RIGHT_YEAR;</year>
|
||||
|
||||
@@ -91,10 +91,8 @@
|
||||
<para>
|
||||
If you use BitBake to generate the ADT Installer tarball, you must
|
||||
<filename>source</filename> the environment setup script
|
||||
(<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>)
|
||||
located in the Source Directory before running the
|
||||
(<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>)
|
||||
located in the Source Directory before running the
|
||||
BitBake command that creates the tarball.
|
||||
</para>
|
||||
|
||||
@@ -231,11 +229,9 @@
|
||||
<title>Using a Cross-Toolchain Tarball</title>
|
||||
|
||||
<para>
|
||||
If you want to simply install a cross-toolchain by hand, you can
|
||||
If you want to simply install the cross-toolchain by hand, you can
|
||||
do so by running the toolchain installer.
|
||||
The installer includes the pre-built cross-toolchain, the
|
||||
<filename>runqemu</filename> script, and support files.
|
||||
If you use this method to install the cross-toolchain, you
|
||||
If you use this method to install the cross-toolchain and you
|
||||
might still need to install the target sysroot by installing and
|
||||
extracting it separately.
|
||||
For information on how to install the sysroot, see the
|
||||
@@ -245,61 +241,80 @@
|
||||
<para>
|
||||
Follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Get your toolchain installer using one of the
|
||||
following methods:
|
||||
<itemizedlist>
|
||||
<listitem><para>Go to
|
||||
<ulink url='&YOCTO_TOOLCHAIN_DL_URL;'></ulink>
|
||||
and find the folder that matches your host
|
||||
development system (i.e. <filename>i686</filename>
|
||||
for 32-bit machines or <filename>x86_64</filename>
|
||||
for 64-bit machines).</para>
|
||||
<para>Go into that folder and download the toolchain
|
||||
installer whose name includes the appropriate target
|
||||
architecture.
|
||||
The toolchains provided by the Yocto Project
|
||||
are based off of the
|
||||
<filename>core-image-sato</filename> image and
|
||||
contain libraries appropriate for developing
|
||||
against that image.
|
||||
For example, if your host development system is a
|
||||
64-bit x86 system and you are going to use
|
||||
your cross-toolchain for a 32-bit x86
|
||||
target, go into the <filename>x86_64</filename>
|
||||
folder and download the following installer:
|
||||
<literallayout class='monospaced'>
|
||||
poky-eglibc-x86_64-core-image-sato-i586.sh
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para>Build your own toolchain installer.
|
||||
For cases where you cannot use an installer
|
||||
from the download area, you can build your own as
|
||||
described in the
|
||||
"<link linkend='optionally-building-a-toolchain-installer'>Optionally Building a Toolchain Installer</link>"
|
||||
section.</para></listitem>
|
||||
</itemizedlist></para></listitem>
|
||||
<listitem><para>Once you have the installer, run it to install
|
||||
the toolchain.
|
||||
<listitem><para>Go to
|
||||
<ulink url='&YOCTO_TOOLCHAIN_DL_URL;'></ulink>
|
||||
and find the folder that matches your host development system
|
||||
(i.e. <filename>i686</filename> for 32-bit machines or
|
||||
<filename>x86-64</filename> for 64-bit machines).</para></listitem>
|
||||
<listitem><para>Go into that folder and download the toolchain installer whose name
|
||||
includes the appropriate target architecture.
|
||||
For example, if your host development system is an Intel-based 64-bit system and
|
||||
you are going to use your cross-toolchain for an Intel-based 32-bit target, go into the
|
||||
<filename>x86_64</filename> folder and download the following installer:
|
||||
<literallayout class='monospaced'>
|
||||
poky-eglibc-x86_64-i586-toolchain-gmae-&DISTRO;.sh
|
||||
</literallayout>
|
||||
<note><para>As an alternative to steps one and two, you can
|
||||
build the toolchain installer if you have a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
If you need GMAE, you should use the
|
||||
<filename>bitbake meta-toolchain-gmae</filename>
|
||||
command.
|
||||
Running the resulting installation script will support
|
||||
such development.
|
||||
If you are not concerned with GMAE, you can generate
|
||||
the toolchain installer using
|
||||
<filename>bitbake meta-toolchain</filename>.
|
||||
Either of these methods requires you to still
|
||||
install the target sysroot by installing and
|
||||
extracting it separately.
|
||||
For information on how to install the sysroot, see the
|
||||
"<link linkend='extracting-the-root-filesystem'>Extracting the Root Filesystem</link>" section.
|
||||
</para>
|
||||
<para>A final method of building the toolchain installer
|
||||
exists that has significant advantages over the previous
|
||||
two methods.
|
||||
This method results in a toolchain installer that
|
||||
contains the sysroot that matches your target root
|
||||
filesystem.
|
||||
To build this installer, use the
|
||||
<filename>bitbake image -c populate_sdk</filename>
|
||||
command.</para>
|
||||
<para>Remember, before using any
|
||||
<filename>bitbake</filename> command, you must source
|
||||
the <filename>&OE_INIT_PATH;</filename> script
|
||||
located in the Source Directory and you must make sure
|
||||
your <filename>conf/local.conf</filename> variables are
|
||||
correct.
|
||||
In particular, you need to be sure the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
variable matches the architecture for which you are
|
||||
building and that the <filename>SDKMACHINE</filename>
|
||||
variable is correctly set if you are building
|
||||
a toolchain for an architecture that differs from your
|
||||
current development host machine.</para>
|
||||
<para>When the BitBake command
|
||||
completes, the toolchain installer will be in
|
||||
<filename>tmp/deploy/sdk</filename> in the Build
|
||||
Directory.</para>
|
||||
</note></para></listitem>
|
||||
<listitem><para>Once you have the installer, run it to install the toolchain.
|
||||
You must change the permissions on the toolchain installer
|
||||
script so that it is executable.</para>
|
||||
<para>The following command shows how to run the installer
|
||||
given a toolchain tarball for a 64-bit x86 development host
|
||||
system and a 32-bit x86 target architecture.
|
||||
The example assumes the toolchain installer is located
|
||||
in <filename>~/Downloads/</filename>.
|
||||
<para>The following command shows how to run the installer given a toolchain tarball
|
||||
for a 64-bit development host system and a 32-bit target architecture.
|
||||
The example assumes the toolchain installer is located in <filename>~/Downloads/</filename>.
|
||||
<literallayout class='monospaced'>
|
||||
$ ~/Downloads/poky-eglibc-x86_64-core-image-sato-i586.sh
|
||||
$ ~/Downloads/poky-eglibc-x86_64-i586-toolchain-gmae-&DISTRO;.sh
|
||||
</literallayout>
|
||||
<note>
|
||||
If you do not have write permissions for the directory
|
||||
into which you are installing the toolchain, the
|
||||
toolchain installer notifies you and exits.
|
||||
Be sure you have write permissions in the directory and
|
||||
run the installer again.
|
||||
If you do not have write permissions for the directory into which you are installing
|
||||
the toolchain, the toolchain installer notifies you and exits.
|
||||
Be sure you have write permissions in the directory and run the installer again.
|
||||
</note>
|
||||
Once the tarball is expanded, the cross-toolchain is
|
||||
installed.
|
||||
You will notice environment setup files for the
|
||||
cross-toolchain in the directory.</para></listitem>
|
||||
Once the tarball is expanded, the cross-toolchain is installed.
|
||||
You will notice environment setup files for the cross-toolchain in the directory.
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
@@ -522,58 +537,6 @@
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='optionally-building-a-toolchain-installer'>
|
||||
<title>Optionally Building a Toolchain Installer</title>
|
||||
|
||||
<para>
|
||||
As an alternative to locating and downloading a toolchain installer,
|
||||
you can build the toolchain installer if you have a
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#build-directory'>Build Directory</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can build the toolchain
|
||||
installer using <filename>bitbake meta-toolchain</filename>.
|
||||
This method requires you to still install the target
|
||||
sysroot by installing and extracting it separately.
|
||||
For information on how to install the sysroot, see the
|
||||
"<link linkend='extracting-the-root-filesystem'>Extracting the Root Filesystem</link>" section.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A final method of building the toolchain installer exists that has
|
||||
significant advantages over the previous two methods.
|
||||
This method results in a toolchain installer that contains the sysroot
|
||||
that matches your target root filesystem.
|
||||
To build this installer, use the
|
||||
<filename>bitbake image -c populate_sdk</filename> command.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Remember, before using any <filename>bitbake</filename> command, you
|
||||
must source the build environment setup script
|
||||
(i.e.
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>)
|
||||
located in the Source Directory and you must make sure your
|
||||
<filename>conf/local.conf</filename> variables are correct.
|
||||
In particular, you need to be sure the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
variable matches the architecture for which you are building and that
|
||||
the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-SDKMACHINE'><filename>SDKMACHINE</filename></ulink>
|
||||
variable is correctly set if you are building a toolchain designed to
|
||||
run on an architecture that differs from your current development host
|
||||
machine (i.e. the build machine).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When the BitBake command completes, the toolchain installer will be in
|
||||
<filename>tmp/deploy/sdk</filename> in the Build Directory.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
|
||||
@@ -74,9 +74,29 @@
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
<revnumber>1.4.1</revnumber>
|
||||
<date>June 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.2</revnumber>
|
||||
<date>August 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.3</revnumber>
|
||||
<date>March 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.4</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.5</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.5 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
|
||||
@@ -34,13 +34,11 @@
|
||||
The BSP consists of a file structure inside a base directory.
|
||||
Collectively, you can think of the base directory and the file structure
|
||||
as a BSP Layer.
|
||||
Although not a strict requirement, layers in the Yocto Project use the
|
||||
following well established naming convention:
|
||||
BSP Layers use the following naming convention:
|
||||
<literallayout class='monospaced'>
|
||||
meta-<bsp_name>
|
||||
</literallayout>
|
||||
The string "meta-" is prepended to the machine or platform name, which is
|
||||
"bsp_name" in the above form.
|
||||
"bsp_name" is a placeholder for the machine or platform name.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -54,7 +52,7 @@
|
||||
definition and from it build an image.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
BBLAYERS ?= " \
|
||||
BBLAYERS = ?" \
|
||||
/usr/local/src/yocto/meta \
|
||||
/usr/local/src/yocto/meta-yocto \
|
||||
/usr/local/src/yocto/meta-yocto-bsp \
|
||||
@@ -261,7 +259,7 @@
|
||||
This file provides information on where to locate the BSP source files.
|
||||
For example, information provides where to find the sources that comprise
|
||||
the images shipped with the BSP.
|
||||
Information is also included to help you find the
|
||||
Information is also included to help you find the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#metadata'>Metadata</ulink>
|
||||
used to generate the images that ship with the BSP.
|
||||
</para>
|
||||
@@ -320,9 +318,9 @@
|
||||
# We have a conf and classes directory, add to BBPATH
|
||||
BBPATH .= ":${LAYERDIR}"
|
||||
|
||||
# We have a recipes directory, add to BBFILES
|
||||
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
|
||||
${LAYERDIR}/recipes-*/*/*.bbappend"
|
||||
# We have recipes-* directories, add to BBFILES
|
||||
BBFILES += "${LAYERDIR}/recipes-*/*.bb \
|
||||
${LAYERDIR}/recipes-*/*.bbappend"
|
||||
|
||||
BBFILE_COLLECTIONS += "bsp"
|
||||
BBFILE_PATTERN_bsp = "^${LAYERDIR}/"
|
||||
@@ -331,8 +329,8 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To illustrate the string substitutions, here are the corresponding statements
|
||||
from the Crown Bay <filename>conf/layer.conf</filename> file:
|
||||
To illustrate the string substitutions, here are the last three statements from the Crown
|
||||
Bay <filename>conf/layer.conf</filename> file:
|
||||
<literallayout class='monospaced'>
|
||||
BBFILE_COLLECTIONS += "crownbay"
|
||||
BBFILE_PATTERN_crownbay = "^${LAYERDIR}/"
|
||||
@@ -382,8 +380,8 @@
|
||||
The <filename>crownbay.conf</filename> file is used for the Crown Bay BSP
|
||||
that supports the <trademark class='registered'>Intel</trademark> Embedded
|
||||
Media and Graphics Driver (<trademark class='registered'>Intel</trademark>
|
||||
EMGD), while the <filename>crownbay-noemgd</filename> file is used for the
|
||||
Crown Bay BSP that supports Video Electronics Standards Association (VESA)
|
||||
EMGD), while the <filename>crownbay-noemgd</filename> file is used for the
|
||||
Crown Bay BSP that supports Video Electronics Standards Association (VESA)
|
||||
graphics only.
|
||||
</para>
|
||||
|
||||
@@ -399,8 +397,9 @@
|
||||
Tuning files are found in the <filename>meta/conf/machine/include</filename>
|
||||
directory within the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>.
|
||||
Tuning files can also reside in the BSP Layer itself.
|
||||
For example, the <filename>ia32-base.inc</filename> file resides in the
|
||||
<filename>meta/conf/machine/include</filename> directory.
|
||||
<filename>meta-intel</filename> BSP Layer in <filename>conf/machine/include</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -410,13 +409,12 @@
|
||||
<literallayout class='monospaced'>
|
||||
require conf/machine/include/tune-atom.inc
|
||||
require conf/machine/include/ia32-base.inc
|
||||
require conf/machine/include/meta-intel.inc
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='bsp-filelayout-misc-recipes'>
|
||||
<title>Miscellaneous BSP-Specific Recipe Files</title>
|
||||
<title>Miscellaneous Recipe Files</title>
|
||||
<para>
|
||||
You can find these files in the BSP Layer at:
|
||||
<literallayout class='monospaced'>
|
||||
@@ -502,28 +500,28 @@
|
||||
the <filename>meta-<bsp_name>/recipes-kernel/linux</filename> directory).
|
||||
</para>
|
||||
<para>
|
||||
Suppose you are using the <filename>linux-yocto_3.8.bb</filename> recipe to build
|
||||
Suppose you are using the <filename>linux-yocto_3.4.bb</filename> recipe to build
|
||||
the kernel.
|
||||
In other words, you have selected the kernel in your
|
||||
<filename><bsp_name>.conf</filename> file by adding these types
|
||||
of statements:
|
||||
<literallayout class='monospaced'>
|
||||
PREFERRED_PROVIDER_virtual/kernel ?= "linux-yocto"
|
||||
PREFERRED_VERSION_linux-yocto ?= "3.8%"
|
||||
PREFERRED_VERSION_linux-yocto = "3.4%"
|
||||
</literallayout>
|
||||
<note>
|
||||
When the preferred provider is assumed by default, the
|
||||
<filename>PREFERRED_PROVIDER</filename> statement does not appear in the
|
||||
<filename><bsp_name>.conf</filename> file.
|
||||
</note>
|
||||
You would use the <filename>linux-yocto_3.8.bbappend</filename> file to append
|
||||
You would use the <filename>linux-yocto_3.4.bbappend</filename> file to append
|
||||
specific BSP settings to the kernel, thus configuring the kernel for your particular BSP.
|
||||
</para>
|
||||
<para>
|
||||
As an example, look at the existing Crown Bay BSP.
|
||||
The append file used is:
|
||||
<literallayout class='monospaced'>
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.8.bbappend
|
||||
meta-crownbay/recipes-kernel/linux/linux-yocto_3.4.bbappend
|
||||
</literallayout>
|
||||
The following listing shows the file.
|
||||
Be aware that the actual commit ID strings in this example listing might be different
|
||||
@@ -533,43 +531,40 @@
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay = "crownbay"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
KERNEL_FEATURES_crownbay_append = " features/drm-emgd/drm-emgd-1.16 cfg/vesafb"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay-noemgd = "crownbay-noemgd"
|
||||
KMACHINE_crownbay-noemgd = "crownbay"
|
||||
KBRANCH_crownbay-noemgd = "standard/crownbay"
|
||||
KERNEL_FEATURES_crownbay-noemgd_append = " cfg/vesafb"
|
||||
KMACHINE_crownbay-noemgd = "crownbay"
|
||||
KBRANCH_crownbay-noemgd = "standard/crownbay"
|
||||
|
||||
LINUX_VERSION = "3.8.4"
|
||||
SRCREV_machine_pn-linux-yocto_crownbay ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
SRCREV_emgd_pn-linux-yocto_crownbay ?= "86643bdd8cbad616a161ab91f51108cf0da827bc"
|
||||
|
||||
SRCREV_meta_crownbay = "2a6d36e75ca0a121570a389d7bab76ec240cbfda"
|
||||
SRCREV_machine_crownbay = "47aed0c17c1c55988198ad39f86ae88894c8e0a4"
|
||||
SRCREV_emgd_crownbay = "c780732f175ff0ec866fac2130175876b519b576"
|
||||
SRCREV_machine_pn-linux-yocto_crownbay-noemgd ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay-noemgd ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
|
||||
SRCREV_meta_crownbay-noemgd = "2a6d36e75ca0a121570a389d7bab76ec240cbfda"
|
||||
SRCREV_machine_crownbay-noemgd = "47aed0c17c1c55988198ad39f86ae88894c8e0a4"
|
||||
|
||||
SRC_URI_crownbay = "git://git.yoctoproject.org/linux-yocto-3.8.git;protocol=git;nocheckout=1;branch=${KBRANCH},${KMETA},emgd-1.16;name=machine,meta,emgd"
|
||||
KSRC_linux_yocto_3_4 ?= "git.yoctoproject.org/linux-yocto-3.4.git"
|
||||
SRC_URI_crownbay = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;nocheckout=1;branch=${KBRANCH},meta,emgd-1.14;name=machine,meta,emgd"
|
||||
SRC_URI_crownbay-noemgd = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;nocheckout=1;branch=${KBRANCH},meta;name=machine,meta"
|
||||
</literallayout>
|
||||
This append file contains statements used to support the Crown Bay BSP for both
|
||||
<trademark class='registered'>Intel</trademark> EMGD and the VESA graphics.
|
||||
The build process, in this case, recognizes and uses only the statements that
|
||||
apply to the defined machine name - <filename>crownbay</filename> in this case.
|
||||
So, the applicable statements in the <filename>linux-yocto_3.8.bbappend</filename>
|
||||
So, the applicable statements in the <filename>linux-yocto_3.4.bbappend</filename>
|
||||
file are follows:
|
||||
<literallayout class='monospaced'>
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
|
||||
|
||||
COMPATIBLE_MACHINE_crownbay = "crownbay"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
KERNEL_FEATURES_crownbay_append = " features/drm-emgd/drm-emgd-1.16 cfg/vesafb"
|
||||
KMACHINE_crownbay = "crownbay"
|
||||
KBRANCH_crownbay = "standard/crownbay"
|
||||
|
||||
SRCREV_meta_crownbay = "2a6d36e75ca0a121570a389d7bab76ec240cbfda"
|
||||
SRCREV_machine_crownbay = "47aed0c17c1c55988198ad39f86ae88894c8e0a4"
|
||||
SRCREV_emgd_crownbay = "c780732f175ff0ec866fac2130175876b519b576"
|
||||
SRCREV_machine_pn-linux-yocto_crownbay ?= "449f7f520350700858f21a5554b81cc8ad23267d"
|
||||
SRCREV_meta_pn-linux-yocto_crownbay ?= "9e3bdb7344054264b750e53fbbb6394cc1c942ac"
|
||||
SRCREV_emgd_pn-linux-yocto_crownbay ?= "86643bdd8cbad616a161ab91f51108cf0da827bc"
|
||||
</literallayout>
|
||||
The append file defines <filename>crownbay</filename> as the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-COMPATIBLE_MACHINE'><filename>COMPATIBLE_MACHINE</filename></ulink>
|
||||
@@ -579,11 +574,8 @@
|
||||
machine name used by the Linux Yocto kernel.
|
||||
The file also uses the optional
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KBRANCH'><filename>KBRANCH</filename></ulink> variable
|
||||
to ensure the build process uses the <filename>standard/crownbay</filename>
|
||||
to ensure the build process uses the <filename>standard/default/crownbay</filename>
|
||||
kernel branch.
|
||||
The
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_FEATURES'><filename>KERNEL_FEATURES</filename></ulink>
|
||||
variable enables features specific to the kernel.
|
||||
Finally, the append file points to specific commits in the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink> Git
|
||||
repository and the <filename>meta</filename> Git repository branches to identify the
|
||||
@@ -609,7 +601,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For example, suppose you had some configuration options in a file called
|
||||
For example, suppose you had a some configuration options in a file called
|
||||
<filename>network_configs.cfg</filename>.
|
||||
You can place that file inside a directory named <filename>/linux-yocto</filename> and then add
|
||||
a <filename>SRC_URI</filename> statement such as the following to the append file.
|
||||
@@ -731,7 +723,7 @@
|
||||
as outlined in <filename>recipes.txt</filename>.
|
||||
If you cannot find a category in <filename>recipes.txt</filename>
|
||||
to fit a particular recipe, you can make up your own
|
||||
<filename>recipes-*</filename> subdirectory.
|
||||
<filename>recipe-*</filename> subdirectory.
|
||||
You can find <filename>recipes.txt</filename> in the
|
||||
<filename>meta</filename> directory of the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>,
|
||||
@@ -788,7 +780,7 @@
|
||||
BSP layer maintainer.
|
||||
This is the person to whom patches and questions should
|
||||
be sent.
|
||||
For information on how to find the right person, see the
|
||||
For information on how to find the right person, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#how-to-submit-a-change'>How to Submit a Change</ulink>"
|
||||
section in the Yocto Project Development Manual.
|
||||
</para></listitem>
|
||||
@@ -819,9 +811,9 @@
|
||||
This file identifies the <filename>meta-<bsp_name></filename>
|
||||
BSP layer as a layer to the build system.</para></listitem>
|
||||
<listitem><para><emphasis>Machine Configuration File:</emphasis>
|
||||
You must include one or more <filename>conf/machine/<bsp_name>.conf</filename>
|
||||
files in the <filename>meta-<bsp_name></filename> directory.
|
||||
These configuration files define machine targets that can be built
|
||||
You must include a <filename>conf/machine/<bsp_name>.conf</filename>
|
||||
in the <filename>meta-<bsp_name></filename> directory.
|
||||
This configuration file defines a machine target that can be built
|
||||
using the BSP layer.
|
||||
Multiple machine configuration files define variations of machine
|
||||
configurations that are supported by the BSP.
|
||||
@@ -835,8 +827,7 @@
|
||||
<note>It is completely possible for a developer to structure the
|
||||
working repository as a conglomeration of unrelated BSP
|
||||
files, and to possibly generate BSPs targeted for release
|
||||
from that directory using scripts or some other mechanism
|
||||
(e.g. <filename>meta-yocto-bsp</filename> layer).
|
||||
from that directory using scripts or some other mechanism.
|
||||
Such considerations are outside the scope of this document.</note>
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
@@ -894,7 +885,7 @@
|
||||
<itemizedlist>
|
||||
<listitem><para>Create a <filename>.bbappend</filename>
|
||||
file for the modified recipe.
|
||||
For information on using append files, see the
|
||||
For information on using append files, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#using-bbappend-files'>Using .bbappend Files</ulink>"
|
||||
section in the Yocto Project Development Manual.
|
||||
</para></listitem>
|
||||
@@ -903,15 +894,15 @@
|
||||
that supports your machine is such that it can be found
|
||||
by the build system.
|
||||
See the example later in this section for more information.
|
||||
</para></listitem>
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Put the append file in a directory whose name matches
|
||||
the machine's name and is located in an appropriate
|
||||
sub-directory inside the BSP layer (i.e.
|
||||
sub-directory inside the BSP layer (i.e.
|
||||
<filename>recipes-bsp</filename>, <filename>recipes-graphics</filename>,
|
||||
<filename>recipes-core</filename>, and so forth).
|
||||
<filename>recipes-core</filename>, and so forth).
|
||||
</para></listitem>
|
||||
<listitem><para>Place the BSP-specific files in the directory named for
|
||||
<listitem><para>Place the BSP-specific files in the directory named for
|
||||
your machine inside the BSP layer.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
@@ -930,7 +921,7 @@
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
|
||||
PRINC := "${@int(PRINC) + 2}"
|
||||
</literallayout>
|
||||
The append file needs to be in the
|
||||
The append file needs to be in the
|
||||
<filename>meta-xyz/recipes-core/init-ifupdown</filename> directory.
|
||||
</para></listitem>
|
||||
<listitem><para>Create and place the new <filename>interfaces</filename>
|
||||
@@ -938,11 +929,11 @@
|
||||
<literallayout class='monospaced'>
|
||||
meta-xyz/recipes-core/init-ifupdown/files/xyz/interfaces
|
||||
</literallayout>
|
||||
The
|
||||
The
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'><filename>FILESEXTRAPATHS</filename></ulink>
|
||||
variable in the append files extends the search path
|
||||
variable in the append files extends the search path
|
||||
the build system uses to find files during the build.
|
||||
Consequently, for this example you need to have the
|
||||
Consequently, for this example you need to have the
|
||||
<filename>files</filename> directory in the same location
|
||||
as your append file.</para></listitem>
|
||||
</orderedlist>
|
||||
@@ -978,7 +969,7 @@
|
||||
|
||||
<para>
|
||||
For cases where you can substitute a free component and still
|
||||
maintain the system's functionality, the "Downloads" page from the
|
||||
maintain the system's functionality, the "Downloads" page from the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project website's</ulink>
|
||||
makes available de-featured BSPs
|
||||
that are completely free of any IP encumbrances.
|
||||
@@ -1033,8 +1024,8 @@
|
||||
can build the encumbered image with no change at all
|
||||
to the normal build process.</para></listitem>
|
||||
<listitem><para><emphasis>Get a pre-built version of the BSP:</emphasis>
|
||||
You can get this type of BSP by visiting the
|
||||
"Downloads" page of the
|
||||
You can get this type of BSP by visiting the
|
||||
"Downloads" page of the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project website</ulink>.
|
||||
You can download BSP tarballs that contain proprietary components
|
||||
after agreeing to the licensing
|
||||
@@ -1193,8 +1184,8 @@
|
||||
the tools.
|
||||
<note>
|
||||
You can also use the <filename>yocto-layer</filename> tool to create
|
||||
a "generic" layer.
|
||||
For information on this tool, see the
|
||||
a "generic" layer.
|
||||
For information on this tool, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#creating-a-general-layer-using-the-yocto-layer-script'>Creating a General Layer Using the yocto-layer Script</ulink>"
|
||||
section in the Yocto Project Development Guide.
|
||||
</note>
|
||||
@@ -1216,7 +1207,7 @@
|
||||
by the Yocto Project, as well as QEMU versions of the same.
|
||||
The default mode of the script's operation is to prompt you for information needed
|
||||
to generate the BSP layer.
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For the current set of BSPs, the script prompts you for various important
|
||||
@@ -1243,11 +1234,11 @@
|
||||
<literallayout class='monospaced'>
|
||||
$ yocto-bsp list karch
|
||||
Architectures available:
|
||||
powerpc
|
||||
i386
|
||||
x86_64
|
||||
arm
|
||||
qemu
|
||||
x86_64
|
||||
i386
|
||||
powerpc
|
||||
arm
|
||||
mips
|
||||
</literallayout>
|
||||
</para>
|
||||
@@ -1268,10 +1259,10 @@
|
||||
As the <filename>yocto-bsp create</filename> command runs, default values for
|
||||
the prompts appear in brackets.
|
||||
Pressing enter without supplying anything on the command line or pressing enter
|
||||
with an invalid response causes the script to accept the default value.
|
||||
and providing an invalid response causes the script to accept the default value.
|
||||
Once the script completes, the new <filename>meta-myarm</filename> BSP layer
|
||||
is created in the current working directory.
|
||||
This example assumes you have sourced the
|
||||
This example assumes you have sourced the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
and are currently in the top-level folder of the
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>.
|
||||
@@ -1359,7 +1350,7 @@
|
||||
<filename>bblayers.conf</filename> file.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
BBLAYERS = ? " \
|
||||
BBLAYERS = ?" \
|
||||
/usr/local/src/yocto/meta \
|
||||
/usr/local/src/yocto/meta-yocto \
|
||||
/usr/local/src/yocto/meta-yocto-bsp \
|
||||
|
||||
@@ -1355,24 +1355,24 @@
|
||||
<para>
|
||||
The most important variables to set in this file are as follows:
|
||||
<itemizedlist>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-TARGET_ARCH'>TARGET_ARCH</ulink></filename>
|
||||
(e.g. "arm")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-PREFERRED_PROVIDER'>PREFERRED_PROVIDER</ulink>_virtual/kernel</filename>
|
||||
(see below)</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE_FEATURES'>MACHINE_FEATURES</ulink></filename>
|
||||
(e.g. "apm screen wifi")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-TARGET_ARCH'>
|
||||
TARGET_ARCH</ulink></filename> (e.g. "arm")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-PREFERRED_PROVIDER'>
|
||||
PREFERRED_PROVIDER</ulink></filename>_virtual/kernel (see below)</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE_FEATURES'>
|
||||
MACHINE_FEATURES</ulink></filename> (e.g. "apm screen wifi")</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You might also need these variables:
|
||||
<itemizedlist>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-SERIAL_CONSOLES'>SERIAL_CONSOLES</ulink></filename>
|
||||
(e.g. "115200 ttyS0")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_IMAGETYPE'>KERNEL_IMAGETYPE</ulink></filename>
|
||||
(e.g. "zImage")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-IMAGE_FSTYPES'>IMAGE_FSTYPES</ulink></filename>
|
||||
(e.g. "tar.gz jffs2")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-SERIAL_CONSOLE'>
|
||||
SERIAL_CONSOLE</ulink></filename> (e.g. "115200 ttyS0")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_IMAGETYPE'>
|
||||
KERNEL_IMAGETYPE</ulink></filename> (e.g. "zImage")</para></listitem>
|
||||
<listitem><para><filename><ulink url='&YOCTO_DOCS_REF_URL;#var-IMAGE_FSTYPES'>
|
||||
IMAGE_FSTYPES</ulink></filename> (e.g. "tar.gz jffs2")</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
@@ -1812,8 +1812,6 @@
|
||||
environment, you must build the tool using BitBake.
|
||||
Thus, the environment must be set up using the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>
|
||||
script found in the
|
||||
<link linkend='build-directory'>Build Directory</link>.
|
||||
The following commands build and invoke <filename>menuconfig</filename> assuming the
|
||||
@@ -2525,8 +2523,6 @@
|
||||
</para></listitem>
|
||||
<listitem><para>Reduce the size of the kernel.
|
||||
</para></listitem>
|
||||
<listitem><para>Eliminate packaging requirements.
|
||||
</para></listitem>
|
||||
<listitem><para>Look for other ways to minimize size.
|
||||
</para></listitem>
|
||||
<listitem><para>Iterate on the process.</para></listitem>
|
||||
@@ -2808,46 +2804,6 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='remove-package-management-requirements'>
|
||||
<title>Remove Package Management Requirements</title>
|
||||
|
||||
<para>
|
||||
Packaging requirements add size to the image.
|
||||
One way to reduce the size of the image is to remove all the
|
||||
packaging requirements from the image.
|
||||
This reduction includes both removing the package manager
|
||||
and its unique dependencies as well as removing the package
|
||||
management data itself.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To eliminate all the packaging requirements for an image,
|
||||
follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Put the following line in your main
|
||||
recipe for the image to remove package management
|
||||
data files:
|
||||
<literallayout class='monospaced'>
|
||||
ROOTFS_POSTPROCESS_COMMAND += "remove_packaging_data_files ;
|
||||
</literallayout>
|
||||
For example, the recipe for the
|
||||
<filename>core-image-minimal</filename> image contains
|
||||
this line.
|
||||
You can also add the line to the
|
||||
<filename>local.conf</filename> configuration file.
|
||||
</para></listitem>
|
||||
<listitem><para>Be sure that "package-management" is not
|
||||
part of your
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-IMAGE_FEATURES'><filename>IMAGE_FEATURES</filename></ulink>
|
||||
statement for the image.
|
||||
When you remove this feature, you are removing the
|
||||
package manager as well as its dependencies
|
||||
from the root filesystem.
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='look-for-other-ways-to-minimize-size'>
|
||||
<title>Look for Other Ways to Minimize Size</title>
|
||||
|
||||
@@ -2926,8 +2882,6 @@
|
||||
<para>
|
||||
This section describes a few tasks that involve packages:
|
||||
<itemizedlist>
|
||||
<listitem><para>Excluding packages from an image
|
||||
</para></listitem>
|
||||
<listitem><para>Incrementing a package revision number
|
||||
</para></listitem>
|
||||
<listitem><para>Handling a package name alias
|
||||
@@ -2942,50 +2896,6 @@
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<section id='excluding-packages-from-an-image'>
|
||||
<title>Excluding Packages from an Image</title>
|
||||
|
||||
<para>
|
||||
You might find it necessary to prevent specific packages
|
||||
from being installed into an image.
|
||||
If so, you can use several variables to direct the build
|
||||
system to essentially ignore installing recommended packages
|
||||
or to not install a package at all.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following list introduces variables you can use to
|
||||
prevent packages from being installed into your image.
|
||||
Each of these variables only works with IPK and RPM
|
||||
package types.
|
||||
Support for Debian packages does not exist.
|
||||
Also, you can use these variables from your
|
||||
<filename>local.conf</filename> file or attach them to a
|
||||
specific image recipe by using a recipe name override.
|
||||
For more detail on the variables, see the descriptions in the
|
||||
Yocto Project Reference Manual's glossary chapter.
|
||||
<itemizedlist>
|
||||
<listitem><para><ulink url='&YOCTO_DOCS_REF_URL;#var-BAD_RECOMMENDATIONS'><filename>BAD_RECOMMENDATIONS</filename></ulink>:
|
||||
Use this variable to specify "recommended-only"
|
||||
packages that you do not want installed.
|
||||
</para></listitem>
|
||||
<listitem><para><ulink url='&YOCTO_DOCS_REF_URL;#var-NO_RECOMMENDATIONS'><filename>NO_RECOMMENDATIONS</filename></ulink>:
|
||||
Use this variable to prevent all "recommended-only"
|
||||
packages from being installed.
|
||||
</para></listitem>
|
||||
<listitem><para><ulink url='&YOCTO_DOCS_REF_URL;#var-PACKAGE_EXCLUDE'><filename>PACKAGE_EXCLUDE</filename></ulink>:
|
||||
Use this variable to prevent specific packages from
|
||||
being installed regardless of whether they are
|
||||
"recommended-only" or not.
|
||||
You need to realize that the build process could
|
||||
fail with an error when you
|
||||
prevent the installation of a package whose presence
|
||||
is required by an installed package.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='incrementing-a-package-revision-number'>
|
||||
<title>Incrementing a Package Revision Number</title>
|
||||
|
||||
@@ -3513,7 +3423,7 @@
|
||||
|
||||
<para>
|
||||
Following are the steps to set up the optional repository.
|
||||
This example assumes you are using RPM and the Apache 2
|
||||
This examples assumes you are using RPM and the Apache 2
|
||||
server:
|
||||
<orderedlist>
|
||||
<listitem><para>
|
||||
@@ -3544,7 +3454,7 @@
|
||||
<literallayout class='monospaced'>
|
||||
service httpd reload
|
||||
</literallayout>
|
||||
For Ubuntu and Debian, use the following:
|
||||
For Ubuntu, use the following:
|
||||
<literallayout class='monospaced'>
|
||||
/etc/init.d/apache2 reload
|
||||
</literallayout>
|
||||
@@ -3797,58 +3707,54 @@
|
||||
<title>Building Software from an External Source</title>
|
||||
|
||||
<para>
|
||||
By default, the OpenEmbedded build system uses the
|
||||
<link linkend='build-directory'>Build Directory</link> to
|
||||
build source code.
|
||||
The build process involves fetching the source files, unpacking
|
||||
them, and then patching them if necessary before the build takes
|
||||
place.
|
||||
By default, the OpenEmbedded build system does its work from within the
|
||||
<link linkend='build-directory'>Build Directory</link>.
|
||||
The build process involves fetching the source files, unpacking them, and then patching them
|
||||
if necessary before the build takes place.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Situations exist where you might want to build software from source
|
||||
files that are external to and thus outside of the
|
||||
OpenEmbedded build system.
|
||||
For example, suppose you have a project that includes a new BSP with
|
||||
a heavily customized kernel.
|
||||
Situations exist where you might want to build software from source files that are external to
|
||||
and thus outside of the <link linkend='source-directory'>Source Directory</link>.
|
||||
For example, suppose you have a project that includes a new BSP with a heavily customized
|
||||
kernel, a very minimal image, and some new user-space recipes.
|
||||
And, you want to minimize exposing the build system to the
|
||||
development team so that they can focus on their project and
|
||||
maintain everyone's workflow as much as possible.
|
||||
In this case, you want a kernel source directory on the development
|
||||
machine where the development occurs.
|
||||
development team so that they can focus on their project and maintain everyone's workflow
|
||||
as much as possible.
|
||||
In this case, you want a kernel source directory on the development machine where the
|
||||
development occurs.
|
||||
You want the recipe's
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-SRC_URI'><filename>SRC_URI</filename></ulink>
|
||||
variable to point to the external directory and use it as is, not
|
||||
copy it.
|
||||
variable to point to the external directory and use it as is, not copy it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To build from software that comes from an external source, all you
|
||||
need to do is inherit
|
||||
To build from software that comes from an external source, all you need to do is
|
||||
change your recipe so that it inherits the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#ref-classes-externalsrc'><filename>externalsrc.bbclass</filename></ulink>
|
||||
and then set the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-EXTERNALSRC'><filename>EXTERNALSRC</filename></ulink>
|
||||
class and then sets the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-S'><filename>S</filename></ulink>
|
||||
variable to point to your external source code.
|
||||
Here are the statements to put in your
|
||||
<filename>local.conf</filename> file:
|
||||
Here are the statements to put in your recipe:
|
||||
<literallayout class='monospaced'>
|
||||
INHERIT += "externalsrc"
|
||||
EXTERNALSRC_pn-myrecipe = "/some/path/to/your/source/tree"
|
||||
inherit externalsrc
|
||||
S = "/some/path/to/your/package/source"
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
By default, <filename>externalsrc.bbclass</filename> builds
|
||||
the source code in a directory separate from the external source
|
||||
directory as specified by
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-EXTERNALSRC'><filename>EXTERNALSRC</filename></ulink>.
|
||||
If you need to have the source built in the same directory in
|
||||
which it resides, or some other nominated directory, you can set
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-EXTERNALSRC_BUILD'><filename>EXTERNALSRC_BUILD</filename></ulink>
|
||||
to point to that directory:
|
||||
<literallayout class='monospaced'>
|
||||
EXTERNALSRC_BUILD_pn-myrecipe = "/path/to/my/source/tree"
|
||||
</literallayout>
|
||||
It is important to know that the <filename>externalsrc.bbclass</filename> assumes that the
|
||||
source directory <filename>S</filename> and the Build Directory
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-B'><filename>B</filename></ulink>
|
||||
are different even though these directories are the same by default.
|
||||
This assumption is important because it supports building different variants of the recipe
|
||||
by using the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-BBCLASSEXTEND'><filename>BBCLASSEXTEND</filename></ulink>
|
||||
variable.
|
||||
You could allow the Build Directory to be the same as the source directory but you would
|
||||
not be able to build more than one variant of the recipe.
|
||||
Consequently, if you are building multiple variants of the recipe, you need to establish a
|
||||
Build Directory that is different than the Source Directory.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -3865,13 +3771,12 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want to use <filename>sysvinit</filename>, you do
|
||||
If you want to use <filename>SysVinit</filename>, you do
|
||||
not have to do anything.
|
||||
But, if you want to use <filename>systemd</filename>, you must
|
||||
take some steps as described in the following sections.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<note>
|
||||
It is recommended that you create your own distribution configuration
|
||||
file to hold these settings instead of using your
|
||||
@@ -3880,7 +3785,6 @@
|
||||
"<link linkend='creating-your-own-distribution'>Creating Your Own Distribution</link>"
|
||||
section.
|
||||
</note>
|
||||
-->
|
||||
|
||||
<section id='using-systemd-exclusively'>
|
||||
<title>Using systemd Exclusively</title>
|
||||
@@ -3901,12 +3805,6 @@
|
||||
Doing so removes any redundant <filename>sysvinit</filename>
|
||||
scripts.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For information on the backfill variable, see
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-DISTRO_FEATURES_BACKFILL_CONSIDERED'><filename>DISTRO_FEATURES_BACKFILL_CONSIDERED</filename></ulink>
|
||||
in the Yocto Project Reference Manual.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='using-systemd-for-the-main-image-and-using-sysvinit-for-the-rescue-image'>
|
||||
@@ -4371,191 +4269,6 @@
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="examining-builds-using-toaster">
|
||||
<title>Examining Builds using Toaster</title>
|
||||
|
||||
<para>
|
||||
Toaster is a Web-based interface to the OpenEmbedded build system,
|
||||
which uses BitBake.
|
||||
Toaster offers features to record and analyze BitBake runs applied
|
||||
to a specific target.
|
||||
Using Toaster, you can do the following:
|
||||
<itemizedlist>
|
||||
<listitem><para>See information about the tasks executed
|
||||
and reused during the build.</para></listitem>
|
||||
<listitem><para>See what is built (recipes and
|
||||
packages) and what packages were installed into the final
|
||||
image.</para></listitem>
|
||||
<listitem><para>See performance-related information such
|
||||
as build time, CPU usage, and disk I/O.</para></listitem>
|
||||
<listitem><para>Examine error, warning and trace messages
|
||||
to aid in debugging.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>This release of Toaster provides you with information
|
||||
about a BitBake run.
|
||||
The tool does not allow you to configure and launch a build.
|
||||
Future development includes plans to integrate the data examination
|
||||
features of Toaster with the configuration and build launching
|
||||
capabilities of
|
||||
<ulink url='&YOCTO_HOME_URL;/tools-resources/projects/hob'>Hob</ulink>.
|
||||
</para>
|
||||
<para>For more information on using Hob to build an image,
|
||||
see the
|
||||
"<link linkend='image-development-using-hob'>Image Development Using Hob</link>"
|
||||
section.</para>
|
||||
</note>
|
||||
|
||||
<section id='starting-toaster'>
|
||||
<title>Starting Toaster</title>
|
||||
|
||||
<para>
|
||||
Getting set up to use and start Toaster is simple.
|
||||
First, be sure you have met the following requirements:
|
||||
<itemizedlist>
|
||||
<listitem><para>You have set up your
|
||||
<link linkend='source-directory'>Source Directory</link>.
|
||||
See the
|
||||
<link linkend='local-yp-release'>Yocto Project Release</link>
|
||||
item for information on how to set up the Source
|
||||
Directory.</para></listitem>
|
||||
<listitem><para>Be sure your build machine has
|
||||
<ulink url='http://en.wikipedia.org/wiki/Django_%28web_framework%29'>Django</ulink>
|
||||
version 1.4 or greater installed.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you have met the requirements, follow these steps to
|
||||
start Toaster running in the background of your shell:
|
||||
<orderedlist>
|
||||
<listitem><para>Set up your build environment by sourcing
|
||||
the <filename>oe-init-build-env</filename> script.
|
||||
</para></listitem>
|
||||
<listitem><para>Edit your <filename>local.conf</filename>
|
||||
configuration file as needed.</para></listitem>
|
||||
<listitem><para>Start the Toaster service using this
|
||||
command from within your build directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ source toaster start
|
||||
</literallayout></para></listitem>
|
||||
<note>
|
||||
The Toaster must be started and running in order
|
||||
for it to collect data.
|
||||
</note>
|
||||
</orderedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When Toaster starts, it creates some additional files in your
|
||||
Build Directory.
|
||||
Deleting these files will cause you to lose data or interrupt
|
||||
Toaster:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis><filename>toaster.sqlite</filename>:</emphasis>
|
||||
Toaster's database file.</para></listitem>
|
||||
<listitem><para><emphasis><filename>tstmain.log</filename>:</emphasis>
|
||||
Toaster's log file.</para></listitem>
|
||||
<listitem><para><emphasis><filename>tstmain.pid</filename>:</emphasis>
|
||||
Contains the PID of the web server.</para></listitem>
|
||||
<listitem><para><emphasis><filename>dsi.pid</filename>:</emphasis>
|
||||
Contains the PID of th bridge.</para></listitem>
|
||||
<listitem><para><emphasis><filename>bitbake-cookerdaemon.log</filename>:</emphasis>
|
||||
The BitBake server's log file.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='using-toaster'>
|
||||
<title>Using Toaster</title>
|
||||
|
||||
<para>
|
||||
Once Toaster is running, it logs information for any BitBake
|
||||
run from your Build Directory.
|
||||
This logging is automatic.
|
||||
All you need to do is access and use the information.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You access the information one of two ways:
|
||||
<itemizedlist>
|
||||
<listitem><para>Open a Browser and type enter in the
|
||||
<filename>http://localhost:8000</filename> URL.
|
||||
</para></listitem>
|
||||
<listitem><para>Use the <filename>xdg-open</filename>
|
||||
tool from the shell and pass it the same URL.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
Either method opens the home page for the Toaster interface.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='examining-toaster-data'>
|
||||
<title>Examining Toaster Data</title>
|
||||
|
||||
<para>
|
||||
The Toaster database is persistent regardless of whether you
|
||||
start or stop the service.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Toaster's interface shows you a list of builds
|
||||
(successful and unsuccessful) for which it has data.
|
||||
You can click on any build to see related information.
|
||||
This information includes configuration details, information
|
||||
about tasks, all recipes and packages built and their
|
||||
dependencies, packages installed in your final image,
|
||||
execution time, CPU usage and disk I/O per task.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
The home page of the interface into the database organizes
|
||||
builds into areas:
|
||||
<itemizedlist>
|
||||
<listitem><para>Recent successful builds, which appear
|
||||
in row format in a green area.</para></listitem>
|
||||
<listitem><para>Recent failed builds, which appear
|
||||
in row format in a red area.</para></listitem>
|
||||
<listitem><para>Recent builds in progress, which appear
|
||||
in row format in a yellow area.</para></listitem>
|
||||
<listitem><para>All builds, which appear in row format at
|
||||
the end of the page.</para></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Each entry is linked to more detail on the particular build
|
||||
or recipe.
|
||||
You can click on the links to learn more information.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When you click on a failed recipe link, you can find out
|
||||
information such as the work directory, the pathname to the
|
||||
failing recipe, the exact error message, and precursor tasks.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Clicking on a successful build provides you with configuration,
|
||||
task, and package information along with directory structure,
|
||||
build time, CPU usage, and disk I/O information.
|
||||
</para>
|
||||
-->
|
||||
</section>
|
||||
|
||||
<section id='stopping-toaster'>
|
||||
<title>Stopping Toaster</title>
|
||||
|
||||
<para>
|
||||
Stop the Toaster service with the following command:
|
||||
<literallayout class='monospaced'>
|
||||
$ source toaster stop
|
||||
</literallayout>
|
||||
The service stops but the Toaster database remains persistent.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="platdev-oprofile">
|
||||
<title>Profiling with OProfile</title>
|
||||
|
||||
@@ -5028,15 +4741,15 @@
|
||||
BBFILES ?= ""
|
||||
|
||||
BBLAYERS ?= " \
|
||||
##OEROOT##/meta \
|
||||
##OEROOT##/meta-yocto \
|
||||
##OEROOT##/meta-yocto-bsp \
|
||||
##OEROOT##/meta-mylayer \
|
||||
##COREBASE##/meta \
|
||||
##COREBASE##/meta-yocto \
|
||||
##COREBASE##/meta-yocto-bsp \
|
||||
##COREBASE##/meta-mylayer \
|
||||
"
|
||||
|
||||
BBLAYERS_NON_REMOVABLE ?= " \
|
||||
##OEROOT##/meta \
|
||||
##OEROOT##/meta-yocto \
|
||||
##COREBASE##/meta \
|
||||
##COREBASE##/meta-yocto \
|
||||
"
|
||||
</literallayout>
|
||||
Creating and providing an archive of the
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<listitem><para>Step-by-step instructions if those instructions exist in other Yocto
|
||||
Project documentation.
|
||||
For example, the Yocto Project Application Developer's Guide contains detailed
|
||||
instructions on how to run the
|
||||
instruction on how to run the
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;#installing-the-adt'>Installing the ADT and Toolchains</ulink>,
|
||||
which is used to set up a cross-development environment.</para></listitem>
|
||||
<listitem><para>Reference material.
|
||||
@@ -96,9 +96,8 @@
|
||||
<title>Other Information</title>
|
||||
|
||||
<para>
|
||||
Because this manual presents overview information for many different
|
||||
topics, supplemental information is recommended for full
|
||||
comprehension.
|
||||
Because this manual presents overview information for many different topics, you will
|
||||
need to supplement it with other information.
|
||||
The following list presents other sources of information you might find helpful:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis><ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink>:
|
||||
@@ -106,7 +105,7 @@
|
||||
as well as links to software and documentation.</para></listitem>
|
||||
<listitem><para><emphasis>
|
||||
<ulink url='&YOCTO_DOCS_QS_URL;'>Yocto Project Quick Start</ulink>:</emphasis> This short document lets you get started
|
||||
with the Yocto Project and quickly begin building an image.</para></listitem>
|
||||
with the Yocto Project quickly and start building an image.</para></listitem>
|
||||
<listitem><para><emphasis>
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;'>Yocto Project Reference Manual</ulink>:</emphasis> This manual is a reference
|
||||
guide to the OpenEmbedded build system known as "Poky."
|
||||
@@ -171,7 +170,7 @@
|
||||
OpenEmbedded build system (Poky).
|
||||
</para></listitem>
|
||||
<listitem><para><ulink url='&YOCTO_LISTS_URL;/listinfo/yocto-announce'></ulink>
|
||||
for a mailing list to receive official Yocto Project announcements
|
||||
for a mailing list to receive official Yocto Project announcements for developments and
|
||||
as well as Yocto Project milestones.</para></listitem>
|
||||
<listitem><para><ulink url='&YOCTO_LISTS_URL;/listinfo'></ulink> for a
|
||||
listing of all public mailing lists on <filename>lists.yoctoproject.org</filename>.
|
||||
@@ -184,7 +183,7 @@
|
||||
<listitem><para><emphasis>
|
||||
<ulink url='&OE_HOME_URL;'>OpenEmbedded</ulink>:</emphasis>
|
||||
The build system used by the Yocto Project.
|
||||
This project is the upstream, generic, embedded distribution that the Yocto
|
||||
This project is the upstream, generic, embedded distribution from which the Yocto
|
||||
Project derives its build system (Poky) from and to which it contributes.</para></listitem>
|
||||
<listitem><para><emphasis>
|
||||
<ulink url='http://developer.berlios.de/projects/bitbake/'>
|
||||
|
||||
553
documentation/dev-manual/dev-manual-kernel-appendix-orig.xml
Normal file
@@ -0,0 +1,553 @@
|
||||
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[<!ENTITY % poky SYSTEM "../poky.ent"> %poky; ] >
|
||||
|
||||
<appendix id='dev-manual-kernel-appendix'>
|
||||
|
||||
<title>Kernel Modification Example</title>
|
||||
|
||||
<para>
|
||||
Kernel modification involves changing or adding configurations to an existing kernel,
|
||||
changing or adding recipes to the kernel that are needed to support specific hardware features,
|
||||
or even altering the source code itself.
|
||||
This appendix presents simple examples that modify the kernel source code,
|
||||
change the kernel configuration, and add a kernel source recipe.
|
||||
<note>
|
||||
You can use the <filename>yocto-kernel</filename> script
|
||||
found in the <link linkend='source-directory'>Source Directory</link>
|
||||
under <filename>scripts</filename> to manage kernel patches and configuration.
|
||||
See the "<ulink url='&YOCTO_DOCS_BSP_URL;#managing-kernel-patches-and-config-items-with-yocto-kernel'>Managing kernel Patches and Config Items with yocto-kernel</ulink>"
|
||||
section in the Yocto Project Board Support Packages (BSP) Developer's Guide for
|
||||
more information.</note>
|
||||
</para>
|
||||
|
||||
<section id='modifying-the-kernel-source-code'>
|
||||
<title>Modifying the Kernel Source Code</title>
|
||||
|
||||
<para>
|
||||
This example adds some simple QEMU emulator console output at boot time by
|
||||
adding <filename>printk</filename> statements to the kernel's
|
||||
<filename>calibrate.c</filename> source code file.
|
||||
Booting the modified image causes the added messages to appear on the emulator's
|
||||
console.
|
||||
</para>
|
||||
|
||||
<section id='understanding-the-files-you-need'>
|
||||
<title>Understanding the Files You Need</title>
|
||||
|
||||
<para>
|
||||
Before you modify the kernel, you need to know what Git repositories and file
|
||||
structures you need.
|
||||
Briefly, you need the following:
|
||||
<itemizedlist>
|
||||
<listitem><para>A local
|
||||
<link linkend='source-directory'>Source Directory</link> for the
|
||||
poky Git repository</para></listitem>
|
||||
<listitem><para>Local copies of the
|
||||
<link linkend='poky-extras-repo'><filename>poky-extras</filename></link>
|
||||
Git repository placed within the Source Directory.</para></listitem>
|
||||
<listitem><para>A bare clone of the
|
||||
<link linkend='local-kernel-files'>Yocto Project Kernel</link> upstream Git
|
||||
repository to which you want to push your modifications.
|
||||
</para></listitem>
|
||||
<listitem><para>A copy of that bare clone in which you make your source
|
||||
modifications</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following figure summarizes these four areas.
|
||||
Within each rectangular that represents a data structure, a
|
||||
host development directory pathname appears at the
|
||||
lower left-hand corner of the box.
|
||||
These pathnames are the locations used in this example.
|
||||
The figure also provides key statements and commands used during the kernel
|
||||
modification process:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<imagedata fileref="figures/kernel-example-repos-generic.png" width="7in" depth="5in"
|
||||
align="center" scale="100" />
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here is a brief description of the four areas:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Local Source Directory:</emphasis>
|
||||
This area contains all the metadata that supports building images
|
||||
using the OpenEmbedded build system.
|
||||
In this example, the
|
||||
<link linkend='source-directory'>Source Directory</link> also
|
||||
contains the
|
||||
<link linkend='build-directory'>Build Directory</link>,
|
||||
which contains the configuration directory
|
||||
that lets you control the build.
|
||||
Also in this example, the Source Directory contains local copies of the
|
||||
<filename>poky-extras</filename> Git repository.</para>
|
||||
<para>See the bulleted item
|
||||
"<link linkend='local-yp-release'>Yocto Project Release</link>"
|
||||
for information on how to get these files on your local system.</para></listitem>
|
||||
<listitem><para><emphasis>Local copies of the <filename>poky-extras</filename> Git Repository:</emphasis>
|
||||
This area contains the <filename>meta-kernel-dev</filename> layer,
|
||||
which is where you make changes that append the kernel build recipes.
|
||||
You edit <filename>.bbappend</filename> files to locate your
|
||||
local kernel source files and to identify the kernel being built.
|
||||
This Git repository is a gathering place for extensions to the Yocto Project
|
||||
(or really any) kernel recipes that faciliate the creation and development
|
||||
of kernel features, BSPs or configurations.</para>
|
||||
<para>See the bulleted item
|
||||
"<link linkend='poky-extras-repo'>The
|
||||
<filename>poky-extras</filename> Git Repository</link>"
|
||||
for information on how to get these files.</para></listitem>
|
||||
<listitem><para><emphasis>Bare Clone of the Yocto Project kernel:</emphasis>
|
||||
This bare Git repository tracks the upstream Git repository of the Linux
|
||||
Yocto kernel source code you are changing.
|
||||
When you modify the kernel you must work through a bare clone.
|
||||
All source code changes you make to the kernel must be committed and
|
||||
pushed to the bare clone using Git commands.
|
||||
As mentioned, the <filename>.bbappend</filename> file in the
|
||||
<filename>poky-extras</filename> repository points to the bare clone
|
||||
so that the build process can locate the locally changed source files.</para>
|
||||
<para>See the bulleted item
|
||||
"<link linkend='local-kernel-files'>Yocto Project Kernel</link>"
|
||||
for information on how to set up the bare clone.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Copy of the Yocto Project Kernel Bare Clone:</emphasis>
|
||||
This Git repository contains the actual source files that you modify.
|
||||
Any changes you make to files in this location need to ultimately be pushed
|
||||
to the bare clone using the <filename>git push</filename> command.</para>
|
||||
<para>See the bulleted item
|
||||
"<link linkend='local-kernel-files'>Yocto Project Kernel</link>"
|
||||
for information on how to set up the bare clone.
|
||||
<note>Typically, Git workflows follow a scheme where changes made to a local area
|
||||
are pulled into a Git repository.
|
||||
However, because the <filename>git pull</filename> command does not work
|
||||
with bare clones, this workflow pushes changes to the
|
||||
repository even though you could use other more complicated methods to
|
||||
get changes into the bare clone.</note>
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='setting-up-the-local-yocto-project-files-git-repository'>
|
||||
<title>Setting Up the Local Source Directory</title>
|
||||
|
||||
<para>
|
||||
You can set up the
|
||||
<link linkend='source-directory'>Source Directory</link>
|
||||
through tarball extraction or by
|
||||
cloning the <filename>poky</filename> Git repository.
|
||||
This example uses <filename>poky</filename> as the root directory of the
|
||||
local Source Directory.
|
||||
See the bulleted item
|
||||
"<link linkend='local-yp-release'>Yocto Project Release</link>"
|
||||
for information on how to get these files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you have Source Directory set up,
|
||||
you have many development branches from which you can work.
|
||||
From inside the local repository you can see the branch names and the tag names used
|
||||
in the upstream Git repository by using either of the following commands:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd poky
|
||||
$ git branch -a
|
||||
$ git tag -l
|
||||
</literallayout>
|
||||
This example uses the Yocto Project &DISTRO; Release code named "&DISTRO_NAME;",
|
||||
which maps to the <filename>&DISTRO_NAME;</filename> branch in the repository.
|
||||
The following commands create and checkout the local <filename>&DISTRO_NAME;</filename>
|
||||
branch:
|
||||
<literallayout class='monospaced'>
|
||||
$ git checkout -b &DISTRO_NAME; origin/&DISTRO_NAME;
|
||||
Branch &DISTRO_NAME; set up to track remote branch &DISTRO_NAME; from origin.
|
||||
Switched to a new branch '&DISTRO_NAME;'
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='setting-up-the-poky-extras-git-repository'>
|
||||
<title>Setting Up the Local poky-extras Git Repository</title>
|
||||
|
||||
<para>
|
||||
This example creates a local copy of the <filename>poky-extras</filename> Git
|
||||
repository inside the <filename>poky</filename> Source Directory.
|
||||
See the bulleted item "<link linkend='poky-extras-repo'>The
|
||||
<filename>poky-extras</filename> Git Repository</link>"
|
||||
for information on how to set up a local copy of the
|
||||
<filename>poky-extras</filename> repository.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Because this example uses the Yocto Project &DISTRO; Release code
|
||||
named "&DISTRO_NAME;", which maps to the <filename>&DISTRO_NAME;</filename>
|
||||
branch in the repository, you need to be sure you are using that
|
||||
branch for <filename>poky-extras</filename>.
|
||||
The following commands create and checkout the local
|
||||
branch you are using for the <filename>&DISTRO_NAME;</filename>
|
||||
branch:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/poky/poky-extras
|
||||
$ git checkout -b &DISTRO_NAME; origin/&DISTRO_NAME;
|
||||
Branch &DISTRO_NAME; set up to track remote branch &DISTRO_NAME; from origin.
|
||||
Switched to a new branch '&DISTRO_NAME;'
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='setting-up-the-bare-clone-and-its-copy'>
|
||||
<title>Setting Up the Bare Clone and its Copy</title>
|
||||
|
||||
<para>
|
||||
This example modifies the <filename>linux-yocto-3.4</filename> kernel.
|
||||
Thus, you need to create a bare clone of that kernel and then make a copy of the
|
||||
bare clone.
|
||||
See the bulleted item
|
||||
"<link linkend='local-kernel-files'>Yocto Project Kernel</link>"
|
||||
for information on how to do that.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The bare clone exists for the kernel build tools and simply as the receiving end
|
||||
of <filename>git push</filename>
|
||||
commands after you make edits and commits inside the copy of the clone.
|
||||
The copy (<filename>my-linux-yocto-3.4-work</filename> in this example) has to have
|
||||
a local branch created and checked out for your work.
|
||||
This example uses <filename>common-pc-base</filename> as the local branch.
|
||||
The following commands create and checkout the branch:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/my-linux-yocto-3.4-work
|
||||
$ git checkout -b standard-common-pc-base origin/standard/common-pc/base
|
||||
Branch standard-common-pc-base set up to track remote branch
|
||||
standard/common-pc/base from origin.
|
||||
Switched to a new branch 'standard-common-pc-base'
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='building-and-booting-the-default-qemu-kernel-image'>
|
||||
<title>Building and Booting the Default QEMU Kernel Image</title>
|
||||
|
||||
<para>
|
||||
Before we make changes to the kernel source files, this example first builds the
|
||||
default image and then boots it inside the QEMU emulator.
|
||||
<note>
|
||||
Because a full build can take hours, you should check two variables in the
|
||||
<filename>build</filename> directory that is created after you source the
|
||||
<filename>&OE_INIT_FILE;</filename> script.
|
||||
You can find these variables
|
||||
<filename>BB_NUMBER_THREADS</filename> and <filename>PARALLEL_MAKE</filename>
|
||||
in the <filename>build/conf</filename> directory in the
|
||||
<filename>local.conf</filename> configuration file.
|
||||
By default, these variables are commented out.
|
||||
If your host development system supports multi-core and multi-thread capabilities,
|
||||
you can uncomment these statements and set the variables to significantly shorten
|
||||
the full build time.
|
||||
As a guideline, set both <filename>BB_NUMBER_THREADS</filename> and
|
||||
<filename>PARALLEL_MAKE</filename> to twice the number
|
||||
of cores your machine supports.
|
||||
</note>
|
||||
The following two commands <filename>source</filename> the build environment setup script
|
||||
and build the default <filename>qemux86</filename> image.
|
||||
If necessary, the script creates the build directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/poky
|
||||
$ source &OE_INIT_FILE;
|
||||
You had no conf/local.conf file. This configuration file has therefore been
|
||||
created for you with some default values. You may wish to edit it to use a
|
||||
different MACHINE (target hardware) or enable parallel build options to take
|
||||
advantage of multiple cores for example. See the file for more information as
|
||||
common configuration options are commented.
|
||||
|
||||
The Yocto Project has extensive documentation about OE including a reference manual
|
||||
which can be found at:
|
||||
http://yoctoproject.org/documentation
|
||||
|
||||
For more information about OpenEmbedded see their website:
|
||||
http://www.openembedded.org/
|
||||
|
||||
You had no conf/bblayers.conf file. The configuration file has been created for
|
||||
you with some default values. To add additional metadata layers into your
|
||||
configuration please add entries to this file.
|
||||
|
||||
The Yocto Project has extensive documentation about OE including a reference manual
|
||||
which can be found at:
|
||||
http://yoctoproject.org/documentation
|
||||
|
||||
For more information about OpenEmbedded see their website:
|
||||
http://www.openembedded.org/
|
||||
|
||||
|
||||
|
||||
### Shell environment set up for builds. ###
|
||||
|
||||
You can now run 'bitbake <target>>'
|
||||
|
||||
Common targets are:
|
||||
core-image-minimal
|
||||
core-image-sato
|
||||
meta-toolchain
|
||||
meta-toolchain-sdk
|
||||
adt-installer
|
||||
meta-ide-support
|
||||
|
||||
You can also run generated qemu images with a command like 'runqemu qemux86'
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following <filename>bitbake</filename> command starts the build:
|
||||
<literallayout class='monospaced'>
|
||||
$ bitbake -k core-image-minimal
|
||||
</literallayout>
|
||||
<note>Be sure to check the settings in the <filename>local.conf</filename>
|
||||
before starting the build.</note>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After the build completes, you can start the QEMU emulator using the resulting image
|
||||
<filename>qemux86</filename> as follows:
|
||||
<literallayout class='monospaced'>
|
||||
$ runqemu qemux86
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As the image boots in the emulator, console message and status output appears
|
||||
across the terminal window.
|
||||
Because the output scrolls by quickly, it is difficult to read.
|
||||
To examine the output, you log into the system using the
|
||||
login <filename>root</filename> with no password.
|
||||
Once you are logged in, issue the following command to scroll through the
|
||||
console output:
|
||||
<literallayout class='monospaced'>
|
||||
# dmesg | less
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Take note of the output as you will want to look for your inserted print command output
|
||||
later in the example.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='changing-the-source-code-and-pushing-it-to-the-bare-clone'>
|
||||
<title>Changing the Source Code and Pushing it to the Bare Clone</title>
|
||||
|
||||
<para>
|
||||
The file you change in this example is named <filename>calibrate.c</filename>
|
||||
and is located in the <filename>my-linux-yocto-3.4-work</filename> Git repository
|
||||
(the copy of the bare clone) in <filename>init</filename>.
|
||||
This example simply inserts several <filename>printk</filename> statements
|
||||
at the beginning of the <filename>calibrate_delay</filename> function.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here is the unaltered code at the start of this function:
|
||||
<literallayout class='monospaced'>
|
||||
void __cpuinit calibrate_delay(void)
|
||||
{
|
||||
unsigned long lpj;
|
||||
static bool printed;
|
||||
int this_cpu = smp_processor_id();
|
||||
|
||||
if (per_cpu(cpu_loops_per_jiffy, this_cpu)) {
|
||||
.
|
||||
.
|
||||
.
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here is the altered code showing five new <filename>printk</filename> statements
|
||||
near the top of the function:
|
||||
<literallayout class='monospaced'>
|
||||
void __cpuinit calibrate_delay(void)
|
||||
{
|
||||
unsigned long lpj;
|
||||
static bool printed;
|
||||
int this_cpu = smp_processor_id();
|
||||
|
||||
printk("*************************************\n");
|
||||
printk("* *\n");
|
||||
printk("* HELLO YOCTO KERNEL *\n");
|
||||
printk("* *\n");
|
||||
printk("*************************************\n");
|
||||
|
||||
if (per_cpu(cpu_loops_per_jiffy, this_cpu)) {
|
||||
.
|
||||
.
|
||||
.
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After making and saving your changes, you need to stage them for the push.
|
||||
The following Git commands are one method of staging and committing your changes:
|
||||
<literallayout class='monospaced'>
|
||||
$ git add calibrate.c
|
||||
$ git commit --signoff
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once the source code has been modified, you need to use Git to push the changes to
|
||||
the bare clone.
|
||||
If you do not push the changes, then the OpenEmbedded build system will not pick
|
||||
up the changed source files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following command pushes the changes to the bare clone:
|
||||
<literallayout class='monospaced'>
|
||||
$ git push origin standard-common-pc-base:standard/default/common-pc/base
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='changing-build-parameters-for-your-build'>
|
||||
<title>Changing Build Parameters for Your Build</title>
|
||||
|
||||
<para>
|
||||
At this point, the source has been changed and pushed.
|
||||
The example now defines some variables used by the OpenEmbedded build system
|
||||
to locate your kernel source.
|
||||
You essentially need to identify where to find the kernel recipe and the changed source code.
|
||||
You also need to be sure some basic configurations are in place that identify the
|
||||
type of machine you are building and to help speed up the build should your host support
|
||||
multiple-core and thread capabilities.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Do the following to make sure the build parameters are set up for the example.
|
||||
Once you set up these build parameters, they do not have to change unless you
|
||||
change the target architecture of the machine you are building or you move
|
||||
the bare clone, copy of the clone, or the <filename>poky-extras</filename> repository:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Build for the Correct Target Architecture:</emphasis> The
|
||||
<filename>local.conf</filename> file in the build directory defines the build's
|
||||
target architecture.
|
||||
By default, <filename>MACHINE</filename> is set to
|
||||
<filename>qemux86</filename>, which specifies a 32-bit
|
||||
<trademark class='registered'>Intel</trademark> Architecture
|
||||
target machine suitable for the QEMU emulator.
|
||||
In this example, <filename>MACHINE</filename> is correctly configured.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Optimize Build Time:</emphasis> Also in the
|
||||
<filename>local.conf</filename> file are two variables that can speed your
|
||||
build time if your host supports multi-core and multi-thread capabilities:
|
||||
<filename>BB_NUMBER_THREADS</filename> and <filename>PARALLEL_MAKE</filename>.
|
||||
If the host system has multiple cores then you can optimize build time
|
||||
by setting both these variables to twice the number of
|
||||
cores.</para></listitem>
|
||||
<listitem><para><emphasis>Identify Your <filename>meta-kernel-dev</filename>
|
||||
Layer:</emphasis> The <filename>BBLAYERS</filename> variable in the
|
||||
<filename>bblayers.conf</filename> file found in the
|
||||
<filename>poky/build/conf</filename> directory needs to have the path to your local
|
||||
<filename>meta-kernel-dev</filename> layer.
|
||||
By default, the <filename>BBLAYERS</filename> variable contains paths to
|
||||
<filename>meta</filename> and <filename>meta-yocto</filename> in the
|
||||
<filename>poky</filename> Git repository.
|
||||
Add the path to your <filename>meta-kernel-dev</filename> location.
|
||||
Be sure to substitute your user information in the statement.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
BBLAYERS = " \
|
||||
/home/scottrif/poky/meta \
|
||||
/home/scottrif/poky/meta-yocto \
|
||||
/home/scottrif/poky/meta-yocto-bsp \
|
||||
/home/scottrif/poky/poky-extras/meta-kernel-dev \
|
||||
"
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para><emphasis>Identify Your Source Files:</emphasis> In the
|
||||
<filename>linux-yocto_3.4.bbappend</filename> file located in the
|
||||
<filename>poky-extras/meta-kernel-dev/recipes-kernel/linux</filename>
|
||||
directory, you need to identify the location of the
|
||||
local source code, which in this example is the bare clone named
|
||||
<filename>linux-yocto-3.4.git</filename>.
|
||||
To do this, set the <filename>KSRC_linux_yocto</filename> variable to point to your
|
||||
local <filename>linux-yocto-3.4.git</filename> Git repository by adding the
|
||||
following statement.
|
||||
Also, be sure the <filename>SRC_URI</filename> variable is pointing to
|
||||
your kernel source files by removing the comment.
|
||||
Finally, be sure to substitute your user information in the statement:
|
||||
<literallayout class='monospaced'>
|
||||
KSRC_linux_yocto_3_4 ?= "/home/scottrif/linux-yocto-3.4.git"
|
||||
SRC_URI = "git://${KSRC_linux_yocto_3_4};protocol=file;nocheckout=1;branch=${KBRANCH},meta;name=machine,meta"
|
||||
</literallayout></para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>Before attempting to build the modified kernel, there is one more set of changes you
|
||||
need to make in the <filename>meta-kernel-dev</filename> layer.
|
||||
Because all the kernel <filename>.bbappend</filename> files are parsed during the
|
||||
build process regardless of whether you are using them or not, you should either
|
||||
comment out the <filename>COMPATIBLE_MACHINE</filename> statements in all
|
||||
unused <filename>.bbappend</filename> files, or simply remove (or rename) all the files
|
||||
except the one your are using for the build
|
||||
(i.e. <filename>linux-yocto_3.4.bbappend</filename> in this example).</para>
|
||||
<para>If you do not make one of these two adjustments, your machine will be compatible
|
||||
with all the kernel recipes in the <filename>meta-kernel-dev</filename> layer.
|
||||
When your machine is comapatible with all the kernel recipes, the build attempts
|
||||
to build all kernels in the layer.
|
||||
You could end up with build errors blocking your work.</para>
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id='building-and-booting-the-modified-qemu-kernel-image'>
|
||||
<title>Building and Booting the Modified QEMU Kernel Image</title>
|
||||
|
||||
<para>
|
||||
Next, you need to build the modified image.
|
||||
Do the following:
|
||||
<orderedlist>
|
||||
<listitem><para>Your environment should be set up since you previously sourced
|
||||
the <filename>&OE_INIT_FILE;</filename> script.
|
||||
If it isn't, source the script again from <filename>poky</filename>.
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/poky
|
||||
$ source &OE_INIT_FILE;
|
||||
</literallayout>
|
||||
</para></listitem>
|
||||
<listitem><para>Be sure old images are cleaned out by running the
|
||||
<filename>cleanall</filename> BitBake task as follows from your build directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ bitbake -c cleanall linux-yocto
|
||||
</literallayout></para>
|
||||
<para><note>Never remove any files by hand from the <filename>tmp/deploy</filename>
|
||||
directory insided the build directory.
|
||||
Always use the BitBake <filename>cleanall</filename> task to clear
|
||||
out previous builds.</note></para></listitem>
|
||||
<listitem><para>Next, build the kernel image using this command:
|
||||
<literallayout class='monospaced'>
|
||||
$ bitbake -k core-image-minimal
|
||||
</literallayout></para></listitem>
|
||||
<listitem><para>Finally, boot the modified image in the QEMU emulator
|
||||
using this command:
|
||||
<literallayout class='monospaced'>
|
||||
$ runqemu qemux86
|
||||
</literallayout></para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Log into the machine using <filename>root</filename> with no password and then
|
||||
use the following shell command to scroll through the console's boot output.
|
||||
<literallayout class='monospaced'>
|
||||
# dmesg | less
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should see the results of your <filename>printk</filename> statements
|
||||
as part of the output.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</appendix>
|
||||
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
@@ -18,8 +18,7 @@
|
||||
"<ulink url='&YOCTO_DOCS_BSP_URL;#creating-a-new-bsp-layer-using-the-yocto-bsp-script'>Creating a New BSP Layer Using the yocto-bsp Script</ulink>"
|
||||
section in the Yocto Project Board Support Package (BSP) Developer's Guide.
|
||||
For more complete information on how to work with the kernel, see the
|
||||
<ulink url='&YOCTO_DOCS_KERNEL_DEV_URL;'>Yocto Project Linux Kernel
|
||||
Development Manual</ulink>.
|
||||
<ulink url='&YOCTO_DOCS_KERNEL_DEV_URL;'>Yocto Project Linux Kernel Development Manual</ulink>.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>User Application Development:</emphasis>
|
||||
User Application Development covers development of applications that you intend
|
||||
@@ -131,7 +130,7 @@
|
||||
<ulink url='&YOCTO_DOCS_BSP_URL;#creating-a-new-bsp-layer-using-the-yocto-bsp-script'><filename>yocto-bsp</filename></ulink> script</emphasis>:
|
||||
Layers are ideal for
|
||||
isolating and storing work for a given piece of hardware.
|
||||
A layer is really just a location or area in which you place
|
||||
A layer is really just a location or area in which you place
|
||||
the recipes and configurations for your BSP.
|
||||
In fact, a BSP is, in itself, a special type of layer.
|
||||
The simplest way to create a new BSP layer that is compliant with the
|
||||
@@ -156,7 +155,7 @@
|
||||
"<ulink url='&YOCTO_DOCS_BSP_URL;#bsp-layers'>BSP Layers</ulink>" section in the
|
||||
Yocto Project Board Support Package (BSP) Developer's Guide.</para>
|
||||
<note>Four BSPs exist that are part of the
|
||||
Yocto Project release: <filename>genericx86</filename>, <filename>beagleboard</filename>,
|
||||
Yocto Project release: <filename>atom-pc</filename>, <filename>beagleboard</filename>,
|
||||
<filename>mpc8315e</filename>, and <filename>routerstationpro</filename>.
|
||||
The recipes and configurations for these four BSPs are located and dispersed
|
||||
within the <link linkend='source-directory'>Source Directory</link>.
|
||||
@@ -165,7 +164,7 @@
|
||||
Romley, sys940x, Sugar Bay, and tlk exist in their own separate layers
|
||||
within the larger <filename>meta-intel</filename> layer.</note>
|
||||
<para>When you set up a layer for a new BSP, you should follow a standard layout.
|
||||
This layout is described in the
|
||||
This layout is described in the
|
||||
"<ulink url='&YOCTO_DOCS_BSP_URL;#bsp-filelayout'>Example Filesystem Layout</ulink>"
|
||||
section of the Board Support Package (BSP) Development Guide.
|
||||
In the standard layout, you will notice a suggested structure for recipes and
|
||||
@@ -343,10 +342,10 @@
|
||||
If you are working in the kernel all the time, you probably would want
|
||||
to set up your own local Git repository of the kernel tree.
|
||||
If you just need to make some patches to the kernel, you can access
|
||||
temporary kernel source files that were extracted and used
|
||||
temporary kernel source files that were extracted and used
|
||||
during a build.
|
||||
We will just talk about working with the temporary source code.
|
||||
For more information on how to get kernel source code onto your
|
||||
For more information on how to get kernel source code onto your
|
||||
host system, see the
|
||||
"<link linkend='local-kernel-files'>Yocto Project Kernel</link>"
|
||||
bulleted item earlier in the manual.
|
||||
@@ -411,7 +410,7 @@
|
||||
"<link linkend='local-yp-release'>Yocto Project Release</link>" earlier in this manual.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Establish the temporary kernel source files</emphasis>:
|
||||
Temporary kernel source files are kept in the
|
||||
Temporary kernel source files are kept in the
|
||||
<link linkend='build-directory'>Build Directory</link>
|
||||
created by the
|
||||
OpenEmbedded build system when you run BitBake.
|
||||
@@ -474,7 +473,7 @@
|
||||
Application development involves creating an application that you want
|
||||
to run on your target hardware, which is running a kernel image created using the
|
||||
OpenEmbedded build system.
|
||||
The Yocto Project provides an
|
||||
The Yocto Project provides an
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;#adt-intro-section'>Application Development Toolkit (ADT)</ulink>
|
||||
and stand-alone
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;#the-cross-development-toolchain'>cross-development toolchains</ulink>
|
||||
@@ -950,7 +949,7 @@
|
||||
and then click "Next".</para></listitem>
|
||||
<listitem><para>Select the root directory and browse to
|
||||
<filename>~/yocto-eclipse/plugins</filename>.</para></listitem>
|
||||
<listitem><para>Three plug-ins exist:
|
||||
<listitem><para>Three plug-ins exist:
|
||||
<filename>org.yocto.bc.ui</filename>,
|
||||
<filename>org.yocto.sdk.ide</filename>, and
|
||||
<filename>org.yocto.sdk.remotetools</filename>.
|
||||
@@ -980,7 +979,7 @@
|
||||
<para>
|
||||
To start, you need to do the following from within the Eclipse IDE:
|
||||
<itemizedlist>
|
||||
<listitem><para>Choose "Preferences" from the
|
||||
<listitem><para>Choose "Preferences" from the
|
||||
"Windows" menu to display
|
||||
the Preferences Dialog</para></listitem>
|
||||
<listitem><para>Click "Yocto Project ADT"</para></listitem>
|
||||
@@ -1010,7 +1009,7 @@
|
||||
<listitem><para><emphasis>
|
||||
<filename>Build System Derived Toolchain:</filename></emphasis>
|
||||
Select this mode if the cross-toolchain has been installed and built
|
||||
as part of the
|
||||
as part of the
|
||||
<link linkend='build-directory'>Build Directory</link>.
|
||||
When you select <filename>Build system derived toolchain</filename>,
|
||||
you are using the toolchain bundled
|
||||
@@ -1022,7 +1021,7 @@
|
||||
If you are using a stand-alone pre-built toolchain, you should be pointing to the
|
||||
where it is installed.
|
||||
If you used the ADT Installer script and accepted the default
|
||||
installation directory, the toolchain will be installed in
|
||||
installation directory, the toolchain will be installed in
|
||||
the <filename>&YOCTO_ADTPATH_DIR;</filename> directory.
|
||||
Sections "<ulink url='&YOCTO_DOCS_ADT_URL;#configuring-and-running-the-adt-installer-script'>Configuring
|
||||
and Running the ADT Installer Script</ulink>" and
|
||||
@@ -1034,17 +1033,17 @@
|
||||
field is the <link linkend='build-directory'>Build Directory</link>.
|
||||
See the "<ulink url='&YOCTO_DOCS_ADT_URL;#using-the-toolchain-from-within-the-build-tree'>Using
|
||||
BitBake and the Build Directory</ulink>" section in the Yocto Project Application
|
||||
Developer's Guide for information on how to install
|
||||
Developer's Guide for information on how to install
|
||||
the toolchain into the Build Directory.</para></listitem>
|
||||
<listitem><para><emphasis>Specify the Sysroot Location:</emphasis>
|
||||
This location is where the root filesystem for the target hardware resides.
|
||||
If you used the ADT Installer script and accepted the
|
||||
If you used the ADT Installer script and accepted the
|
||||
default installation directory, then the location is
|
||||
<filename>/opt/poky/<release></filename>.
|
||||
Additionally, when you use the ADT Installer script,
|
||||
the same location is used for
|
||||
the QEMU user-space tools and the NFS boot process.</para>
|
||||
<para>If you used either of the other two methods to
|
||||
<para>If you used either of the other two methods to
|
||||
install the toolchain or did not accept the ADT Installer
|
||||
script's default installation directory, then the
|
||||
location of the sysroot filesystem depends on where you separately
|
||||
@@ -1160,7 +1159,7 @@
|
||||
configurations.
|
||||
You can override these settings for a given project by following these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Select "Change Yocto Project Settings" from the
|
||||
<listitem><para>Select "Change Yocto Project Settings" from the
|
||||
"Project" menu.
|
||||
This selection brings up the Yocto Project Settings Dialog
|
||||
and allows you to make changes specific to an individual project.
|
||||
@@ -1170,14 +1169,14 @@
|
||||
Dialog as described earlier
|
||||
in the "<link linkend='configuring-the-eclipse-yocto-plug-in'>Configuring the Eclipse
|
||||
Yocto Plug-in</link>" section.
|
||||
The Yocto Project Settings Dialog allows you to override
|
||||
The Yocto Project Settings Dialog allows you to override
|
||||
those default settings for a given project.</para></listitem>
|
||||
<listitem><para>Make your configurations for the project and click "OK".
|
||||
If you are running the Juno version of Eclipse, you can skip down to the next
|
||||
section where you build the project.
|
||||
If you are not working with Juno, you need to reconfigure the project as
|
||||
described in the next step.</para></listitem>
|
||||
<listitem><para>Select "Reconfigure Project" from the
|
||||
<listitem><para>Select "Reconfigure Project" from the
|
||||
"Project" menu.
|
||||
This selection reconfigures the project by running
|
||||
<filename>autogen.sh</filename> in the workspace for your project.
|
||||
@@ -1197,7 +1196,7 @@
|
||||
<para>
|
||||
To build the project in Juno, right click on the project in the navigator pane and select
|
||||
"Build Project".
|
||||
If you are not running Juno, select "Build Project" from the
|
||||
If you are not running Juno, select "Build Project" from the
|
||||
"Project" menu.
|
||||
The console should update and you can note the cross-compiler you are using.
|
||||
</para>
|
||||
@@ -1209,7 +1208,7 @@
|
||||
<para>
|
||||
To start the QEMU emulator from within Eclipse, follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Expose and select "External Tools" from
|
||||
<listitem><para>Expose and select "External Tools" from
|
||||
the "Run" menu.
|
||||
Your image should appear as a selectable menu item.
|
||||
</para></listitem>
|
||||
@@ -1232,12 +1231,12 @@
|
||||
<title>Deploying and Debugging the Application</title>
|
||||
|
||||
<para>
|
||||
Once the QEMU emulator is running the image, you can deploy
|
||||
your application using the Eclipse IDE and use then use
|
||||
Once the QEMU emulator is running the image, you can deploy
|
||||
your application using the Eclipse IDE and use then use
|
||||
the emulator to perform debugging.
|
||||
Follow these steps to deploy the application.
|
||||
<orderedlist>
|
||||
<listitem><para>Select "Debug Configurations..." from the
|
||||
<listitem><para>Select "Debug Configurations..." from the
|
||||
"Run" menu.</para></listitem>
|
||||
<listitem><para>In the left area, expand <filename>C/C++Remote Application</filename>.</para></listitem>
|
||||
<listitem><para>Locate your project and select it to bring up a new
|
||||
@@ -1258,7 +1257,7 @@
|
||||
determined earlier.</para></listitem>
|
||||
<listitem><para>Click "Finish" to close the
|
||||
New Connections Dialog.</para></listitem>
|
||||
<listitem><para>Use the drop-down menu now in the
|
||||
<listitem><para>Use the drop-down menu now in the
|
||||
"Connection" field and pick the IP Address you entered.
|
||||
</para></listitem>
|
||||
<listitem><para>Click "Run" to bring up a login screen
|
||||
@@ -1315,8 +1314,8 @@
|
||||
display the output.
|
||||
For information on how to use Lttng to trace an application,
|
||||
see <ulink url='http://lttng.org/documentation'></ulink>
|
||||
and the
|
||||
"<ulink url='&YOCTO_DOCS_PROF_URL;#lttng-linux-trace-toolkit-next-generation'>LTTng (Linux Trace Toolkit, next generation)</ulink>"
|
||||
and the
|
||||
"<ulink url='&YOCTO_DOCS_PROF_URL;#lttng-linux-trace-toolkit-next-generation'>LTTng (Linux Trace Toolkit, next generation)</ulink>"
|
||||
section, which is in the Yocto Project Profiling and Tracing Manual.
|
||||
<note>Do not use <filename>Lttng-user space (legacy)</filename> tool.
|
||||
This tool no longer has any upstream support.</note>
|
||||
@@ -1326,18 +1325,18 @@
|
||||
Tracing project.
|
||||
Do the following:
|
||||
<orderedlist>
|
||||
<listitem><para>Select "Open Perspective" from the
|
||||
<listitem><para>Select "Open Perspective" from the
|
||||
"Window" menu and then select "Tracing".</para></listitem>
|
||||
<listitem><para>Click "OK" to change the Eclipse perspective
|
||||
into the Tracing perspective.</para></listitem>
|
||||
<listitem><para>Create a new Tracing project by selecting
|
||||
"Project" from the "File -> New" menu.</para></listitem>
|
||||
<listitem><para>Choose "Tracing Project" from the
|
||||
<listitem><para>Choose "Tracing Project" from the
|
||||
"Tracing" menu.
|
||||
</para></listitem>
|
||||
<listitem><para>Generate your tracing data on the remote target.
|
||||
</para></listitem>
|
||||
<listitem><para>Select "Lttng2.0 ust trace import" from
|
||||
<listitem><para>Select "Lttng2.0 ust trace import" from
|
||||
the "Yocto Project Tools" menu to
|
||||
start the data import process.</para></listitem>
|
||||
<listitem><para>Specify your remote connection name.</para></listitem>
|
||||
@@ -1348,8 +1347,8 @@
|
||||
<listitem><para>Click "OK" to complete the import process.
|
||||
The data is now in the local tracing project you created.</para></listitem>
|
||||
<listitem><para>Right click on the data and then use the menu to
|
||||
Select "Generic CTF Trace" from the
|
||||
"Trace Type... -> Common Trace Format" menu to map
|
||||
Select "Generic CTF Trace" from the
|
||||
"Trace Type... -> Common Trace Format" menu to map
|
||||
the tracing type.</para></listitem>
|
||||
<listitem><para>Right click the mouse and select "Open"
|
||||
to bring up the Eclipse Lttng Trace Viewer so you
|
||||
@@ -1386,7 +1385,7 @@
|
||||
|
||||
<para>
|
||||
Within the Eclipse IDE, you can create a Yocto BitBake Commander project,
|
||||
edit the <link linkend='metadata'>Metadata</link>, and then use
|
||||
edit the <link linkend='metadata'>Metadata</link>, and then use
|
||||
<ulink url='&YOCTO_HOME_URL;/tools-resources/projects/hob'>Hob</ulink> to build a customized
|
||||
image all within one IDE.
|
||||
</para>
|
||||
@@ -1397,16 +1396,16 @@
|
||||
<para>
|
||||
To create a Yocto BitBake Commander project, follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Select "Other" from the
|
||||
"Window -> Open Perspective" menu
|
||||
<listitem><para>Select "Other" from the
|
||||
"Window -> Open Perspective" menu
|
||||
and then choose "Bitbake Commander".</para></listitem>
|
||||
<listitem><para>Click "OK" to change the perspective to
|
||||
Bitbake Commander.</para></listitem>
|
||||
<listitem><para>Select "Project" from the "File -> New"
|
||||
menu to create a new Yocto
|
||||
Bitbake Commander project.</para></listitem>
|
||||
<listitem><para>Choose "New Yocto Project" from the
|
||||
"Yocto Project Bitbake Commander" menu and click
|
||||
<listitem><para>Choose "New Yocto Project" from the
|
||||
"Yocto Project Bitbake Commander" menu and click
|
||||
"Next".</para></listitem>
|
||||
<listitem><para>Enter the Project Name and choose the Project Location.
|
||||
The Yocto project's Metadata files will be put under the directory
|
||||
@@ -1424,7 +1423,7 @@
|
||||
<title>Editing the Metadata</title>
|
||||
|
||||
<para>
|
||||
After you create the Yocto Bitbake Commander project, you can modify the
|
||||
After you create the Yocto Bitbake Commander project, you can modify the
|
||||
<link linkend='metadata'>Metadata</link> files
|
||||
by opening them in the project.
|
||||
When editing recipe files (<filename>.bb</filename> files), you can view BitBake
|
||||
@@ -1436,8 +1435,8 @@
|
||||
To edit the Metadata, follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Select your Yocto Bitbake Commander project.</para></listitem>
|
||||
<listitem><para>Select "BitBake Recipe" from the
|
||||
"File -> New -> Yocto BitBake Commander" menu
|
||||
<listitem><para>Select "BitBake Recipe" from the
|
||||
"File -> New -> Yocto BitBake Commander" menu
|
||||
to open a new recipe wizard.</para></listitem>
|
||||
<listitem><para>Point to your source by filling in the "SRC_URL" field.
|
||||
For example, you can add a recipe to your
|
||||
@@ -1459,13 +1458,13 @@
|
||||
<title>Building and Customizing the Image Using Hob</title>
|
||||
|
||||
<para>
|
||||
To build and customize the image using Hob from within the
|
||||
To build and customize the image using Hob from within the
|
||||
Eclipse IDE, follow these steps:
|
||||
<orderedlist>
|
||||
<listitem><para>Select your Yocto Bitbake Commander project.</para></listitem>
|
||||
<listitem><para>Select "Launch Hob" from the "Project"
|
||||
menu.</para></listitem>
|
||||
<listitem><para>Enter the
|
||||
<listitem><para>Enter the
|
||||
<link linkend='build-directory'>Build Directory</link>
|
||||
where you want to put your final images.</para></listitem>
|
||||
<listitem><para>Click "OK" to launch Hob.</para></listitem>
|
||||
@@ -1508,7 +1507,7 @@
|
||||
support development using actual hardware.
|
||||
For example, the area might contain
|
||||
<filename>.hddimg</filename> files that combine the
|
||||
kernel image with the filesystem, boot loaders, and
|
||||
kernel image with the filesystem, boot loaders, and
|
||||
so forth.
|
||||
Be sure to get the files you need for your particular
|
||||
development process.</para>
|
||||
@@ -1879,9 +1878,9 @@
|
||||
|
||||
<para>
|
||||
For a better understanding of Hob, see the project page at
|
||||
<ulink url='&YOCTO_HOME_URL;/tools-resources/projects/hob'></ulink>
|
||||
<ulink url='&YOCTO_HOME_URL;/tools-resources/projects/hob'></ulink>
|
||||
on the Yocto Project website.
|
||||
If you follow the "Documentation" link from the Hob page, you will
|
||||
If you follow the "Documentation" link from the Hob page, you will
|
||||
find a short introductory training video on Hob.
|
||||
The following lists some features of Hob:
|
||||
<itemizedlist>
|
||||
@@ -1893,9 +1892,9 @@
|
||||
<listitem><para>You can set the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-MACHINE'><filename>MACHINE</filename></ulink>
|
||||
for which you are building the image.</para></listitem>
|
||||
<listitem><para>You can modify various policy settings such as the
|
||||
<listitem><para>You can modify various policy settings such as the
|
||||
package format with which to build,
|
||||
the parallelism BitBake uses, whether or not to build an
|
||||
the parallelism BitBake uses, whether or not to build an
|
||||
external toolchain, and which host to build against.
|
||||
</para></listitem>
|
||||
<listitem><para>You can manage
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
closed, proprietary environment.
|
||||
Additionally, the Yocto Project uses specific tools and constructs as part of its development
|
||||
environment.
|
||||
This chapter specifically addresses open source philosophy, using the
|
||||
This chapter specifically addresses open source philosophy, using the
|
||||
Yocto Project in a team environment, source repositories, Yocto Project
|
||||
terms, licensing, the open source distributed version control system Git,
|
||||
terms, licensing, the open source distributed version control system Git,
|
||||
workflows, bug tracking, and how to submit changes.
|
||||
</para>
|
||||
|
||||
@@ -190,15 +190,15 @@
|
||||
extensive testing while they continue to develop
|
||||
locally using their primary development system.
|
||||
</para></listitem>
|
||||
<listitem><para>Enable the PR Service when package feeds
|
||||
need to be incremental with continually increasing
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-PR'>PR</ulink>
|
||||
<listitem><para>Enable the PR Service when package feeds
|
||||
need to be incremental with continually increasing
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-PR'>PR</ulink>
|
||||
values.
|
||||
Typically, this situation occurs when you use or
|
||||
Typically, this situation occurs when you use or
|
||||
publish package feeds and use a shared state.
|
||||
You should enable the PR Service for all users who
|
||||
use the shared state pool.
|
||||
For more information on the PR Service, see the
|
||||
For more information on the PR Service, see the
|
||||
"<link linkend='working-with-a-pr-service'>Working With a PR Service</link>".
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
@@ -218,9 +218,9 @@
|
||||
Of the SCMs BitBake supports, the
|
||||
Yocto Project team strongly recommends using
|
||||
<link linkend='git'>Git</link>.
|
||||
Git is a distributed system that is easy to backup,
|
||||
allows you to work remotely, and then connects back to the
|
||||
infrastructure.
|
||||
Git is a distributed system that is easy to backup
|
||||
(each checkout is a backup in itself), allows you to work
|
||||
remotely, and then connects back to the infrastructure.
|
||||
<note>
|
||||
For information about BitBake and SCMs, see the
|
||||
BitBake manual located in the
|
||||
@@ -428,8 +428,8 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For any supported release of Yocto Project, you can go to the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink> and
|
||||
For any supported release of Yocto Project, you can go to the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink> and
|
||||
select the "Downloads" tab and get a tarball of the release.
|
||||
You can also go to this site to download any supported BSP tarballs.
|
||||
Unpacking the tarball gives you a hierarchical Source Directory that lets you develop
|
||||
@@ -444,14 +444,14 @@
|
||||
<para>
|
||||
In summary, here is where you can get the project files needed for development:
|
||||
<itemizedlist>
|
||||
<listitem><para id='source-repositories'><emphasis><ulink url='&YOCTO_GIT_URL;/cgit/cgit.cgi'>Source Repositories:</ulink></emphasis>
|
||||
<listitem><para id='source-repositories'>:S<emphasis><ulink url='&YOCTO_GIT_URL;/cgit/cgit.cgi'>Source Repositories:</ulink></emphasis>
|
||||
This area contains IDE Plugins, Matchbox, Poky, Poky Support, Tools, Yocto Linux Kernel, and Yocto
|
||||
Metadata Layers.
|
||||
You can create local copies of Git repositories for each of these areas.</para>
|
||||
<para>
|
||||
<imagedata fileref="figures/source-repos.png" align="center" width="6in" depth="4in" />
|
||||
</para></listitem>
|
||||
<listitem><para><anchor id='index-downloads' /><emphasis><ulink url='&YOCTO_DL_URL;/releases/'>Index of /releases:</ulink></emphasis>
|
||||
<listitem><para><anchor id='index-downloads' />:<emphasis><ulink url='&YOCTO_DL_URL;/releases/'>Index of /releases:</ulink></emphasis>
|
||||
This area contains index releases such as
|
||||
the <trademark class='trade'>Eclipse</trademark>
|
||||
Yocto Plug-in, miscellaneous support, poky, pseudo, installers for cross-development toolchains,
|
||||
@@ -461,7 +461,7 @@
|
||||
<para>
|
||||
<imagedata fileref="figures/index-downloads.png" align="center" width="6in" depth="3.5in" />
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>"Downloads" page for the
|
||||
<listitem><para><emphasis>"Downloads" page for the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink>:</emphasis>
|
||||
Access this page by going to the website and then selecting
|
||||
the "Downloads" tab.
|
||||
@@ -489,7 +489,7 @@
|
||||
Append files are known as BitBake append files and <filename>.bbappend</filename> files.
|
||||
The OpenEmbedded build system expects every append file to have a corresponding
|
||||
recipe (<filename>.bb</filename>) file.
|
||||
Furthermore, the append file and corresponding recipe file
|
||||
Furthermore, the append file and corresponding recipe file
|
||||
must use the same root filename.
|
||||
The filenames can differ only in the file type suffix used (e.g.
|
||||
<filename>formfactor_0.0.bb</filename> and <filename>formfactor_0.0.bbappend</filename>).
|
||||
@@ -509,9 +509,7 @@
|
||||
This term refers to the area used by the OpenEmbedded build system for builds.
|
||||
The area is created when you <filename>source</filename> the setup
|
||||
environment script that is found in the Source Directory
|
||||
(i.e. <ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>
|
||||
or
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#structure-memres-core-script'><filename>oe-init-build-env-memres</filename></ulink>).
|
||||
(i.e. <ulink url='&YOCTO_DOCS_REF_URL;#structure-core-script'><filename>&OE_INIT_FILE;</filename></ulink>).
|
||||
The <ulink url='&YOCTO_DOCS_REF_URL;#var-TOPDIR'><filename>TOPDIR</filename></ulink>
|
||||
variable points to the Build Directory.</para>
|
||||
|
||||
@@ -568,40 +566,59 @@
|
||||
</para></listitem>
|
||||
<listitem><para id='cross-development-toolchain'>
|
||||
<emphasis>Cross-Development Toolchain:</emphasis>
|
||||
In general, a cross-development toolchain is a collection of
|
||||
software development tools and utilities that run on one
|
||||
architecture and allow you to develop software for a
|
||||
different, or targeted, architecture.
|
||||
These toolchains contain cross-compilers, linkers, and
|
||||
debuggers that are specific to the target architecture.
|
||||
</para>
|
||||
|
||||
<para>The Yocto Project supports two different cross-development
|
||||
toolchains:
|
||||
<itemizedlist>
|
||||
<listitem><para>A toolchain only used by and within
|
||||
BitBake when building an image for a target
|
||||
architecture.</para></listitem>
|
||||
<listitem><para>A relocatable toolchain used outside of
|
||||
BitBake by developers when developing applications
|
||||
that will run on a targeted device.
|
||||
Sometimes this relocatable cross-development
|
||||
toolchain is referred to as the meta-toolchain.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Creation of these toolchains is simple and automated.
|
||||
For information on toolchain concepts as they apply to the
|
||||
Yocto Project, see the
|
||||
"<ulink url='&YOCTO_DOCS_REF_URL;#cross-development-toolchain-generation'>Cross-Development Toolchain Generation</ulink>"
|
||||
section in the Yocto Project Reference Manual.
|
||||
You can also find more information on using the
|
||||
relocatable toolchain in the
|
||||
<ulink url='&YOCTO_DOCS_ADT_URL;'>Yocto Project
|
||||
Application Developer's Guide</ulink>.
|
||||
</para></listitem>
|
||||
A collection of software development
|
||||
tools and utilities that allow you to develop software for
|
||||
targeted architectures.
|
||||
This toolchain contains cross-compilers, linkers, and debuggers
|
||||
that are specific to an architecture.
|
||||
You can use the OpenEmbedded build system to build a
|
||||
cross-development toolchain installer that, when run, installs
|
||||
the toolchain that contains the development tools you
|
||||
need to cross-compile and test your software.
|
||||
The Yocto Project ships with images that contain installers for
|
||||
toolchains for supported architectures as well.
|
||||
Sometimes this toolchain is referred to as the
|
||||
meta-toolchain.</para>
|
||||
<para>Following is a list of toolchain recipes with brief
|
||||
descriptions of each:
|
||||
<itemizedlist>
|
||||
<listitem><para><filename>gcc-cross-initial</filename>:
|
||||
The initial compiler needed to bootstrap the toolchain
|
||||
that runs on the host and is used to build software
|
||||
for the target.
|
||||
This tool is a "native" package.</para></listitem>
|
||||
<listitem><para><filename>gcc-cross-intermediate</filename>:
|
||||
The second stage of the bootstrap process that runs
|
||||
on the host and builds software for the target.
|
||||
This tool is a "native" package.</para></listitem>
|
||||
<listitem><para><filename>gcc-cross</filename>:
|
||||
The the final stage of the bootstrap process that
|
||||
results in the cross compiler that runs on the host
|
||||
and builds software for the target.
|
||||
If you are replacing the cross compiler toolchain
|
||||
with a custom version, this is what you must replace.
|
||||
This tool is a "native" package.</para></listitem>
|
||||
<listitem><para><filename>gcc-runtime</filename>:
|
||||
Runtime libraries from the toolchain bootstrapping
|
||||
process.
|
||||
This tool produces a binary for the target.
|
||||
</para></listitem>
|
||||
<listitem><para><filename>gcc-crosssdk-initial/intermediate</filename>:
|
||||
Stage 1 and 2 of the a cross compiler that runs on the
|
||||
host and builds for the SDK.
|
||||
Often the SDK is not the same target as the host.
|
||||
This tool is a "native" binary.</para></listitem>
|
||||
<listitem><para><filename>gcc-crosssdk</filename>:
|
||||
The final stage of the SDK compiler.
|
||||
This tool is a "native" binary.
|
||||
The tool runs on the host and builds for the SDK.
|
||||
</para></listitem>
|
||||
<listitem><para><filename>gcc-cross-canadian</filename>:
|
||||
The compiler that runs on the SDK machine and is
|
||||
included with the SDK that builds software for the
|
||||
target.
|
||||
This tool is a "nativesdk" package.</para></listitem>
|
||||
</itemizedlist></para></listitem>
|
||||
<listitem><para><emphasis>Image:</emphasis> An image is the result produced when
|
||||
BitBake processes a given collection of recipes and related Metadata.
|
||||
Images are the binary output that run on specific hardware or QEMU
|
||||
@@ -613,12 +630,7 @@
|
||||
a BSP, or an application stack.
|
||||
For a discussion on BSP Layers, see the
|
||||
"<ulink url='&YOCTO_DOCS_BSP_URL;#bsp-layers'>BSP Layers</ulink>"
|
||||
section in the Yocto Project Board Support Packages (BSP)
|
||||
Developer's Guide.</para></listitem>
|
||||
<listitem><para id='meta-toochain'><emphasis>Meta-Toolchain:</emphasis>
|
||||
A term sometimes used for
|
||||
<link linkend='cross-development-toolchain'>Cross-Development Toolchain</link>.
|
||||
</para></listitem>
|
||||
section in the Yocto Project Board Support Packages (BSP) Developer's Guide.</para></listitem>
|
||||
<listitem><para id='metadata'><emphasis>Metadata:</emphasis>
|
||||
The files that BitBake parses when building an image.
|
||||
In general, Metadata includes recipes, classes, and
|
||||
@@ -629,7 +641,7 @@
|
||||
</para></listitem>
|
||||
<listitem><para id='oe-core'><emphasis>OE-Core:</emphasis> A core set of Metadata originating
|
||||
with OpenEmbedded (OE) that is shared between OE and the Yocto Project.
|
||||
This Metadata is found in the <filename>meta</filename> directory of the
|
||||
This Metadata is found in the <filename>meta</filename> directory of the
|
||||
<link linkend='source-directory'>Source Directory</link>.</para></listitem>
|
||||
<listitem><para><emphasis>Package:</emphasis> In the context of the Yocto Project,
|
||||
this term refers to the packaged output from a baked recipe.
|
||||
@@ -764,7 +776,7 @@
|
||||
<para>
|
||||
When you build an image using the Yocto Project, the build process uses a
|
||||
known list of licenses to ensure compliance.
|
||||
You can find this list in the
|
||||
You can find this list in the
|
||||
<link linkend='source-directory'>Source Directory</link> at
|
||||
<filename>meta/files/common-licenses</filename>.
|
||||
Once the build completes, the list of all licenses found and used during that build are
|
||||
@@ -857,7 +869,7 @@
|
||||
When you clone a Git repository, you end up with an identical copy of the
|
||||
repository on your development system.
|
||||
Once you have a local copy of a repository, you can take steps to develop locally.
|
||||
For examples on how to clone Git repositories, see the
|
||||
For examples on how to clone Git repositories, see the
|
||||
"<link linkend='getting-setup'>Getting Set Up</link>" section.
|
||||
</para>
|
||||
|
||||
@@ -899,14 +911,14 @@
|
||||
$ cd poky
|
||||
$ git checkout -b &DISTRO_NAME; origin/&DISTRO_NAME;
|
||||
</literallayout>
|
||||
In this example, the name of the top-level directory of your local
|
||||
<link linkend='source-directory'>Source Directory</link>
|
||||
In this example, the name of the top-level directory of your local
|
||||
<link linkend='source-directory'>Source Directory</link>
|
||||
is <filename>poky</filename>,
|
||||
and the name of that local working area (local branch) you just
|
||||
and the name of that local working area (local branch) you just
|
||||
created and checked out is <filename>&DISTRO_NAME;</filename>.
|
||||
The files in your local repository now reflect the same files that
|
||||
are in the <filename>&DISTRO_NAME;</filename> development
|
||||
branch of the Yocto Project's <filename>poky</filename>
|
||||
The files in your local repository now reflect the same files that
|
||||
are in the <filename>&DISTRO_NAME;</filename> development
|
||||
branch of the Yocto Project's <filename>poky</filename>
|
||||
upstream repository.
|
||||
It is important to understand that when you create and checkout a
|
||||
local working branch based on a branch name,
|
||||
@@ -1025,10 +1037,10 @@
|
||||
repository and places it in your local Git repository.
|
||||
You use this command to make sure you are synchronized with the repository
|
||||
from which you are basing changes (.e.g. the master branch).</para></listitem>
|
||||
<listitem><para><emphasis><filename>git push</filename>:</emphasis>
|
||||
Sends all your committed local changes to an upstream Git
|
||||
<listitem><para><emphasis><filename>git push</filename>:</emphasis>
|
||||
Sends all your committed local changes to an upstream Git
|
||||
repository (e.g. a contribution repository).
|
||||
The maintainer of the project draws from these repositories
|
||||
The maintainer of the project draws from these repositories
|
||||
when adding changes to the project’s master repository or
|
||||
other development branch.
|
||||
</para></listitem>
|
||||
@@ -1076,8 +1088,8 @@
|
||||
The "master" branch is the “upstream” repository where the final builds of the project occur.
|
||||
The maintainer is responsible for allowing changes in from other developers and for
|
||||
organizing the underlying branch structure to reflect release strategies and so forth.
|
||||
<note>For information on finding out who is responsible (maintains)
|
||||
for a particular area of code, see the
|
||||
<note>For information on finding out who is responsible (maintains)
|
||||
for a particular area of code, see the
|
||||
"<link linkend='how-to-submit-a-change'>How to Submit a Change</link>"
|
||||
section.
|
||||
</note>
|
||||
@@ -1148,8 +1160,8 @@
|
||||
You can name these branches anything you like.
|
||||
It is helpful to give them names associated with the particular feature or change
|
||||
on which you are working.
|
||||
Once you are done with a feature or change and have merged it
|
||||
into your local master branch, simply discard the temporary
|
||||
Once you are done with a feature or change and have merged it
|
||||
into your local master branch, simply discard the temporary
|
||||
branch.</para></listitem>
|
||||
<listitem><para><emphasis>Merge Changes:</emphasis> The <filename>git merge</filename>
|
||||
command allows you to take the
|
||||
@@ -1177,9 +1189,9 @@
|
||||
<filename>send-pull-request</filename> that ship with the release to facilitate this
|
||||
workflow.
|
||||
You can find these scripts in the <filename>scripts</filename>
|
||||
folder of the
|
||||
folder of the
|
||||
<link linkend='source-directory'>Source Directory</link>.
|
||||
For information on how to use these scripts, see the
|
||||
For information on how to use these scripts, see the
|
||||
"<link linkend='pushing-a-change-upstream'>Using Scripts to Push a Change Upstream and Request a Pull</link>" section.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Patch Workflow:</emphasis> This workflow allows you to notify the
|
||||
@@ -1187,7 +1199,7 @@
|
||||
for the "master" branch of the Git repository.
|
||||
To send this type of change, you format the patch and then send the email using the Git commands
|
||||
<filename>git format-patch</filename> and <filename>git send-email</filename>.
|
||||
For information on how to use these scripts, see the
|
||||
For information on how to use these scripts, see the
|
||||
"<link linkend='how-to-submit-a-change'>How to Submit a Change</link>"
|
||||
section.
|
||||
</para></listitem>
|
||||
@@ -1239,10 +1251,10 @@
|
||||
<listitem><para>Provide a detailed description of the issue.
|
||||
You should provide as much detail as you can about the context, behavior, output,
|
||||
and so forth that surrounds the issue.
|
||||
You can even attach supporting files for output from logs by
|
||||
You can even attach supporting files for output from logs by
|
||||
using the "Add an attachment" button.</para></listitem>
|
||||
<listitem><para>Be sure to copy the appropriate people in the
|
||||
"CC List" for the bug.
|
||||
<listitem><para>Be sure to copy the appropriate people in the
|
||||
"CC List" for the bug.
|
||||
See the "<link linkend='how-to-submit-a-change'>How to Submit a Change</link>"
|
||||
section for information about finding out who is responsible
|
||||
for code.</para></listitem>
|
||||
@@ -1269,37 +1281,37 @@
|
||||
or notifying:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Maintenance File:</emphasis>
|
||||
Examine the <filename>maintainers.inc</filename> file, which is
|
||||
located in the
|
||||
Examine the <filename>maintainers.inc</filename> file, which is
|
||||
located in the
|
||||
<link linkend='source-directory'>Source Directory</link>
|
||||
at <filename>meta-yocto/conf/distro/include</filename>, to
|
||||
at <filename>meta-yocto/conf/distro/include</filename>, to
|
||||
see who is responsible for code.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Board Support Package (BSP) README Files:</emphasis>
|
||||
For BSP maintainers of supported BSPs, you can examine
|
||||
For BSP maintainers of supported BSPs, you can examine
|
||||
individual BSP <filename>README</filename> files.
|
||||
Alternatively, you can examine the
|
||||
<filename>MAINTAINERS</filename> file, which is found in the
|
||||
Alternatively, you can examine the
|
||||
<filename>MAINTAINERS</filename> file, which is found in the
|
||||
<filename>meta-intel</filename>, for a list of all supported
|
||||
BSP maintainers.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Search by File:</emphasis>
|
||||
Using <link linkend='git'>Git</link>, you can enter the
|
||||
following command to bring up a short list of all commits
|
||||
Using <link linkend='git'>Git</link>, you can enter the
|
||||
following command to bring up a short list of all commits
|
||||
against a specific file:
|
||||
<literallayout class='monospaced'>
|
||||
git shortlog -- <filename>
|
||||
</literallayout>
|
||||
Just provide the name of the file for which you are interested.
|
||||
The information returned is not ordered by history but does
|
||||
include a list of all committers grouped by name.
|
||||
The information returned is not ordered by history but does
|
||||
include a list of all committers grouped by name.
|
||||
From the list, you can see who is responsible for the bulk of
|
||||
the changes against the file.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<para>
|
||||
For a list of the Yocto Project and related mailing lists, see the
|
||||
"<ulink url='&YOCTO_DOCS_REF_URL;#resources-mailinglist'>Mailing lists</ulink>" section in
|
||||
the Yocto Project Reference Manual.
|
||||
@@ -1308,7 +1320,7 @@
|
||||
<para>
|
||||
Here is some guidance on which mailing list to use for what type of change:
|
||||
<itemizedlist>
|
||||
<listitem><para>For changes to the core
|
||||
<listitem><para>For changes to the core
|
||||
<link linkend='metadata'>Metadata</link>, send your patch to the
|
||||
<ulink url='&OE_LISTS_URL;/listinfo/openembedded-core'>openembedded-core</ulink> mailing list.
|
||||
For example, a change to anything under the <filename>meta</filename> or
|
||||
@@ -1407,7 +1419,7 @@
|
||||
references - any commit that addresses a specific bug should include the
|
||||
bug ID in the description (typically at the beginning) as follows:
|
||||
<literallayout class='monospaced'>
|
||||
[YOCTO #<bug-id>]
|
||||
Fixes YOCTO #<bug-id>
|
||||
|
||||
<detailed description of change>
|
||||
</literallayout></para></listitem>
|
||||
@@ -1423,7 +1435,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The next two sections describe general instructions for both pushing
|
||||
The next two sections describe general instructions for both pushing
|
||||
changes upstream and for submitting changes as patches.
|
||||
</para>
|
||||
|
||||
@@ -1481,7 +1493,7 @@
|
||||
Depending on the components changed, you need to submit the email to a specific
|
||||
mailing list.
|
||||
For some guidance on which mailing list to use, see the list in the
|
||||
"<link linkend='how-to-submit-a-change'>How to Submit a Change</link>"
|
||||
"<link linkend='how-to-submit-a-change'>How to Submit a Change</link>"
|
||||
section.
|
||||
For a description of the available mailing lists, see the
|
||||
"<ulink url='&YOCTO_DOCS_REF_URL;#resources-mailinglist'>Mailing Lists</ulink>"
|
||||
@@ -1509,8 +1521,8 @@
|
||||
To format commits, use the <filename>git format-patch</filename> command.
|
||||
When you provide the command, you must include a revision list or a number of patches
|
||||
as part of the command.
|
||||
For example, either of these two commands takes your most
|
||||
recent single commit and formats it as an email message in
|
||||
For example, either of these two commands takes your most
|
||||
recent single commit and formats it as an email message in
|
||||
the current directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ git format-patch -1
|
||||
@@ -1522,7 +1534,7 @@
|
||||
<para>After the command is run, the current directory contains a
|
||||
numbered <filename>.patch</filename> file for the commit.</para>
|
||||
<para>If you provide several commits as part of the command,
|
||||
the <filename>git format-patch</filename> command produces a
|
||||
the <filename>git format-patch</filename> command produces a
|
||||
series of numbered files in the current directory – one for each commit.
|
||||
If you have more than one patch, you should also use the
|
||||
<filename>--cover</filename> option with the command, which generates a
|
||||
@@ -1539,7 +1551,7 @@
|
||||
<filename>git send-email</filename> command.
|
||||
<note>In order to use <filename>git send-email</filename>, you must have the
|
||||
the proper Git packages installed.
|
||||
For Ubuntu, Debian, and Fedora the package is <filename>git-email</filename>.</note></para>
|
||||
For Ubuntu and Fedora the package is <filename>git-email</filename>.</note></para>
|
||||
<para>The <filename>git send-email</filename> command sends email by using a local
|
||||
or remote Mail Transport Agent (MTA) such as
|
||||
<filename>msmtp</filename>, <filename>sendmail</filename>, or through a direct
|
||||
@@ -1559,7 +1571,7 @@
|
||||
The command also has several options that let you
|
||||
specify recipients and perform further editing of the email message.
|
||||
For information on how to use the <filename>git send-email</filename> command,
|
||||
see <filename>GIT-SEND-EMAIL(1)</filename> displayed using
|
||||
see <filename>GIT-SEND-EMAIL(1)</filename> displayed using
|
||||
the <filename>man git-send-email</filename> command.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
<para>
|
||||
The Yocto Project is an open-source collaboration project focused on embedded Linux development.
|
||||
The project currently provides a build system that is
|
||||
The project currently provides a build system, which is
|
||||
referred to as the OpenEmbedded build system in the Yocto Project documentation.
|
||||
The Yocto Project provides various ancillary tools for the embedded developer
|
||||
The Yocto Project provides various ancillary tools suitable for the embedded developer
|
||||
and also features the Sato reference User Interface, which is optimized for
|
||||
stylus driven, low-resolution screens.
|
||||
</para>
|
||||
@@ -72,10 +72,10 @@
|
||||
<listitem><para><emphasis>Packages:</emphasis> The OpenEmbedded build system
|
||||
requires certain packages exist on your development system (e.g. Python 2.6 or 2.7).
|
||||
See "<ulink url='&YOCTO_DOCS_QS_URL;#packages'>The Packages</ulink>"
|
||||
section in the Yocto Project Quick Start and the
|
||||
section in the Yocto Project Quick Start and the
|
||||
"<ulink url='&YOCTO_DOCS_REF_URL;#required-packages-for-the-host-development-system'>Required Packages for the Host Development System</ulink>"
|
||||
section in the Yocto Project Reference Manual for the exact
|
||||
package requirements and the installation commands to install
|
||||
section in the Yocto Project Reference Manual for the exact
|
||||
package requirements and the installation commands to install
|
||||
them for the supported distributions.
|
||||
</para></listitem>
|
||||
<listitem id='local-yp-release'><para><emphasis>Yocto Project Release:</emphasis>
|
||||
@@ -88,14 +88,14 @@
|
||||
hierarchical set of files as the "Source Directory."
|
||||
</note>
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Tarball Extraction:</emphasis>
|
||||
If you are not going to contribute back into the Yocto
|
||||
Project, you can simply go to the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink>,
|
||||
<listitem><para><emphasis>Tarball Extraction:</emphasis>
|
||||
If you are not going to contribute back into the Yocto
|
||||
Project, you can simply go to the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink>,
|
||||
select the "Downloads" tab, and choose what you want.
|
||||
Once you have the tarball, just extract it into a
|
||||
Once you have the tarball, just extract it into a
|
||||
directory of your choice.</para>
|
||||
<para>For example, the following command extracts the
|
||||
<para>For example, the following command extracts the
|
||||
Yocto Project &DISTRO; release tarball
|
||||
into the current working directory and sets up the local Source Directory
|
||||
with a top-level folder named <filename>&YOCTO_POKY;</filename>:
|
||||
@@ -126,7 +126,7 @@
|
||||
remote: Compressing objects: 100% (47428/47428), done.
|
||||
remote: Total 183981 (delta 132271), reused 183703 (delta 132044)
|
||||
Receiving objects: 100% (183981/183981), 89.71 MiB | 2.93 MiB/s, done.
|
||||
Resolving deltas: 100% (132271/132271), done.
|
||||
Resolving deltas: 100% (132271/132271), done.
|
||||
</literallayout></para>
|
||||
<para>For another example of how to set up your own local Git repositories, see this
|
||||
<ulink url='&YOCTO_WIKI_URL;/wiki/Transcript:_from_git_checkout_to_meta-intel_BSP'>
|
||||
@@ -168,9 +168,9 @@
|
||||
Cloning into 'my-linux-yocto-3.8-work'...
|
||||
done.
|
||||
</literallayout></para></listitem>
|
||||
<listitem id='meta-yocto-kernel-extras-repo'><para><emphasis>
|
||||
The <filename>meta-yocto-kernel-extras</filename> Git Repository</emphasis>:
|
||||
The <filename>meta-yocto-kernel-extras</filename> Git repository contains Metadata needed
|
||||
<listitem id='poky-extras-repo'><para><emphasis>
|
||||
The <filename>poky-extras</filename> Git Repository</emphasis>:
|
||||
The <filename>poky-extras</filename> Git repository contains Metadata needed
|
||||
only if you are modifying and building the kernel image.
|
||||
In particular, it contains the kernel BitBake append (<filename>.bbappend</filename>)
|
||||
files that you
|
||||
@@ -178,17 +178,17 @@
|
||||
image.
|
||||
Pointing to these local files is much more efficient than requiring a download of the
|
||||
kernel's source files from upstream each time you make changes to the kernel.</para>
|
||||
<para>You can find the <filename>meta-yocto-kernel-extras</filename> Git Repository in the
|
||||
<para>You can find the <filename>poky-extras</filename> Git Repository in the
|
||||
"Yocto Metadata Layers" area of the Yocto Project Source Repositories at
|
||||
<ulink url='&YOCTO_GIT_URL;/cgit.cgi'></ulink>.
|
||||
It is good practice to create this Git repository inside the Source Directory.</para>
|
||||
<para>Following is an example that creates the <filename>meta-yocto-kernel-extras</filename> Git
|
||||
<para>Following is an example that creates the <filename>poky-extras</filename> Git
|
||||
repository inside the Source Directory, which is named <filename>poky</filename>
|
||||
in this case:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/poky
|
||||
$ git clone git://git.yoctoproject.org/meta-yocto-kernel-extras meta-yocto-kernel-extras
|
||||
Cloning into 'meta-yocto-kernel-extras'...
|
||||
$ git clone git://git.yoctoproject.org/poky-extras poky-extras
|
||||
Cloning into 'poky-extras'...
|
||||
remote: Counting objects: 690, done.
|
||||
remote: Compressing objects: 100% (431/431), done.
|
||||
remote: Total 690 (delta 238), reused 690 (delta 238)
|
||||
@@ -223,10 +223,10 @@
|
||||
information on BSP Layers.
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Tarball Extraction:</emphasis> You can download any released
|
||||
BSP tarball from the same "Downloads" page of the
|
||||
BSP tarball from the same "Downloads" page of the
|
||||
<ulink url='&YOCTO_HOME_URL;'>Yocto Project Website</ulink>
|
||||
to get the Yocto Project release.
|
||||
Once on the "Download" page, look for "BSP" under the
|
||||
Once on the "Download" page, look for "BSP" under the
|
||||
"Type" heading.</para>
|
||||
<para>Once you have the tarball, just extract it into a directory of your choice.
|
||||
Again, this method just produces a snapshot of the BSP layer in the form
|
||||
|
||||
@@ -52,9 +52,29 @@
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
<revnumber>1.4.1</revnumber>
|
||||
<date>June 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.2</revnumber>
|
||||
<date>August 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.3</revnumber>
|
||||
<date>March 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.4</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.5</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.5 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
|
||||
@@ -88,14 +88,9 @@
|
||||
<literallayout class='monospaced'>
|
||||
<your-layer>/recipes-kernel/linux/linux-yocto_3.4.bbappend
|
||||
</literallayout>
|
||||
The append file should initially extend the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESPATH'><filename>FILESPATH</filename></ulink>
|
||||
search path by prepending the directory that contains your
|
||||
files to the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'><filename>FILESEXTRAPATHS</filename></ulink>
|
||||
variable as follows:
|
||||
The append file should initially contain the following text:
|
||||
<literallayout class='monospaced'>
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}"
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'>FILESEXTRAPATHS</ulink> := "${THISDIR}/${PN}"
|
||||
</literallayout>
|
||||
The path <filename>${</filename><ulink url='&YOCTO_DOCS_REF_URL;#var-THISDIR'><filename>THISDIR</filename></ulink><filename>}/${</filename><ulink url='&YOCTO_DOCS_REF_URL;#var-PN'><filename>PN</filename></ulink><filename>}</filename>
|
||||
expands to "linux-yocto" in the current directory for this
|
||||
@@ -156,13 +151,13 @@
|
||||
<para>
|
||||
You can make wholesale or incremental changes to the Linux
|
||||
kernel <filename>.config</filename> file by including a
|
||||
<filename>defconfig</filename> and by specifying
|
||||
<filename>defconfig</filename> or by specifying
|
||||
configuration fragments in the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-SRC_URI'><filename>SRC_URI</filename></ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you have a final Linux kernel <filename>.config</filename>
|
||||
If you have a complete Linux kernel <filename>.config</filename>
|
||||
file you want to use, copy it to a directory named
|
||||
<filename>files</filename>, which must be in
|
||||
your layer's <filename>recipes-kernel/linux</filename>
|
||||
@@ -173,7 +168,8 @@
|
||||
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
|
||||
SRC_URI += "file://defconfig"
|
||||
</literallayout>
|
||||
The <filename>SRC_URI</filename> tells the build system how to
|
||||
The
|
||||
<filename>SRC_URI</filename> tells the build system how to
|
||||
search for the file, while the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-FILESEXTRAPATHS'><filename>FILESEXTRAPATHS</filename></ulink>
|
||||
extends the
|
||||
@@ -183,26 +179,13 @@
|
||||
configuration changes.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
The build system applies the configurations from the
|
||||
<filename>.config</filename> file before applying any
|
||||
subsequent configuration fragments.
|
||||
The final kernel configuration is a combination of the
|
||||
configurations in the <filename>.config</filename> file and
|
||||
any configuration fragments you provide.
|
||||
You need to realize that if you have any configuration
|
||||
fragments, the build system applies these on top of and
|
||||
after applying the existing <filename>.config</filename>
|
||||
file configurations.
|
||||
</note>
|
||||
|
||||
<para>
|
||||
Generally speaking, the preferred approach is to determine the
|
||||
incremental change you want to make and add that as a
|
||||
configuration fragment.
|
||||
For example, if you want to add support for a basic serial
|
||||
console, create a file named <filename>8250.cfg</filename> in
|
||||
the <filename>files</filename> directory with the following
|
||||
console, create a file named <filename>8250.cfg</filename> in the
|
||||
<filename>files</filename> directory with the following
|
||||
content (without indentation):
|
||||
<literallayout class='monospaced'>
|
||||
CONFIG_SERIAL_8250=y
|
||||
|
||||
@@ -37,9 +37,29 @@
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.5</revnumber>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.5 Release.</revremark>
|
||||
<revnumber>1.4.1</revnumber>
|
||||
<date>June 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.2</revnumber>
|
||||
<date>August 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.3</revnumber>
|
||||
<date>March 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.4</revnumber>
|
||||
<date>May 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.4 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4.5</revnumber>
|
||||
<date>July 2014</date>
|
||||
<revremark>Released with the Yocto Project 1.4.5 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
|
||||
BIN
documentation/kernel-manual/figures/kernel-architecture-overview.png
Executable file
|
After Width: | Height: | Size: 40 KiB |
BIN
documentation/kernel-manual/figures/kernel-title.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
392
documentation/kernel-manual/kernel-concepts.xml
Normal file
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[<!ENTITY % poky SYSTEM "../poky.ent"> %poky; ] >
|
||||
|
||||
<chapter id='kernel-concepts'>
|
||||
|
||||
<title>Yocto Project Kernel Concepts</title>
|
||||
|
||||
<section id='concepts-org'>
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
This chapter provides conceptual information about the kernel:
|
||||
<itemizedlist>
|
||||
<listitem><para>Kernel Goals</para></listitem>
|
||||
<listitem><para>Kernel Development and Maintenance Overview</para></listitem>
|
||||
<listitem><para>Kernel Architecture</para></listitem>
|
||||
<listitem><para>Kernel Tools</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='kernel-goals'>
|
||||
<title>Kernel Goals</title>
|
||||
<para>
|
||||
The complexity of embedded kernel design has increased dramatically.
|
||||
Whether it is managing multiple implementations of a particular feature or tuning and
|
||||
optimizing board specific features, both flexibility and maintainability are key concerns.
|
||||
The Linux kernels available through the Yocto Project are presented with the embedded
|
||||
developer's needs in mind and have evolved to assist in these key concerns.
|
||||
For example, prior methods such as applying hundreds of patches to an extracted
|
||||
tarball have been replaced with proven techniques that allow easy inspection,
|
||||
bisection and analysis of changes.
|
||||
Application of these techniques also creates a platform for performing integration and
|
||||
collaboration with the thousands of upstream development projects.
|
||||
</para>
|
||||
<para>
|
||||
With all these considerations in mind, the Yocto Project's kernel and development team
|
||||
strives to attain these goals:
|
||||
<itemizedlist>
|
||||
<listitem><para>Allow the end user to leverage community best practices to seamlessly
|
||||
manage the development, build and debug cycles.</para></listitem>
|
||||
<listitem><para>Create a platform for performing integration and collaboration with the
|
||||
thousands of upstream development projects that exist.</para></listitem>
|
||||
<listitem><para>Provide mechanisms that support many different work flows, front-ends and
|
||||
management techniques.</para></listitem>
|
||||
<listitem><para>Deliver the most up-to-date kernel possible while still ensuring that
|
||||
the baseline kernel is the most stable official release.</para></listitem>
|
||||
<listitem><para>Include major technological features as part of the Yocto Project's
|
||||
upward revision strategy.</para></listitem>
|
||||
<listitem><para>Present a kernel Git repository that, similar to the upstream
|
||||
<filename>kernel.org</filename> tree,
|
||||
has a clear and continuous history.</para></listitem>
|
||||
<listitem><para>Deliver a key set of supported kernel types, where each type is tailored
|
||||
to meet a specific use (e.g. networking, consumer, devices, and so forth).</para></listitem>
|
||||
<listitem><para>Employ a Git branching strategy that, from a developer's point of view,
|
||||
results in a linear path from the baseline <filename>kernel.org</filename>,
|
||||
through a select group of features and
|
||||
ends with their BSP-specific commits.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='kernel-big-picture'>
|
||||
<title>Yocto Project Kernel Development and Maintenance Overview</title>
|
||||
<para>
|
||||
Kernels available through the Yocto Project, like other kernels, are based off the Linux
|
||||
kernel releases from <ulink url='http://www.kernel.org'></ulink>.
|
||||
At the beginning of a major development cycle, the Yocto Project team
|
||||
chooses its kernel based on factors such as release timing, the anticipated release
|
||||
timing of final upstream <filename>kernel.org</filename> versions, and Yocto Project
|
||||
feature requirements.
|
||||
Typically, the kernel chosen is in the
|
||||
final stages of development by the community.
|
||||
In other words, the kernel is in the release
|
||||
candidate or "rc" phase and not yet a final release.
|
||||
But, by being in the final stages of external development, the team knows that the
|
||||
<filename>kernel.org</filename> final release will clearly be within the early stages of
|
||||
the Yocto Project development window.
|
||||
</para>
|
||||
<para>
|
||||
This balance allows the team to deliver the most up-to-date kernel
|
||||
possible, while still ensuring that the team has a stable official release for
|
||||
the baseline Linux kernel version.
|
||||
</para>
|
||||
<para>
|
||||
The ultimate source for kernels available through the Yocto Project are released kernels
|
||||
from <filename>kernel.org</filename>.
|
||||
In addition to a foundational kernel from <filename>kernel.org</filename>, the
|
||||
kernels available contain a mix of important new mainline
|
||||
developments, non-mainline developments (when there is no alternative),
|
||||
Board Support Package (BSP) developments,
|
||||
and custom features.
|
||||
These additions result in a commercially released Yocto Project Linux kernel that caters
|
||||
to specific embedded designer needs for targeted hardware.
|
||||
</para>
|
||||
<para>
|
||||
Once a kernel is officially released, the Yocto Project team goes into
|
||||
their next development cycle, or upward revision (uprev) cycle, while still
|
||||
continuing maintenance on the released kernel.
|
||||
It is important to note that the most sustainable and stable way
|
||||
to include feature development upstream is through a kernel uprev process.
|
||||
Back-porting hundreds of individual fixes and minor features from various
|
||||
kernel versions is not sustainable and can easily compromise quality.
|
||||
</para>
|
||||
<para>
|
||||
During the uprev cycle, the Yocto Project team uses an ongoing analysis of
|
||||
kernel development, BSP support, and release timing to select the best
|
||||
possible <filename>kernel.org</filename> version.
|
||||
The team continually monitors community kernel
|
||||
development to look for significant features of interest.
|
||||
The team does consider back-porting large features if they have a significant advantage.
|
||||
User or community demand can also trigger a back-port or creation of new
|
||||
functionality in the Yocto Project baseline kernel during the uprev cycle.
|
||||
</para>
|
||||
<para>
|
||||
Generally speaking, every new kernel both adds features and introduces new bugs.
|
||||
These consequences are the basic properties of upstream kernel development and are
|
||||
managed by the Yocto Project team's kernel strategy.
|
||||
It is the Yocto Project team's policy to not back-port minor features to the released kernel.
|
||||
They only consider back-porting significant technological jumps - and, that is done
|
||||
after a complete gap analysis.
|
||||
The reason for this policy is that back-porting any small to medium sized change
|
||||
from an evolving kernel can easily create mismatches, incompatibilities and very
|
||||
subtle errors.
|
||||
</para>
|
||||
<para>
|
||||
These policies result in both a stable and a cutting
|
||||
edge kernel that mixes forward ports of existing features and significant and critical
|
||||
new functionality.
|
||||
Forward porting functionality in the kernels available through the Yocto Project kernel
|
||||
can be thought of as a "micro uprev."
|
||||
The many “micro uprevs” produce a kernel version with a mix of
|
||||
important new mainline, non-mainline, BSP developments and feature integrations.
|
||||
This kernel gives insight into new features and allows focused
|
||||
amounts of testing to be done on the kernel, which prevents
|
||||
surprises when selecting the next major uprev.
|
||||
The quality of these cutting edge kernels is evolving and the kernels are used in leading edge
|
||||
feature and BSP development.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='kernel-architecture'>
|
||||
<title>Kernel Architecture</title>
|
||||
<para>
|
||||
This section describes the architecture of the kernels available through the
|
||||
Yocto Project and provides information
|
||||
on the mechanisms used to achieve that architecture.
|
||||
</para>
|
||||
|
||||
<section id='architecture-overview'>
|
||||
<title>Overview</title>
|
||||
<para>
|
||||
As mentioned earlier, a key goal of the Yocto Project is to present the
|
||||
developer with
|
||||
a kernel that has a clear and continuous history that is visible to the user.
|
||||
The architecture and mechanisms used achieve that goal in a manner similar to the
|
||||
upstream <filename>kernel.org</filename>.
|
||||
</para>
|
||||
<para>
|
||||
You can think of a Yocto Project kernel as consisting of a baseline Linux kernel with
|
||||
added features logically structured on top of the baseline.
|
||||
The features are tagged and organized by way of a branching strategy implemented by the
|
||||
source code manager (SCM) Git.
|
||||
For information on Git as applied to the Yocto Project, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#git'>Git</ulink>" section in the
|
||||
Yocto Project Development Manual.
|
||||
</para>
|
||||
<para>
|
||||
The result is that the user has the ability to see the added features and
|
||||
the commits that make up those features.
|
||||
In addition to being able to see added features, the user can also view the history of what
|
||||
made up the baseline kernel.
|
||||
</para>
|
||||
<para>
|
||||
The following illustration shows the conceptual Yocto Project kernel.
|
||||
</para>
|
||||
<para>
|
||||
<imagedata fileref="figures/kernel-architecture-overview.png" width="6in" depth="7in" align="center" scale="100" />
|
||||
</para>
|
||||
<para>
|
||||
In the illustration, the "Kernel.org Branch Point"
|
||||
marks the specific spot (or release) from
|
||||
which the Yocto Project kernel is created.
|
||||
From this point "up" in the tree, features and differences are organized and tagged.
|
||||
</para>
|
||||
<para>
|
||||
The "Yocto Project Baseline Kernel" contains functionality that is common to every kernel
|
||||
type and BSP that is organized further up the tree.
|
||||
Placing these common features in the
|
||||
tree this way means features don't have to be duplicated along individual branches of the
|
||||
structure.
|
||||
</para>
|
||||
<para>
|
||||
From the Yocto Project Baseline Kernel, branch points represent specific functionality
|
||||
for individual BSPs as well as real-time kernels.
|
||||
The illustration represents this through three BSP-specific branches and a real-time
|
||||
kernel branch.
|
||||
Each branch represents some unique functionality for the BSP or a real-time kernel.
|
||||
</para>
|
||||
<para>
|
||||
In this example structure, the real-time kernel branch has common features for all
|
||||
real-time kernels and contains
|
||||
more branches for individual BSP-specific real-time kernels.
|
||||
The illustration shows three branches as an example.
|
||||
Each branch points the way to specific, unique features for a respective real-time
|
||||
kernel as they apply to a given BSP.
|
||||
</para>
|
||||
<para>
|
||||
The resulting tree structure presents a clear path of markers (or branches) to the
|
||||
developer that, for all practical purposes, is the kernel needed for any given set
|
||||
of requirements.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='branching-and-workflow'>
|
||||
<title>Branching Strategy and Workflow</title>
|
||||
<para>
|
||||
The Yocto Project team creates kernel branches at points where functionality is
|
||||
no longer shared and thus, needs to be isolated.
|
||||
For example, board-specific incompatibilities would require different functionality
|
||||
and would require a branch to separate the features.
|
||||
Likewise, for specific kernel features, the same branching strategy is used.
|
||||
</para>
|
||||
<para>
|
||||
This branching strategy results in a tree that has features organized to be specific
|
||||
for particular functionality, single kernel types, or a subset of kernel types.
|
||||
This strategy also results in not having to store the same feature twice
|
||||
internally in the tree.
|
||||
Rather, the kernel team stores the unique differences required to apply the
|
||||
feature onto the kernel type in question.
|
||||
<note>
|
||||
The Yocto Project team strives to place features in the tree such that they can be
|
||||
shared by all boards and kernel types where possible.
|
||||
However, during development cycles or when large features are merged,
|
||||
the team cannot always follow this practice.
|
||||
In those cases, the team uses isolated branches to merge features.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
BSP-specific code additions are handled in a similar manner to kernel-specific additions.
|
||||
Some BSPs only make sense given certain kernel types.
|
||||
So, for these types, the team creates branches off the end of that kernel type for all
|
||||
of the BSPs that are supported on that kernel type.
|
||||
From the perspective of the tools that create the BSP branch, the BSP is really no
|
||||
different than a feature.
|
||||
Consequently, the same branching strategy applies to BSPs as it does to features.
|
||||
So again, rather than store the BSP twice, the team only stores the unique
|
||||
differences for the BSP across the supported multiple kernels.
|
||||
</para>
|
||||
<para>
|
||||
While this strategy can result in a tree with a significant number of branches, it is
|
||||
important to realize that from the developer's point of view, there is a linear
|
||||
path that travels from the baseline <filename>kernel.org</filename>, through a select
|
||||
group of features and ends with their BSP-specific commits.
|
||||
In other words, the divisions of the kernel are transparent and are not relevant
|
||||
to the developer on a day-to-day basis.
|
||||
From the developer's perspective, this path is the "master" branch.
|
||||
The developer does not need to be aware of the existence of any other branches at all.
|
||||
Of course, there is value in the existence of these branches
|
||||
in the tree, should a person decide to explore them.
|
||||
For example, a comparison between two BSPs at either the commit level or at the line-by-line
|
||||
code <filename>diff</filename> level is now a trivial operation.
|
||||
</para>
|
||||
<para>
|
||||
Working with the kernel as a structured tree follows recognized community best practices.
|
||||
In particular, the kernel as shipped with the product, should be
|
||||
considered an "upstream source" and viewed as a series of
|
||||
historical and documented modifications (commits).
|
||||
These modifications represent the development and stabilization done
|
||||
by the Yocto Project kernel development team.
|
||||
</para>
|
||||
<para>
|
||||
Because commits only change at significant release points in the product life cycle,
|
||||
developers can work on a branch created
|
||||
from the last relevant commit in the shipped Yocto Project kernel.
|
||||
As mentioned previously, the structure is transparent to the developer
|
||||
because the kernel tree is left in this state after cloning and building the kernel.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='source-code-manager-git'>
|
||||
<title>Source Code Manager - Git</title>
|
||||
<para>
|
||||
The Source Code Manager (SCM) is Git.
|
||||
This SCM is the obvious mechanism for meeting the previously mentioned goals.
|
||||
Not only is it the SCM for <filename>kernel.org</filename> but,
|
||||
Git continues to grow in popularity and supports many different work flows,
|
||||
front-ends and management techniques.
|
||||
</para>
|
||||
<para>
|
||||
You can find documentation on Git at <ulink url='http://git-scm.com/documentation'></ulink>.
|
||||
You can also get an introduction to Git as it applies to the Yocto Project in the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#git'>Git</ulink>"
|
||||
section in the Yocto Project Development Manual.
|
||||
These referenced sections overview Git and describe a minimal set of
|
||||
commands that allows you to be functional using Git.
|
||||
<note>
|
||||
You can use as much, or as little, of what Git has to offer to accomplish what
|
||||
you need for your project.
|
||||
You do not have to be a "Git Master" in order to use it with the Yocto Project.
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='kernel-configuration'>
|
||||
<title>Kernel Configuration</title>
|
||||
<para>
|
||||
Kernel configuration, along with kernel features, defines how a kernel
|
||||
image is built for the Yocto Project.
|
||||
Through configuration settings, you can customize a Yocto Project kernel to be
|
||||
specific to particular hardware.
|
||||
For example, you can specify sound support or networking support.
|
||||
This section describes basic concepts behind Kernel configuration within the
|
||||
Yocto Project and references you to other areas for specific configuration
|
||||
applications.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Conceptually, configuration of a Yocto Project kernel occurs similarly to that needed for any
|
||||
Linux kernel.
|
||||
The build process for a Yocto Project kernel uses a <filename>.config</filename> file, which
|
||||
is created through the Linux Kernel Configuration (LKC) tool.
|
||||
You can directly set various configurations in the
|
||||
<filename>.config</filename> file by using the <filename>menuconfig</filename>
|
||||
tool as built by BitBake.
|
||||
You can also define configurations in the file by using configuration fragments.
|
||||
<note>
|
||||
It is not recommended that you edit the <filename>.config</filename> file directly.
|
||||
</note>
|
||||
Here are some brief descriptions of the ways you can affect the
|
||||
<filename>.config</filename> file:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>The <filename>menuconfig</filename> Tool:</emphasis>
|
||||
One of many front-ends that allows you to define kernel configurations.
|
||||
Some others are <filename>make config</filename>,
|
||||
<filename>make nconfig</filename>, and <filename>make gconfig</filename>.
|
||||
In the Yocto Project environment, you must use BitBake to build the
|
||||
<filename>menuconfig</filename> tool before you can use it to define
|
||||
configurations:
|
||||
<literallayout class='monospaced'>
|
||||
$ bitbake linux-yocto -c menuconfig
|
||||
</literallayout>
|
||||
After the tool is built, you can interact with it normally.
|
||||
You can see how <filename>menuconfig</filename> is used to change a simple
|
||||
kernel configuration in the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#configuring-the-kernel'>Configuring the Kernel</ulink>"
|
||||
section of the Yocto Project Development Manual.
|
||||
For general information on <filename>menuconfig</filename>, see
|
||||
<ulink url='http://en.wikipedia.org/wiki/Menuconfig'></ulink>.
|
||||
</para></listitem>
|
||||
<listitem><para><emphasis>Configuration Fragments:</emphasis> A file with a
|
||||
list of kernel options just as they would appear syntactically in the
|
||||
<filename>.config</filename> file.
|
||||
Configuration fragments are typically logical groupings and are assembled
|
||||
by the OpenEmbedded build system to produce input used by the LKC
|
||||
that ultimately generates the <filename>.config</filename> file.</para>
|
||||
<para>The
|
||||
<filename><ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_FEATURES'>KERNEL_FEATURES</ulink></filename>
|
||||
variable can be used to list configuration fragments.
|
||||
For further discussion on applying configuration fragments, see the
|
||||
"<ulink url='&YOCTO_DOCS_BSP_URL;#bsp-filelayout-kernel'>Linux Kernel Configuration</ulink>"
|
||||
section in the Yocto Project Board Support Package (BSP) Guide.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='kernel-tools'>
|
||||
<title>Kernel Tools</title>
|
||||
<para>
|
||||
Since most standard workflows involve moving forward with an existing tree by
|
||||
continuing to add and alter the underlying baseline, the tools that manage
|
||||
the Yocto Project's kernel construction are largely hidden from the developer to
|
||||
present a simplified view of the kernel for ease of use.
|
||||
</para>
|
||||
<para>
|
||||
Fundamentally, the kernel tools that manage and construct the
|
||||
Yocto Project kernel accomplish the following:
|
||||
<itemizedlist>
|
||||
<listitem><para>Group patches into named, reusable features.</para></listitem>
|
||||
<listitem><para>Allow top-down control of included features.</para></listitem>
|
||||
<listitem><para>Bind kernel configurations to kernel patches and features.</para></listitem>
|
||||
<listitem><para>Present a seamless Git repository that blends Yocto Project value
|
||||
with the <filename>kernel.org</filename> history and development.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
78
documentation/kernel-manual/kernel-doc-intro.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[<!ENTITY % poky SYSTEM "../poky.ent"> %poky; ] >
|
||||
|
||||
<chapter id='kernel-doc-intro'>
|
||||
|
||||
<title>Yocto Project Kernel Architecture and Use Manual</title>
|
||||
|
||||
<section id='kernel-intro-section'>
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
The Yocto Project presents kernels as a fully patched, history-clean Git
|
||||
repositories.
|
||||
Each repository represents selected features, board support,
|
||||
and configurations extensively tested by the Yocto Project.
|
||||
Yocto Project kernels allow the end user to leverage community
|
||||
best practices to seamlessly manage the development, build and debug cycles.
|
||||
</para>
|
||||
<para>
|
||||
This manual describes Yocto Project kernels by providing information
|
||||
on history, organization, benefits, and use.
|
||||
The manual consists of two sections:
|
||||
<itemizedlist>
|
||||
<listitem><para><emphasis>Concepts:</emphasis> Describes concepts behind a kernel.
|
||||
You will understand how a kernel is organized and why it is organized in
|
||||
the way it is. You will understand the benefits of a kernel's organization
|
||||
and the mechanisms used to work with the kernel and how to apply it in your
|
||||
design process.</para></listitem>
|
||||
<listitem><para><emphasis>Using a Kernel:</emphasis> Describes best practices
|
||||
and "how-to" information
|
||||
that lets you put a kernel to practical use.
|
||||
Some examples are how to examine changes in a branch and how to
|
||||
save kernel modifications.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For more information on the Linux kernel, see the following links:
|
||||
<itemizedlist>
|
||||
<listitem><para>The Linux Foundation's guide for kernel development
|
||||
process - <ulink url='http://www.linuxfoundation.org/content/1-guide-kernel-development-process'></ulink></para></listitem>
|
||||
<listitem><para>A fairly encompassing guide on Linux kernel development -
|
||||
<ulink url='http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/HOWTO;hb=HEAD'></ulink></para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For more discussion on the Yocto Project kernel, you can see these sections
|
||||
in the Yocto Project Development Manual:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#kernel-overview'>Kernel Overview</ulink>"</para></listitem>
|
||||
<listitem><para>
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#kernel-modification-workflow'>Kernel Modification Workflow</ulink>"
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#patching-the-kernel'>Patching the Kernel</ulink>"</para></listitem>
|
||||
<listitem><para>
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#configuring-the-kernel'>Configuring the Kernel</ulink>"</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For general information on the Yocto Project, visit the website at
|
||||
<ulink url='&YOCTO_HOME_URL;'></ulink>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</chapter>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
918
documentation/kernel-manual/kernel-how-to.xml
Normal file
@@ -0,0 +1,918 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[<!ENTITY % poky SYSTEM "../poky.ent"> %poky; ] >
|
||||
|
||||
<chapter id='kernel-how-to'>
|
||||
|
||||
<title>Working with the Yocto Project Kernel</title>
|
||||
|
||||
|
||||
<section id='actions-org'>
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
This chapter describes how to accomplish tasks involving a kernel's tree structure.
|
||||
The information is designed to help the developer that wants to modify the Yocto
|
||||
Project kernel and contribute changes upstream to the Yocto Project.
|
||||
The information covers the following:
|
||||
<itemizedlist>
|
||||
<listitem><para>Tree construction</para></listitem>
|
||||
<listitem><para>Build strategies</para></listitem>
|
||||
<listitem><para>Workflow examples</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='tree-construction'>
|
||||
<title>Tree Construction</title>
|
||||
<para>
|
||||
This section describes construction of the Yocto Project kernel source repositories
|
||||
as accomplished by the Yocto Project team to create kernel repositories.
|
||||
These kernel repositories are found under the heading "Yocto Linux Kernel" at
|
||||
<ulink url='&YOCTO_GIT_URL;/cgit.cgi'>&YOCTO_GIT_URL;/cgit.cgi</ulink>
|
||||
and can be shipped as part of a Yocto Project release.
|
||||
The team creates these repositories by
|
||||
compiling and executing the set of feature descriptions for every BSP/feature
|
||||
in the product.
|
||||
Those feature descriptions list all necessary patches,
|
||||
configuration, branching, tagging and feature divisions found in a kernel.
|
||||
Thus, the Yocto Project kernel repository (or tree) is built.
|
||||
</para>
|
||||
<para>
|
||||
The existence of this tree allows you to access and clone a particular
|
||||
Yocto Project kernel repository and use it to build images based on their configurations
|
||||
and features.
|
||||
</para>
|
||||
<para>
|
||||
You can find the files used to describe all the valid features and BSPs
|
||||
in the Yocto Project kernel in any clone of the Yocto Project kernel source repository
|
||||
Git tree.
|
||||
For example, the following command clones the Yocto Project baseline kernel that
|
||||
branched off of <filename>linux.org</filename> version 3.4:
|
||||
<literallayout class='monospaced'>
|
||||
$ git clone git://git.yoctoproject.org/linux-yocto-3.4
|
||||
</literallayout>
|
||||
For another example of how to set up a local Git repository of the Yocto Project
|
||||
kernel files, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#local-kernel-files'>Yocto Project Kernel</ulink>" bulleted
|
||||
item in the Yocto Project Development Manual.
|
||||
</para>
|
||||
<para>
|
||||
Once you have cloned the kernel Git repository on your local machine, you can
|
||||
switch to the <filename>meta</filename> branch within the repository.
|
||||
Here is an example that assumes the local Git repository for the kernel is in
|
||||
a top-level directory named <filename>linux-yocto-3.4</filename>:
|
||||
<literallayout class='monospaced'>
|
||||
$ cd ~/linux-yocto-3.4
|
||||
$ git checkout -b meta origin/meta
|
||||
</literallayout>
|
||||
Once you have checked out and switched to the <filename>meta</filename> branch,
|
||||
you can see a snapshot of all the kernel configuration and feature descriptions that are
|
||||
used to build that particular kernel repository.
|
||||
These descriptions are in the form of <filename>.scc</filename> files.
|
||||
</para>
|
||||
<para>
|
||||
You should realize, however, that browsing your local kernel repository
|
||||
for feature descriptions and patches is not an effective way to determine what is in a
|
||||
particular kernel branch.
|
||||
Instead, you should use Git directly to discover the changes in a branch.
|
||||
Using Git is an efficient and flexible way to inspect changes to the kernel.
|
||||
For examples showing how to use Git to inspect kernel commits, see the following sections
|
||||
in this chapter.
|
||||
<note>
|
||||
Ground up reconstruction of the complete kernel tree is an action only taken by the
|
||||
Yocto Project team during an active development cycle.
|
||||
When you create a clone of the kernel Git repository, you are simply making it
|
||||
efficiently available for building and development.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
The following steps describe what happens when the Yocto Project Team constructs
|
||||
the Yocto Project kernel source Git repository (or tree) found at
|
||||
<ulink url='&YOCTO_GIT_URL;/cgit.cgi'></ulink> given the
|
||||
introduction of a new top-level kernel feature or BSP.
|
||||
These are the actions that effectively create the tree
|
||||
that includes the new feature, patch or BSP:
|
||||
<orderedlist>
|
||||
<listitem><para>A top-level kernel feature is passed to the kernel build subsystem.
|
||||
Normally, this feature is a BSP for a particular kernel type.</para></listitem>
|
||||
<listitem><para>The file that describes the top-level feature is located by searching
|
||||
these system directories:
|
||||
<itemizedlist>
|
||||
<listitem><para>The in-tree kernel-cache directories, which are located
|
||||
in <filename>meta/cfg/kernel-cache</filename></para></listitem>
|
||||
<listitem><para>Areas pointed to by <filename>SRC_URI</filename> statements
|
||||
found in recipes</para></listitem>
|
||||
</itemizedlist>
|
||||
For a typical build, the target of the search is a
|
||||
feature description in an <filename>.scc</filename> file
|
||||
whose name follows this format:
|
||||
<literallayout class='monospaced'>
|
||||
<bsp_name>-<kernel_type>.scc
|
||||
</literallayout>
|
||||
</para></listitem>
|
||||
<listitem><para>Once located, the feature description is either compiled into a simple script
|
||||
of actions, or into an existing equivalent script that is already part of the
|
||||
shipped kernel.</para></listitem>
|
||||
<listitem><para>Extra features are appended to the top-level feature description.
|
||||
These features can come from the
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-KERNEL_FEATURES'><filename>KERNEL_FEATURES</filename></ulink>
|
||||
variable in recipes.</para></listitem>
|
||||
<listitem><para>Each extra feature is located, compiled and appended to the script
|
||||
as described in step three.</para></listitem>
|
||||
<listitem><para>The script is executed to produce a series of <filename>meta-*</filename>
|
||||
directories.
|
||||
These directories are descriptions of all the branches, tags, patches and configurations that
|
||||
need to be applied to the base Git repository to completely create the
|
||||
source (build) branch for the new BSP or feature.</para></listitem>
|
||||
<listitem><para>The base repository is cloned, and the actions
|
||||
listed in the <filename>meta-*</filename> directories are applied to the
|
||||
tree.</para></listitem>
|
||||
<listitem><para>The Git repository is left with the desired branch checked out and any
|
||||
required branching, patching and tagging has been performed.</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
<para>
|
||||
The kernel tree is now ready for developer consumption to be locally cloned,
|
||||
configured, and built into a Yocto Project kernel specific to some target hardware.
|
||||
<note><para>The generated <filename>meta-*</filename> directories add to the kernel
|
||||
as shipped with the Yocto Project release.
|
||||
Any add-ons and configuration data are applied to the end of an existing branch.
|
||||
The full repository generation that is found in the
|
||||
official Yocto Project kernel repositories at
|
||||
<ulink url='&YOCTO_GIT_URL;/cgit.cgi'>http://git.yoctoproject.org/cgit.cgi</ulink>
|
||||
is the combination of all supported boards and configurations.</para>
|
||||
<para>The technique the Yocto Project team uses is flexible and allows for seamless
|
||||
blending of an immutable history with additional patches specific to a
|
||||
deployment.
|
||||
Any additions to the kernel become an integrated part of the branches.</para>
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='build-strategy'>
|
||||
<title>Build Strategy</title>
|
||||
<para>
|
||||
Once a local Git repository of the Yocto Project kernel exists on a development system,
|
||||
you can consider the compilation phase of kernel development - building a kernel image.
|
||||
Some prerequisites exist that are validated by the build process before compilation
|
||||
starts:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>The
|
||||
<ulink url='&YOCTO_DOCS_REF_URL;#var-SRC_URI'><filename>SRC_URI</filename></ulink> points
|
||||
to the kernel Git repository.</para></listitem>
|
||||
<listitem><para>A BSP build branch exists.
|
||||
This branch has the following form:
|
||||
<literallayout class='monospaced'>
|
||||
<kernel_type>/<bsp_name>
|
||||
</literallayout></para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
The OpenEmbedded build system makes sure these conditions exist before attempting compilation.
|
||||
Other means, however, do exist, such as as bootstrapping a BSP, see
|
||||
the "<link linkend='workflow-examples'>Workflow Examples</link>".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Before building a kernel, the build process verifies the tree
|
||||
and configures the kernel by processing all of the
|
||||
configuration "fragments" specified by feature descriptions in the <filename>.scc</filename>
|
||||
files.
|
||||
As the features are compiled, associated kernel configuration fragments are noted
|
||||
and recorded in the <filename>meta-*</filename> series of directories in their compilation order.
|
||||
The fragments are migrated, pre-processed and passed to the Linux Kernel
|
||||
Configuration subsystem (<filename>lkc</filename>) as raw input in the form
|
||||
of a <filename>.config</filename> file.
|
||||
The <filename>lkc</filename> uses its own internal dependency constraints to do the final
|
||||
processing of that information and generates the final <filename>.config</filename> file
|
||||
that is used during compilation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Using the board's architecture and other relevant values from the board's template,
|
||||
kernel compilation is started and a kernel image is produced.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The other thing that you notice once you configure a kernel is that
|
||||
the build process generates a build tree that is separate from your kernel's local Git
|
||||
source repository tree.
|
||||
This build tree has a name that uses the following form, where
|
||||
<filename>${MACHINE}</filename> is the metadata name of the machine (BSP) and "kernel_type" is one
|
||||
of the Yocto Project supported kernel types (e.g. "standard"):
|
||||
<literallayout class='monospaced'>
|
||||
linux-${MACHINE}-<kernel_type>-build
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The existing support in the <filename>kernel.org</filename> tree achieves this
|
||||
default functionality.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This behavior means that all the generated files for a particular machine or BSP are now in
|
||||
the build tree directory.
|
||||
The files include the final <filename>.config</filename> file, all the <filename>.o</filename>
|
||||
files, the <filename>.a</filename> files, and so forth.
|
||||
Since each machine or BSP has its own separate build directory in its own separate branch
|
||||
of the Git repository, you can easily switch between different builds.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='workflow-examples'>
|
||||
<title>Workflow Examples</title>
|
||||
|
||||
<para>
|
||||
As previously noted, the Yocto Project kernel has built-in Git integration.
|
||||
However, these utilities are not the only way to work with the kernel repository.
|
||||
The Yocto Project has not made changes to Git or to other tools that
|
||||
would invalidate alternate workflows.
|
||||
Additionally, the way the kernel repository is constructed results in using
|
||||
only core Git functionality, thus allowing any number of tools or front ends to use the
|
||||
resulting tree.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This section contains several workflow examples.
|
||||
Many of the examples use Git commands.
|
||||
You can find Git documentation at
|
||||
<ulink url='http://git-scm.com/documentation'></ulink>.
|
||||
You can find a simple overview of using Git with the Yocto Project in the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#git'>Git</ulink>"
|
||||
section of the Yocto Project Development Manual.
|
||||
</para>
|
||||
|
||||
<section id='change-inspection-kernel-changes-commits'>
|
||||
<title>Change Inspection: Changes/Commits</title>
|
||||
|
||||
<para>
|
||||
A common question when working with a kernel is:
|
||||
"What changes have been applied to this tree?"
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In projects that have a collection of directories that
|
||||
contain patches to the kernel, it is possible to inspect or "grep" the contents
|
||||
of the directories to get a general feel for the changes.
|
||||
This sort of patch inspection is not an efficient way to determine what has been
|
||||
done to the kernel.
|
||||
The reason it is inefficient is because there are many optional patches that are
|
||||
selected based on the kernel type and the feature description.
|
||||
Additionally, patches could exist in directories that are not included in the search.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A more efficient way to determine what has changed in the branch is to use
|
||||
Git and inspect or search the kernel tree.
|
||||
This method gives you a full view of not only the source code modifications,
|
||||
but also provides the reasons for the changes.
|
||||
</para>
|
||||
|
||||
<section id='what-changed-in-a-kernel'>
|
||||
<title>What Changed in a Kernel?</title>
|
||||
|
||||
<para>
|
||||
Following are a few examples that show how to use Git commands to examine changes.
|
||||
Because Git repositories in the Yocto Project do not break existing Git
|
||||
functionality, and because there exists many permutations of these types of
|
||||
Git commands, many methods exist by which you can discover changes.
|
||||
<note>
|
||||
In the following examples, unless you provide a commit range,
|
||||
<filename>kernel.org</filename> history is blended with Yocto Project
|
||||
kernel changes.
|
||||
You can form ranges by using branch names from the kernel tree as the
|
||||
upper and lower commit markers with the Git commands.
|
||||
You can see the branch names through the web interface to the
|
||||
Yocto Project source repositories at
|
||||
<ulink url='http://git.yoctoproject.org/cgit.cgi'></ulink>.
|
||||
For example, the branch names for the <filename>linux-yocto-3.4</filename>
|
||||
kernel repository can be seen at
|
||||
<ulink url='http://git.yoctoproject.org/cgit.cgi/linux-yocto-3.4/refs/heads'></ulink>.
|
||||
</note>
|
||||
To see a full range of the changes, use the
|
||||
<filename>git whatchanged</filename> command and specify a commit range
|
||||
for the branch (<filename><commit>..<commit></filename>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here is an example that looks at what has changed in the
|
||||
<filename>emenlow</filename> branch of the
|
||||
<filename>linux-yocto-3.4</filename> kernel.
|
||||
The lower commit range is the commit associated with the
|
||||
<filename>standard/base</filename> branch, while
|
||||
the upper commit range is the commit associated with the
|
||||
<filename>standard/emenlow</filename> branch.
|
||||
<literallayout class='monospaced'>
|
||||
$ git whatchanged origin/standard/base..origin/standard/emenlow
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To see a summary of changes use the <filename>git log</filename> command.
|
||||
Here is an example using the same branches:
|
||||
<literallayout class='monospaced'>
|
||||
$ git log --oneline origin/standard/base..origin/standard/emenlow
|
||||
</literallayout>
|
||||
The <filename>git log</filename> output might be more useful than
|
||||
the <filename>git whatchanged</filename> as you get
|
||||
a short, one-line summary of each change and not the entire commit.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want to see code differences associated with all the changes, use
|
||||
the <filename>git diff</filename> command.
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
$ git diff origin/standard/base..origin/standard/emenlow
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can see the commit log messages and the text differences using the
|
||||
<filename>git show</filename> command:
|
||||
Here is an example:
|
||||
<literallayout class='monospaced'>
|
||||
$ git show origin/standard/base..origin/standard/emenlow
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can create individual patches for each change by using the
|
||||
<filename>git format-patch</filename> command.
|
||||
Here is an example that that creates patch files for each commit and
|
||||
places them in your <filename>Documents</filename> directory:
|
||||
<literallayout class='monospaced'>
|
||||
$ git format-patch -o $HOME/Documents origin/standard/base..origin/standard/emenlow
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='show-a-particular-feature-or-branch-change'>
|
||||
<title>Show a Particular Feature or Branch Change</title>
|
||||
|
||||
<para>
|
||||
Developers use tags in the Yocto Project kernel tree to divide changes for significant
|
||||
features or branches.
|
||||
Once you know a particular tag, you can use Git commands
|
||||
to show changes associated with the tag and find the branches that contain
|
||||
the feature.
|
||||
<note>
|
||||
Because BSP branch, <filename>kernel.org</filename>, and feature tags are all
|
||||
present, there could be many tags.
|
||||
</note>
|
||||
The <filename>git show <tag></filename> command shows changes that are tagged by
|
||||
a feature.
|
||||
Here is an example that shows changes tagged by the <filename>systemtap</filename>
|
||||
feature:
|
||||
<literallayout class='monospaced'>
|
||||
$ git show systemtap
|
||||
</literallayout>
|
||||
You can use the <filename>git branch --contains <tag></filename> command
|
||||
to show the branches that contain a particular feature.
|
||||
This command shows the branches that contain the <filename>systemtap</filename>
|
||||
feature:
|
||||
<literallayout class='monospaced'>
|
||||
$ git branch --contains systemtap
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can use many other comparisons to isolate BSP and kernel changes.
|
||||
For example, you can compare against <filename>kernel.org</filename> tags
|
||||
such as the <filename>v3.4</filename> tag.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='development-saving-kernel-modifications'>
|
||||
<title>Development: Saving Kernel Modifications</title>
|
||||
|
||||
<para>
|
||||
Another common operation is to build a BSP supplied by the Yocto Project, make some
|
||||
changes, rebuild, and then test.
|
||||
Those local changes often need to be exported, shared or otherwise maintained.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Since the Yocto Project kernel source tree is backed by Git, this activity is
|
||||
much easier as compared to with previous releases.
|
||||
Because Git tracks file modifications, additions and deletions, it is easy
|
||||
to modify the code and later realize that you need to save the changes.
|
||||
It is also easy to determine what has changed.
|
||||
This method also provides many tools to commit, undo and export those modifications.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This section and its sub-sections, describe general application of Git's
|
||||
<filename>push</filename> and <filename>pull</filename> commands, which are used to
|
||||
get your changes upstream or source your code from an upstream repository.
|
||||
The Yocto Project provides scripts that help you work in a collaborative development
|
||||
environment.
|
||||
For information on these scripts, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#pushing-a-change-upstream'>Using Scripts to Push a Change
|
||||
Upstream and Request a Pull</ulink>" and
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#submitting-a-patch'>Using Email to Submit a Patch</ulink>"
|
||||
sections in the Yocto Project Development Manual.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are many ways to save kernel modifications.
|
||||
The technique employed
|
||||
depends on the destination for the patches:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Bulk storage</para></listitem>
|
||||
<listitem><para>Internal sharing either through patches or by using Git</para></listitem>
|
||||
<listitem><para>External submissions</para></listitem>
|
||||
<listitem><para>Exporting for integration into another Source Code
|
||||
Manager (SCM)</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Because of the following list of issues, the destination of the patches also influences
|
||||
the method for gathering them:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Bisectability</para></listitem>
|
||||
<listitem><para>Commit headers</para></listitem>
|
||||
<listitem><para>Division of subsystems for separate submission or review</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<section id='bulk-export'>
|
||||
<title>Bulk Export</title>
|
||||
|
||||
<para>
|
||||
This section describes how you can "bulk" export changes that have not
|
||||
been separated or divided.
|
||||
This situation works well when you are simply storing patches outside of the kernel
|
||||
source repository, either permanently or temporarily, and you are not committing
|
||||
incremental changes during development.
|
||||
<note>
|
||||
This technique is not appropriate for full integration of upstream submission
|
||||
because changes are not properly divided and do not provide an avenue for per-change
|
||||
commit messages.
|
||||
Therefore, this example assumes that changes have not been committed incrementally
|
||||
during development and that you simply must gather and export them.
|
||||
</note>
|
||||
<literallayout class='monospaced'>
|
||||
# bulk export of ALL modifications without separation or division
|
||||
# of the changes
|
||||
|
||||
$ git add .
|
||||
$ git commit -s -a -m <msg>
|
||||
or
|
||||
$ git commit -s -a # and interact with $EDITOR
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The previous operations capture all the local changes in the project source
|
||||
tree in a single Git commit.
|
||||
And, that commit is also stored in the project's source tree.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once the changes are exported, you can restore them manually using a template
|
||||
or through integration with the <filename>default_kernel</filename>.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id='incremental-planned-sharing'>
|
||||
<title>Incremental/Planned Sharing</title>
|
||||
|
||||
<para>
|
||||
This section describes how to save modifications when you are making incremental
|
||||
commits or practicing planned sharing.
|
||||
The examples in this section assume that you have incrementally committed
|
||||
changes to the tree during development and now need to export them.
|
||||
The sections that follow
|
||||
describe how you can export your changes internally through either patches or by
|
||||
using Git commands.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
During development, the following commands are of interest.
|
||||
For full Git documentation, refer to the Git documentation at
|
||||
<ulink url='http://github.com'></ulink>.
|
||||
|
||||
<literallayout class='monospaced'>
|
||||
# edit a file
|
||||
$ vi <path>/file
|
||||
# stage the change
|
||||
$ git add <path>/file
|
||||
# commit the change
|
||||
$ git commit -s
|
||||
# remove a file
|
||||
$ git rm <path>/file
|
||||
# commit the change
|
||||
$ git commit -s
|
||||
|
||||
... etc.
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Distributed development with Git is possible when you use a universally
|
||||
agreed-upon unique commit identifier (set by the creator of the commit) that maps to a
|
||||
specific change set with a specific parent.
|
||||
This identifier is created for you when
|
||||
you create a commit, and is re-created when you amend, alter or re-apply
|
||||
a commit.
|
||||
As an individual in isolation, this is of no interest.
|
||||
However, if you
|
||||
intend to share your tree with normal Git <filename>push</filename> and
|
||||
<filename>pull</filename> operations for
|
||||
distributed development, you should consider the ramifications of changing a
|
||||
commit that you have already shared with others.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Assuming that the changes have not been pushed upstream, or pulled into
|
||||
another repository, you can update both the commit content and commit messages
|
||||
associated with development by using the following commands:
|
||||
|
||||
<literallayout class='monospaced'>
|
||||
$ Git add <path>/file
|
||||
$ Git commit --amend
|
||||
$ Git rebase or Git rebase -i
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Again, assuming that the changes have not been pushed upstream, and that
|
||||
no pending works-in-progress exist (use <filename>git status</filename> to check), then
|
||||
you can revert (undo) commits by using the following commands:
|
||||
|
||||
<literallayout class='monospaced'>
|
||||
# remove the commit, update working tree and remove all
|
||||
# traces of the change
|
||||
$ git reset --hard HEAD^
|
||||
# remove the commit, but leave the files changed and staged for re-commit
|
||||
$ git reset --soft HEAD^
|
||||
# remove the commit, leave file change, but not staged for commit
|
||||
$ git reset --mixed HEAD^
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can create branches, "cherry-pick" changes, or perform any number of Git
|
||||
operations until the commits are in good order for pushing upstream
|
||||
or for pull requests.
|
||||
After a <filename>push</filename> or <filename>pull</filename> command,
|
||||
commits are normally considered
|
||||
"permanent" and you should not modify them.
|
||||
If the commits need to be changed, you can incrementally do so with new commits.
|
||||
These practices follow standard Git workflow and the <filename>kernel.org</filename> best
|
||||
practices, which is recommended.
|
||||
<note>
|
||||
It is recommended to tag or branch before adding changes to a Yocto Project
|
||||
BSP or before creating a new one.
|
||||
The reason for this recommendation is because the branch or tag provides a
|
||||
reference point to facilitate locating and exporting local changes.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<section id='export-internally-via-patches'>
|
||||
<title>Exporting Changes Internally by Using Patches</title>
|
||||
|
||||
<para>
|
||||
This section describes how you can extract committed changes from a working directory
|
||||
by exporting them as patches.
|
||||
Once the changes have been extracted, you can use the patches for upstream submission,
|
||||
place them in a Yocto Project template for automatic kernel patching,
|
||||
or apply them in many other common uses.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This example shows how to create a directory with sequentially numbered patches.
|
||||
Once the directory is created, you can apply it to a repository using the
|
||||
<filename>git am</filename> command to reproduce the original commit and all
|
||||
the related information such as author, date, commit log, and so forth.
|
||||
<note>
|
||||
The new commit identifiers (ID) will be generated upon re-application.
|
||||
This action reflects that the commit is now applied to an underlying commit
|
||||
with a different ID.
|
||||
</note>
|
||||
<literallayout class='monospaced'>
|
||||
# <first-commit> can be a tag if one was created before development
|
||||
# began. It can also be the parent branch if a branch was created
|
||||
# before development began.
|
||||
|
||||
$ git format-patch -o <dir> <first commit>..<last commit>
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In other words:
|
||||
<literallayout class='monospaced'>
|
||||
# Identify commits of interest.
|
||||
|
||||
# If the tree was tagged before development
|
||||
$ git format-patch -o <save dir> <tag>
|
||||
|
||||
# If no tags are available
|
||||
$ git format-patch -o <save dir> HEAD^ # last commit
|
||||
$ git format-patch -o <save dir> HEAD^^ # last 2 commits
|
||||
$ git whatchanged # identify last commit
|
||||
$ git format-patch -o <save dir> <commit id>
|
||||
$ git format-patch -o <save dir> <rev-list>
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='export-internally-via-git'>
|
||||
<title>Exporting Changes Internally by Using Git</title>
|
||||
|
||||
<para>
|
||||
This section describes how you can export changes from a working directory
|
||||
by pushing the changes into a master repository or by making a pull request.
|
||||
Once you have pushed the changes to the master repository, you can then
|
||||
pull those same changes into a new kernel build at a later time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Use this command form to push the changes:
|
||||
<literallayout class='monospaced'>
|
||||
$ git push ssh://<master_server>/<path_to_repo>
|
||||
<local_branch>:<remote_branch>
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For example, the following command pushes the changes from your local branch
|
||||
<filename>yocto/standard/common-pc/base</filename> to the remote branch with the same name
|
||||
in the master repository <filename>//git.mycompany.com/pub/git/kernel-3.4</filename>.
|
||||
<literallayout class='monospaced'>
|
||||
$ git push ssh://git.mycompany.com/pub/git/kernel-3.4 \
|
||||
yocto/standard/common-pc/base:yocto/standard/common-pc/base
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A pull request entails using the <filename>git request-pull</filename> command to compose
|
||||
an email to the
|
||||
maintainer requesting that a branch be pulled into the master repository, see
|
||||
<ulink url='http://github.com/guides/pull-requests'></ulink> for an example.
|
||||
<note>
|
||||
Other commands such as <filename>git stash</filename> or branching can also be used to save
|
||||
changes, but are not covered in this document.
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='export-for-external-upstream-submission'>
|
||||
<title>Exporting Changes for External (Upstream) Submission</title>
|
||||
|
||||
<para>
|
||||
This section describes how to export changes for external upstream submission.
|
||||
If the patch series is large or the maintainer prefers to pull
|
||||
changes, you can submit these changes by using a pull request.
|
||||
However, it is common to send patches as an email series.
|
||||
This method allows easy review and integration of the changes.
|
||||
<note>
|
||||
Before sending patches for review be sure you understand the
|
||||
community standards for submitting and documenting changes and follow their best practices.
|
||||
For example, kernel patches should follow standards such as:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
<ulink url='http://linux.yyz.us/patch-format.html'></ulink></para></listitem>
|
||||
<listitem><para>Documentation/SubmittingPatches (in any linux
|
||||
kernel source tree)</para></listitem>
|
||||
</itemizedlist>
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The messages used to commit changes are a large part of these standards.
|
||||
Consequently, be sure that the headers for each commit have the required information.
|
||||
For information on how to follow the Yocto Project commit message standards, see the
|
||||
"<ulink url='&YOCTO_DOCS_DEV_URL;#how-to-submit-a-change'>How to Submit a
|
||||
Change</ulink>" section in the Yocto Project Development Manual.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the initial commits were not properly documented or do not meet those standards,
|
||||
you can re-base by using the <filename>git rebase -i</filename> command to
|
||||
manipulate the commits and
|
||||
get them into the required format.
|
||||
Other techniques such as branching and cherry-picking commits are also viable options.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you complete the commits, you can generate the email that sends the patches
|
||||
to the maintainer(s) or lists that review and integrate changes.
|
||||
The command <filename>git send-email</filename> is commonly used to ensure
|
||||
that patches are properly
|
||||
formatted for easy application and avoid mailer-induced patch damage.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following is an example of dumping patches for external submission:
|
||||
<literallayout class='monospaced'>
|
||||
# dump the last 4 commits
|
||||
$ git format-patch --thread -n -o ~/rr/ HEAD^^^^
|
||||
$ git send-email --compose --subject '[RFC 0/N] <patch series summary>' \
|
||||
--to foo@yoctoproject.org --to bar@yoctoproject.org \
|
||||
--cc list@yoctoproject.org ~/rr
|
||||
# the editor is invoked for the 0/N patch, and when complete the entire
|
||||
# series is sent via email for review
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='export-for-import-into-other-scm'>
|
||||
<title>Exporting Changes for Import into Another SCM</title>
|
||||
|
||||
<para>
|
||||
When you want to export changes for import into another
|
||||
Source Code Manager (SCM), you can use any of the previously discussed
|
||||
techniques.
|
||||
However, if the patches are manually applied to a secondary tree and then
|
||||
that tree is checked into the SCM, you can lose change information such as
|
||||
commit logs.
|
||||
This process is not recommended.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Many SCMs can directly import Git commits, or can translate Git patches so that
|
||||
information is not lost.
|
||||
Those facilities are SCM-dependent and you should use them whenever possible.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='scm-working-with-the-yocto-project-kernel-in-another-scm'>
|
||||
<title>Working with the Yocto Project Kernel in Another SCM</title>
|
||||
|
||||
<para>
|
||||
This section describes kernel development in an SCM other than Git,
|
||||
which is not the same as exporting changes to another SCM described earlier.
|
||||
For this scenario, you use the OpenEmbedded build system to
|
||||
develop the kernel in a different SCM.
|
||||
The following must be true for you to accomplish this:
|
||||
<itemizedlist>
|
||||
<listitem><para>The delivered Yocto Project kernel must be exported into the second
|
||||
SCM.</para></listitem>
|
||||
<listitem><para>Development must be exported from that secondary SCM into a
|
||||
format that can be used by the OpenEmbedded build system.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<section id='exporting-delivered-kernel-to-scm'>
|
||||
<title>Exporting the Delivered Kernel to the SCM</title>
|
||||
|
||||
<para>
|
||||
Depending on the SCM, it might be possible to export the entire Yocto Project
|
||||
kernel Git repository, branches and all, into a new environment.
|
||||
This method is preferred because it has the most flexibility and potential to maintain
|
||||
the meta data associated with each commit.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When a direct import mechanism is not available, it is still possible to
|
||||
export a branch (or series of branches) and check them into a new repository.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following commands illustrate some of the steps you could use to
|
||||
import the <filename>yocto/standard/common-pc/base</filename>
|
||||
kernel into a secondary SCM:
|
||||
<literallayout class='monospaced'>
|
||||
$ git checkout yocto/standard/common-pc/base
|
||||
$ cd .. ; echo linux/.git > .cvsignore
|
||||
$ cvs import -m "initial import" linux MY_COMPANY start
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You could now relocate the CVS repository and use it in a centralized manner.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following commands illustrate how you can condense and merge two BSPs into a
|
||||
second SCM:
|
||||
<literallayout class='monospaced'>
|
||||
$ git checkout yocto/standard/common-pc/base
|
||||
$ git merge yocto/standard/common-pc-64/base
|
||||
# resolve any conflicts and commit them
|
||||
$ cd .. ; echo linux/.git > .cvsignore
|
||||
$ cvs import -m "initial import" linux MY_COMPANY start
|
||||
</literallayout>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='importing-changes-for-build'>
|
||||
<title>Importing Changes for the Build</title>
|
||||
|
||||
<para>
|
||||
Once development has reached a suitable point in the second development
|
||||
environment, you need to export the changes as patches.
|
||||
To export them, place the changes in a recipe and
|
||||
automatically apply them to the kernel during patching.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id='bsp-creating'>
|
||||
<title>Creating a BSP Based on an Existing Similar BSP</title>
|
||||
|
||||
<para>
|
||||
This section overviews the process of creating a BSP based on an
|
||||
existing similar BSP.
|
||||
The information is introductory in nature and does not provide step-by-step examples.
|
||||
For detailed information on how to create a new BSP, see
|
||||
the "<ulink url='&YOCTO_DOCS_BSP_URL;#creating-a-new-bsp-layer-using-the-yocto-bsp-script'>Creating a New BSP Layer Using the yocto-bsp Script</ulink>" section in the
|
||||
Yocto Project Board Support Package (BSP) Developer's Guide, or see the
|
||||
<ulink url='&YOCTO_WIKI_URL;/wiki/Transcript:_creating_one_generic_Atom_BSP_from_another'>Transcript:_creating_one_generic_Atom_BSP_from_another</ulink>
|
||||
wiki page.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The basic steps you need to follow are:
|
||||
<orderedlist>
|
||||
<listitem><para><emphasis>Make sure you have set up a local Source Directory:</emphasis>
|
||||
You must create a local
|
||||
<ulink url='&YOCTO_DOCS_DEV_URL;#source-directory'>Source Directory</ulink>
|
||||
by either creating a Git repository (recommended) or
|
||||
extracting a Yocto Project release tarball.</para></listitem>
|
||||
<listitem><para><emphasis>Choose an existing BSP available with the Yocto Project:</emphasis>
|
||||
Try to map your board features as closely to the features of a BSP that is
|
||||
already supported and exists in the Yocto Project.
|
||||
Starting with something as close as possible to your board makes developing
|
||||
your BSP easier.
|
||||
You can find all the BSPs that are supported and ship with the Yocto Project
|
||||
on the Yocto Project's Download page at
|
||||
<ulink url='&YOCTO_HOME_URL;/download'></ulink>.</para></listitem>
|
||||
<listitem><para><emphasis>Be sure you have the Base BSP:</emphasis>
|
||||
You need to either have a local Git repository of the base BSP set up or
|
||||
have downloaded and extracted the files from a release BSP tarball.
|
||||
Either method gives you access to the BSP source files.</para></listitem>
|
||||
<listitem><para><emphasis>Make a copy of the existing BSP, thus isolating your new
|
||||
BSP work:</emphasis>
|
||||
Copying the existing BSP file structure gives you a new area in which to work.</para></listitem>
|
||||
<listitem><para><emphasis>Make configuration and recipe changes to your new BSP:</emphasis>
|
||||
Configuration changes involve the files in the BSP's <filename>conf</filename>
|
||||
directory.
|
||||
Changes include creating a machine-specific configuration file and editing the
|
||||
<filename>layer.conf</filename> file.
|
||||
The configuration changes identify the kernel you will be using.
|
||||
Recipe changes include removing, modifying, or adding new recipe files that
|
||||
instruct the build process on what features to include in the image.</para></listitem>
|
||||
<listitem><para><emphasis>Prepare for the build:</emphasis>
|
||||
Before you actually initiate the build, you need to set up the build environment
|
||||
by sourcing the environment initialization script.
|
||||
After setting up the environment, you need to make some build configuration
|
||||
changes to the <filename>local.conf</filename> and <filename>bblayers.conf</filename>
|
||||
files.</para></listitem>
|
||||
<listitem><para><emphasis>Build the image:</emphasis>
|
||||
The OpenEmbedded build system uses BitBake to create the image.
|
||||
You need to decide on the type of image you are going to build (e.g. minimal, base,
|
||||
core, sato, and so forth) and then start the build using the <filename>bitbake</filename>
|
||||
command.</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id='tip-dirty-string'>
|
||||
<title>"-dirty" String</title>
|
||||
|
||||
<para>
|
||||
If kernel images are being built with "-dirty" on the end of the version
|
||||
string, this simply means that modifications in the source
|
||||
directory have not been committed.
|
||||
<literallayout class='monospaced'>
|
||||
$ git status
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can use the above Git command to report modified, removed, or added files.
|
||||
You should commit those changes to the tree regardless of whether they will be saved,
|
||||
exported, or used.
|
||||
Once you commit the changes you need to rebuild the kernel.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To brute force pickup and commit all such pending changes, enter the following:
|
||||
<literallayout class='monospaced'>
|
||||
$ git add .
|
||||
$ git commit -s -a -m "getting rid of -dirty"
|
||||
</literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Next, rebuild the kernel.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
11
documentation/kernel-manual/kernel-manual-customization.xsl
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version='1.0'?>
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
|
||||
|
||||
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" />
|
||||
|
||||
<xsl:param name="html.stylesheet" select="'kernel-style.css'" />
|
||||
<xsl:param name="chapter.autolabel" select="1" />
|
||||
<xsl:param name="appendix.autolabel" select="A" />
|
||||
<xsl:param name="section.autolabel" select="1" />
|
||||
<xsl:param name="section.label.includes.component.label" select="1" />
|
||||
</xsl:stylesheet>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version='1.0'?>
|
||||
<xsl:stylesheet
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:fo="http://www.w3.org/1999/XSL/Format"
|
||||
version="1.0">
|
||||
|
||||
<xsl:import
|
||||
href="http://docbook.sourceforge.net/release/xsl/current/eclipse/eclipse3.xsl" />
|
||||
|
||||
<xsl:param name="chunker.output.indent" select="'yes'"/>
|
||||
<xsl:param name="chunk.quietly" select="1"/>
|
||||
<xsl:param name="chunk.first.sections" select="1"/>
|
||||
<xsl:param name="chunk.section.depth" select="10"/>
|
||||
<xsl:param name="use.id.as.filename" select="1"/>
|
||||
<xsl:param name="ulink.target" select="'_self'" />
|
||||
<xsl:param name="base.dir" select="'html/kernel-manual/'"/>
|
||||
<xsl:param name="html.stylesheet" select="'../book.css'"/>
|
||||
<xsl:param name="eclipse.manifest" select="0"/>
|
||||
<xsl:param name="create.plugin.xml" select="0"/>
|
||||
<xsl:param name="suppress.navigation" select="1"/>
|
||||
<xsl:param name="generate.index" select="0"/>
|
||||
<xsl:param name="chapter.autolabel" select="1" />
|
||||
<xsl:param name="appendix.autolabel" select="A" />
|
||||
<xsl:param name="section.autolabel" select="1" />
|
||||
<xsl:param name="section.label.includes.component.label" select="1" />
|
||||
</xsl:stylesheet>
|
||||
106
documentation/kernel-manual/kernel-manual.xml
Normal file
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[<!ENTITY % poky SYSTEM "../poky.ent"> %poky; ] >
|
||||
|
||||
<book id='kernel-manual' lang='en'
|
||||
xmlns:xi="http://www.w3.org/2003/XInclude"
|
||||
xmlns="http://docbook.org/ns/docbook"
|
||||
>
|
||||
<bookinfo>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref='figures/kernel-title.png'
|
||||
format='SVG'
|
||||
align='left' scalefit='1' width='100%'/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<title>
|
||||
The Yocto Project Kernel Architecture and Use Manual
|
||||
</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Bruce</firstname> <surname>Ashfield</surname>
|
||||
<affiliation>
|
||||
<orgname>Wind River Corporation</orgname>
|
||||
</affiliation>
|
||||
<email>bruce.ashfield@windriver.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>0.9</revnumber>
|
||||
<date>24 November 2010</date>
|
||||
<revremark>The initial document draft released with the Yocto Project 0.9 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.0</revnumber>
|
||||
<date>6 April 2011</date>
|
||||
<revremark>Released with the Yocto Project 1.0 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.0.1</revnumber>
|
||||
<date>23 May 2011</date>
|
||||
<revremark>Released with the Yocto Project 1.0.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.1</revnumber>
|
||||
<date>6 October 2011</date>
|
||||
<revremark>Released with the Yocto Project 1.1 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.2</revnumber>
|
||||
<date>April 2012</date>
|
||||
<revremark>Released with the Yocto Project 1.2 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.3</revnumber>
|
||||
<date>October 2012</date>
|
||||
<revremark>Released with the Yocto Project 1.3 Release.</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.4</revnumber>
|
||||
<date>Sometime in 2013</date>
|
||||
<revremark>Released with the Yocto Project 1.4 Release.</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
<year>©RIGHT_YEAR;</year>
|
||||
<holder>Linux Foundation</holder>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
Permission is granted to copy, distribute and/or modify this document under
|
||||
the terms of the <ulink type="http" url="http://creativecommons.org/licenses/by-sa/2.0/uk/">Creative Commons Attribution-Share Alike 2.0 UK: England & Wales</ulink> as published by Creative Commons.
|
||||
</para>
|
||||
<note>
|
||||
Due to production processes, there could be differences between the Yocto Project
|
||||
documentation bundled in the release tarball and the
|
||||
<ulink url='&YOCTO_DOCS_KERNEL_URL;'>Yocto Project Kernel Architecture and Use Manual</ulink> on
|
||||
the <ulink url='&YOCTO_HOME_URL;'>Yocto Project</ulink> website.
|
||||
For the latest version of this manual, see the manual on the website.
|
||||
</note>
|
||||
</legalnotice>
|
||||
|
||||
</bookinfo>
|
||||
|
||||
<xi:include href="kernel-doc-intro.xml"/>
|
||||
|
||||
<xi:include href="kernel-concepts.xml"/>
|
||||
|
||||
<xi:include href="kernel-how-to.xml"/>
|
||||
|
||||
<!-- <index id='index'>
|
||||
<title>Index</title>
|
||||
</index>
|
||||
-->
|
||||
|
||||
</book>
|
||||
<!--
|
||||
vim: expandtab tw=80 ts=4
|
||||
-->
|
||||
979
documentation/kernel-manual/kernel-style.css
Normal file
@@ -0,0 +1,979 @@
|
||||
/*
|
||||
Generic XHTML / DocBook XHTML CSS Stylesheet.
|
||||
|
||||
Browser wrangling and typographic design by
|
||||
Oyvind Kolas / pippin@gimp.org
|
||||
|
||||
Customised for Poky by
|
||||
Matthew Allum / mallum@o-hand.com
|
||||
|
||||
Thanks to:
|
||||
Liam R. E. Quin
|
||||
William Skaggs
|
||||
Jakub Steiner
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
The stylesheet is divided into the following sections:
|
||||
|
||||
Positioning
|
||||
Margins, paddings, width, font-size, clearing.
|
||||
Decorations
|
||||
Borders, style
|
||||
Colors
|
||||
Colors
|
||||
Graphics
|
||||
Graphical backgrounds
|
||||
Nasty IE tweaks
|
||||
Workarounds needed to make it work in internet explorer,
|
||||
currently makes the stylesheet non validating, but up until
|
||||
this point it is validating.
|
||||
Mozilla extensions
|
||||
Transparency for footer
|
||||
Rounded corners on boxes
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*************** /
|
||||
/ Positioning /
|
||||
/ ***************/
|
||||
|
||||
body {
|
||||
font-family: Verdana, Sans, sans-serif;
|
||||
|
||||
min-width: 640px;
|
||||
width: 80%;
|
||||
margin: 0em auto;
|
||||
padding: 2em 5em 5em 5em;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6,h7 {
|
||||
font-family: Arial, Sans;
|
||||
color: #00557D;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
text-align: left;
|
||||
padding: 0em 0em 0em 0em;
|
||||
margin: 2em 0em 0em 0em;
|
||||
}
|
||||
|
||||
h2.subtitle {
|
||||
margin: 0.10em 0em 3.0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
font-size: 1.8em;
|
||||
padding-left: 20%;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 2em 0em 0.66em 0em;
|
||||
padding: 0.5em 0em 0em 0em;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3.subtitle {
|
||||
margin: 0em 0em 1em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
font-size: 142.14%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0em 0.5em 0em;
|
||||
padding: 1em 0em 0em 0em;
|
||||
font-size: 140%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 1em 0em 0.5em 0em;
|
||||
padding: 1em 0em 0em 0em;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin: 1em 0em 0.5em 0em;
|
||||
padding: 1em 0em 0em 0em;
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin: 1em 0em 0em 0em;
|
||||
padding: 1em 0em 0em 0em;
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.authorgroup {
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
padding-top: 256px;
|
||||
background-image: url("figures/kernel-title.png");
|
||||
background-position: left top;
|
||||
margin-top: -256px;
|
||||
padding-right: 50px;
|
||||
margin-left: 0px;
|
||||
text-align: right;
|
||||
width: 740px;
|
||||
}
|
||||
|
||||
h3.author {
|
||||
margin: 0em 0me 0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
font-weight: normal;
|
||||
font-size: 100%;
|
||||
color: #333;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.author tt.email {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
.titlepage hr {
|
||||
width: 0em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.revhistory {
|
||||
padding-top: 2em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.toc,
|
||||
.list-of-tables,
|
||||
.list-of-examples,
|
||||
.list-of-figures {
|
||||
padding: 1.33em 0em 2.5em 0em;
|
||||
color: #00557D;
|
||||
}
|
||||
|
||||
.toc p,
|
||||
.list-of-tables p,
|
||||
.list-of-figures p,
|
||||
.list-of-examples p {
|
||||
padding: 0em 0em 0em 0em;
|
||||
padding: 0em 0em 0.3em;
|
||||
margin: 1.5em 0em 0em 0em;
|
||||
}
|
||||
|
||||
.toc p b,
|
||||
.list-of-tables p b,
|
||||
.list-of-figures p b,
|
||||
.list-of-examples p b{
|
||||
font-size: 100.0%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.toc dl,
|
||||
.list-of-tables dl,
|
||||
.list-of-figures dl,
|
||||
.list-of-examples dl {
|
||||
margin: 0em 0em 0.5em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
.toc dt {
|
||||
margin: 0em 0em 0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
.toc dd {
|
||||
margin: 0em 0em 0em 2.6em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
div.glossary dl,
|
||||
div.variablelist dl {
|
||||
}
|
||||
|
||||
.glossary dl dt,
|
||||
.variablelist dl dt,
|
||||
.variablelist dl dt span.term {
|
||||
font-weight: normal;
|
||||
width: 20em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.variablelist dl dt {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.glossary dl dd,
|
||||
.variablelist dl dd {
|
||||
margin-top: -1em;
|
||||
margin-left: 25.5em;
|
||||
}
|
||||
|
||||
.glossary dd p,
|
||||
.variablelist dd p {
|
||||
margin-top: 0em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
div.calloutlist table td {
|
||||
padding: 0em 0em 0em 0em;
|
||||
margin: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
div.calloutlist table td p {
|
||||
margin-top: 0em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
div p.copyright {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.legalnotice p.legalnotice-title {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5em;
|
||||
margin-top: 0em;
|
||||
|
||||
}
|
||||
|
||||
dl {
|
||||
padding-top: 0em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: solid 1px;
|
||||
}
|
||||
|
||||
|
||||
.mediaobject,
|
||||
.mediaobjectco {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0em 0em 0em 1.5em;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
ul li p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table {
|
||||
width :100%;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 0.25em;
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.25em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
p a[id] {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
display: inline;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
/*font-weight: bold;*/
|
||||
}
|
||||
|
||||
|
||||
div.informalfigure,
|
||||
div.informalexample,
|
||||
div.informaltable,
|
||||
div.figure,
|
||||
div.table,
|
||||
div.example {
|
||||
margin: 1em 0em;
|
||||
padding: 1em;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
|
||||
div.informalfigure p.title b,
|
||||
div.informalexample p.title b,
|
||||
div.informaltable p.title b,
|
||||
div.figure p.title b,
|
||||
div.example p.title b,
|
||||
div.table p.title b{
|
||||
padding-top: 0em;
|
||||
margin-top: 0em;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mediaobject .caption,
|
||||
.mediaobject .caption p {
|
||||
text-align: center;
|
||||
font-size: 80%;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.epigraph {
|
||||
padding-left: 55%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.epigraph p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.epigraph .quote {
|
||||
font-style: italic;
|
||||
}
|
||||
.epigraph .attribution {
|
||||
font-style: normal;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span.application {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.programlisting {
|
||||
font-family: monospace;
|
||||
font-size: 80%;
|
||||
white-space: pre;
|
||||
margin: 1.33em 0em;
|
||||
padding: 1.33em;
|
||||
}
|
||||
|
||||
.tip,
|
||||
.warning,
|
||||
.caution,
|
||||
.note {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
}
|
||||
|
||||
/* force full width of table within div */
|
||||
.tip table,
|
||||
.warning table,
|
||||
.caution table,
|
||||
.note table {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.tip table th,
|
||||
.warning table th,
|
||||
.caution table th,
|
||||
.note table th {
|
||||
padding: 0.8em 0.0em 0.0em 0.0em;
|
||||
margin : 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
.tip p,
|
||||
.warning p,
|
||||
.caution p,
|
||||
.note p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding-right: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.acronym {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
b.keycap,
|
||||
.keycap {
|
||||
padding: 0.09em 0.3em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.itemizedlist li {
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.filename {
|
||||
font-size: medium;
|
||||
font-family: Courier, monospace;
|
||||
}
|
||||
|
||||
|
||||
div.navheader, div.heading{
|
||||
position: absolute;
|
||||
left: 0em;
|
||||
top: 0em;
|
||||
width: 100%;
|
||||
background-color: #cdf;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.navfooter, div.footing{
|
||||
position: fixed;
|
||||
left: 0em;
|
||||
bottom: 0em;
|
||||
background-color: #eee;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
div.navheader td,
|
||||
div.navfooter td {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
div.navheader table th {
|
||||
/*font-family: Georgia, Times, serif;*/
|
||||
/*font-size: x-large;*/
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
div.navheader table {
|
||||
border-left: 0em;
|
||||
border-right: 0em;
|
||||
border-top: 0em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.navfooter table {
|
||||
border-left: 0em;
|
||||
border-right: 0em;
|
||||
border-bottom: 0em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.navheader table td a,
|
||||
div.navfooter table td a {
|
||||
color: #777;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* normal text in the footer */
|
||||
div.navfooter table td {
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.navheader table td a:visited,
|
||||
div.navfooter table td a:visited {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
|
||||
/* links in header and footer */
|
||||
div.navheader table td a:hover,
|
||||
div.navfooter table td a:hover {
|
||||
text-decoration: underline;
|
||||
background-color: transparent;
|
||||
color: #33a;
|
||||
}
|
||||
|
||||
div.navheader hr,
|
||||
div.navfooter hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.qandaset tr.question td p {
|
||||
margin: 0em 0em 1em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
.qandaset tr.answer td p {
|
||||
margin: 0em 0em 1em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
.answer td {
|
||||
padding-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.emphasis {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/************* /
|
||||
/ decorations /
|
||||
/ *************/
|
||||
|
||||
.titlepage {
|
||||
}
|
||||
|
||||
.part .title {
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/*
|
||||
h1 {
|
||||
border: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
border-top: solid 0.2em;
|
||||
border-bottom: solid 0.06em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
border-top: 0em;
|
||||
border-bottom: solid 0.06em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
border: 0em;
|
||||
border-bottom: solid 0.06em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
border: 0em;
|
||||
}
|
||||
*/
|
||||
|
||||
.programlisting {
|
||||
border: solid 1px;
|
||||
}
|
||||
|
||||
div.figure,
|
||||
div.table,
|
||||
div.informalfigure,
|
||||
div.informaltable,
|
||||
div.informalexample,
|
||||
div.example {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.tip,
|
||||
.warning,
|
||||
.caution,
|
||||
.note {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.tip table th,
|
||||
.warning table th,
|
||||
.caution table th,
|
||||
.note table th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.question td {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
.answer {
|
||||
}
|
||||
|
||||
|
||||
b.keycap,
|
||||
.keycap {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
|
||||
div.navheader, div.heading{
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
|
||||
div.navfooter, div.footing{
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
/********* /
|
||||
/ colors /
|
||||
/ *********/
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
background: white;
|
||||
}
|
||||
|
||||
a {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: #dedede;
|
||||
}
|
||||
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
h7,
|
||||
h8 {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: #aaa;
|
||||
}
|
||||
|
||||
|
||||
.tip, .warning, .caution, .note {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.tip table th,
|
||||
.warning table th,
|
||||
.caution table th,
|
||||
.note table th {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.warning {
|
||||
background-color: #f0f0f2;
|
||||
}
|
||||
|
||||
.caution {
|
||||
background-color: #f0f0f2;
|
||||
}
|
||||
|
||||
.tip {
|
||||
background-color: #f0f0f2;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #f0f0f2;
|
||||
}
|
||||
|
||||
.glossary dl dt,
|
||||
.variablelist dl dt,
|
||||
.variablelist dl dt span.term {
|
||||
color: #044;
|
||||
}
|
||||
|
||||
div.figure,
|
||||
div.table,
|
||||
div.example,
|
||||
div.informalfigure,
|
||||
div.informaltable,
|
||||
div.informalexample {
|
||||
border-color: #aaa;
|
||||
}
|
||||
|
||||
pre.programlisting {
|
||||
color: black;
|
||||
background-color: #fff;
|
||||
border-color: #aaa;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.guimenu,
|
||||
.guilabel,
|
||||
.guimenuitem {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
|
||||
b.keycap,
|
||||
.keycap {
|
||||
background-color: #eee;
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
|
||||
div.navheader {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
|
||||
div.navfooter {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
|
||||
/*********** /
|
||||
/ graphics /
|
||||
/ ***********/
|
||||
|
||||
/*
|
||||
body {
|
||||
background-image: url("images/body_bg.jpg");
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.navheader,
|
||||
.note,
|
||||
.tip {
|
||||
background-image: url("images/note_bg.jpg");
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.warning,
|
||||
.caution {
|
||||
background-image: url("images/warning_bg.jpg");
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.figure,
|
||||
.informalfigure,
|
||||
.example,
|
||||
.informalexample,
|
||||
.table,
|
||||
.informaltable {
|
||||
background-image: url("images/figure_bg.jpg");
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
*/
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
h7{
|
||||
}
|
||||
|
||||
/*
|
||||
Example of how to stick an image as part of the title.
|
||||
|
||||
div.article .titlepage .title
|
||||
{
|
||||
background-image: url("figures/white-on-black.png");
|
||||
background-position: center;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
*/
|
||||
|
||||
div.preface .titlepage .title,
|
||||
div.colophon .title,
|
||||
div.chapter .titlepage .title,
|
||||
div.article .titlepage .title
|
||||
{
|
||||
}
|
||||
|
||||
div.section div.section .titlepage .title,
|
||||
div.sect2 .titlepage .title {
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
h1.title {
|
||||
background-color: transparent;
|
||||
background-image: url("figures/yocto-project-bw.png");
|
||||
background-repeat: no-repeat;
|
||||
height: 256px;
|
||||
text-indent: -9000px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
h2.subtitle {
|
||||
background-color: transparent;
|
||||
text-indent: -9000px;
|
||||
overflow:hidden;
|
||||
width: 0px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*************************************** /
|
||||
/ pippin.gimp.org specific alterations /
|
||||
/ ***************************************/
|
||||
|
||||
/*
|
||||
div.heading, div.navheader {
|
||||
color: #777;
|
||||
font-size: 80%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: url('/gfx/heading_bg.png') transparent;
|
||||
background-repeat: repeat-x;
|
||||
background-attachment: fixed;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.heading a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.footing, div.navfooter {
|
||||
border: none;
|
||||
color: #ddd;
|
||||
font-size: 80%;
|
||||
text-align:right;
|
||||
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
|
||||
background: url('/gfx/footing_bg.png') transparent;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/****************** /
|
||||
/ nasty ie tweaks /
|
||||
/ ******************/
|
||||
|
||||
/*
|
||||
div.heading, div.navheader {
|
||||
width:expression(document.body.clientWidth + "px");
|
||||
}
|
||||
|
||||
div.footing, div.navfooter {
|
||||
width:expression(document.body.clientWidth + "px");
|
||||
margin-left:expression("-5em");
|
||||
}
|
||||
body {
|
||||
padding:expression("4em 5em 0em 5em");
|
||||
}
|
||||
*/
|
||||
|
||||
/**************************************** /
|
||||
/ mozilla vendor specific css extensions /
|
||||
/ ****************************************/
|
||||
/*
|
||||
div.navfooter, div.footing{
|
||||
-moz-opacity: 0.8em;
|
||||
}
|
||||
|
||||
div.figure,
|
||||
div.table,
|
||||
div.informalfigure,
|
||||
div.informaltable,
|
||||
div.informalexample,
|
||||
div.example,
|
||||
.tip,
|
||||
.warning,
|
||||
.caution,
|
||||
.note {
|
||||
-moz-border-radius: 0.5em;
|
||||
}
|
||||
|
||||
b.keycap,
|
||||
.keycap {
|
||||
-moz-border-radius: 0.3em;
|
||||
}
|
||||
*/
|
||||
|
||||
table tr td table tr td {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 0em;
|
||||
}
|
||||
|
||||
.photo {
|
||||
float: right;
|
||||
margin-left: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 0em;
|
||||
max-width: 17em;
|
||||
border: 1px solid gray;
|
||||
padding: 3px;
|
||||
background: white;
|
||||
}
|
||||
.seperator {
|
||||
padding-top: 2em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#validators {
|
||||
margin-top: 5em;
|
||||
text-align: right;
|
||||
color: #777;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
font-size: 8pt;
|
||||
}
|
||||
.noprint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tip,
|
||||
.note {
|
||||
background: #f0f0f2;
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.tip h3,
|
||||
.note h3 {
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.tip a,
|
||||
.note a {
|
||||
color: #333;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footnote {
|
||||
font-size: small;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Changes the announcement text */
|
||||
.tip h3,
|
||||
.warning h3,
|
||||
.caution h3,
|
||||
.note h3 {
|
||||
font-size:large;
|
||||
color: #00557D;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 20 KiB |